EVALUATION
This problem is the result of many contributing factors.
First, it seems each time a new class is entered (Enter.java), a new environment is created using Enter.classEnv. This has the effect of creating a new attribution environment nested inside the current one, with its own scope. Enter.java however creates an additional scope that gets then assigned to the class symbol's members fields. As a result, we have two (!!) different scopes at the end of the entering process - the scope in env.info.scope and the one in csym.members() - some symbols are added to the former (e.g. type-parameters, etc.) while other symbols are only added to the latter (e.g. member types).
In fact, member types get added to the 'enclosing scope' and such enclosing scope is retrieved using a special method in Enter that uses the current env - if the env is a class, then the enclosing env is the class scope (members()), otherwise it uses env.info.scope. The enter/memberEnter code has to deal with this scope duplicity and there are places (as MemberEnter.baseEnv()) in which class type-variables have to be added manually to a fake empty scope so that correct rules are enforced as expected [i.e. type-variables shadow outer type-decls].
The type resolution routine in Resolve.java is compatible with this duplication; in fact, the routine performs two rather orthogonal steps repeatedly:
*) first the current scope is accessed to find a type (using env.info.scope)
*) if nothing has been found, try accessing all member types in the enclosing class' scope (env.enclClass.sym.members())
Now, the problem with this example is that local classes are somewhat in between; the resolution routine would expect to see a local class in the first scope (env.info.scope) - however, when it comes to attributing extends/implements clause, the synthetic scope created for attributing such statements is missing local class symbols. Which means that step 1 of type resolution fails, and type resolution defaults back to step 2, which means finding all member types in the enclosing class declaration - here's why resolution strangely yields p.b rather than resolve().b.
The solution is to slightly change the synthetic scope used for attributing extends/implements clause, so that it also imports all local classes from the enclosing scope.
[NOTE]
The scope for attributing a superclass/superinterface is the enclosing scope which contains the class declaration whose extends/implements clause needs to be attributed; for instance, given the following declarations:
class P {
interface I {}
}
class T extends P implements I { // error: no I in scope
}
when we attribute P/I, we use as scope the toplevel scope (!!) containing P/T
Unfortunately, this needs a trick - since a superclass declaration might refer to the type-variable of the declaration we are attributing, we need to manually adds type-variables to the scope used to attribute supertypes.
Why all this machinery? Because in the above case we have that T extends P, so all members of P are inherithed in T, which means they are also available in T scope as unqualified names, which would make the above program perfectly legal.
|