Replace mro_source with inheritance behavior flags
Summary:
The original design of linearization supposed that we could determine how to handle member lookup simply by knowing the final step of how an ancestor was included in a linearization. For example, in the original design, the class C in this file:
```
interface I {}
class B implements I {
private function foo(): void {}
}
trait T1 {
private function bar(): void {}
}
trait T2 {
use T1;
}
class C extends B {
use T2;
}
```
would be given the linearization [C(child), T2(trait), T1(trait), B(parent), I(parent)]. Each element in the MRO is marked with an "MRO source"--C is given (child), since C is the class which was linearized, T2 is given (trait), since C used it directly, T1 is given (trait), since C used it via the trait T2, B is given (parent), since C extends B, and I is given (parent) (rather than (interface)!), since C implements it via C's parent B.
If we attempt to look up C::foo, we would traverse the linearization until we reached B. We would observe that B has a method named foo, but since it is private, C does not inherit it, and the lookup fails.
If we attempt to look up C::bar, we would traverse the linearization until we reached T1. We would observe that T1 has a method named bar, and it is private, but since the MRO element T1 has the MRO source "(trait)", C has access to it, and the lookup succeeds.
Unfortunately, this is not sufficient. Consider this example:
```
class A {
private function foo(): void {}
}
trait T {
require extends A;
private function bar(): void {}
}
class C extends A {
use T;
}
```
The original design assigned to C the linearization [C(child), T(trait), A(trait), A(parent)]. If we attempted to look up C::foo, we would traverse to A(trait), observe that foo was private, but consider it to be inherited by C because of the (trait) source on the MRO element A(trait)!
The reason A(trait) is included in the linearization of C is because of T--when C uses T, the entire linearization of T is included in C's linearization, including T's require-extends ancestor A. It is marked as coming from a (trait) source because we mark T's entire linearization as coming from a trait-use.
Instead of blindly marking the entire linearization of T with the (trait) MRO source, we should be a little more clever, and only inherit private members if there is an unbroken chain of trait-uses connecting the child class to the private member (consider T1::bar above, where C does not directly use T1, but does inherit bar).
We end up in a similar situation when it comes to interfaces (where we inherit constants and type constants only) and XHP attribute uses (where we inherit XHP attributes only).
This change replaces the mro_source on each element with separate flags for "xhp_attrs_only", "consts_only", and "copy_private_members". We then can (for instance) avoid inheriting a non-XHP-attribute member if *any* ancestor in the chain reaching them was marked xhp_attrs_only, and copy a private member only if *all* ancestors in the chain reaching that member were marked with copy_private_members.
Reviewed By: arxanas
Differential Revision:
D13822817
fbshipit-source-id:
284b3808da7667a5bcd4d16243ab7087d75f2125