JDK-6977800 : Regression: invalid resolution of supertype for local class
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: unknown
  • Submitted: 2010-08-17
  • Updated: 2011-03-07
  • Resolved: 2011-03-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b109Fixed
Related Reports
Relates :  
Relates :  
Description
Hi,
   recently, we have recieved a report about an incorrectly compiled class (http://netbeans.org/bugzilla/show_bug.cgi?id=187452). Consider the following code:
---------------------------------------------------------
public class p1 {

    public static void resolve() {
        class b {
            int x = 1;
        }
        class c extends b {}

        System.out.println(new c().x);
    }

    static class b {}
}
---------------------------------------------------------

Using a recent JDK7 javac, there is a compilation error that "x" cannot be resolved on the line with "new c().x". This compilation error does not seem correct to me. JDK6 javac accepts that code. I tried to find out the cause, and it seems that this was introduced by a fix for bug 5060485.

Comments
SUGGESTED FIX A webrev of this fix is available at the following URL: http://hg.openjdk.java.net/jdk7/tl/langtools/rev/a75770c0d7f6
19-08-2010

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.
17-08-2010