JDK-4094180 : # Synthetic names can conflict with explicit declaration
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 1.1,1.1.4,1.2.0,1.3.0,1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS:
    generic,solaris_2.5,solaris_2.5.1,windows_95,windows_nt generic,solaris_2.5,solaris_2.5.1,windows_95,windows_nt
  • CPU: generic,x86,sparc
  • Submitted: 1997-11-20
  • Updated: 2002-07-10
  • Resolved: 2002-07-10
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description

Name: dkC59003			Date: 11/20/97



The document "Inner Classes in Java 1.1" says:
"For purposes of linking, the compiler must generate a unique externally 
visible name for every inaccessible class. The overall form of
these names is a class name, followed by additional numbers or names, separated by $ characters.
Also, variable names synthesized by the compiler beginning with this$ and val$ must follow the usage patterns described here."

Compiler outputs an error while compiling the following source file 
incorrectly considering val$prm2 as int whereas it is defined as Object.
Note that the case of val$prm1 is OK. The only difference between these two
variables is the parameter prm2 occurence preceding val$prm2 usage.

-----------------------------------------
package javasoft.sqe.tests.lang.icls122.icls12201;
 
import java.io.PrintStream;
 
public class icls12201 {
	
	interface Intf {
		int getFirst();
		int getSecond();
	}

	static Intf meth(final int prm1, final int prm2) {
	
		final Object val$prm1 = null;
		final Object val$prm2 = null;
		
		class InnClass implements Intf {
			Object locVar;
			public int getFirst() {
				locVar = val$prm1;
				return prm1;
			}
			public int getSecond() {
				int locInt;
				locInt = prm2;
				locVar = val$prm2;
				return locInt;
			}
		}
		return new InnClass();
	}
}
-----------------------------------------

JDK 1.1.5F and JDK 1.2S compilers output:

t1.java:26: Incompatible type for =. Can't convert int to java.lang.Object.
				locVar = val$prm2;
				       ^
1 error
-----------------------------------------

======================================================================

A similar problem can arise with the flatnames of inner classes
conflicting with explicitly-declared classes (from 4096303):

class Some$Clazz { }

class Some {
    class Clazz { }
}

Another case was reported in 4030356:

Source code can refer to synthetic names like "this$Foo",
which are an implementation detail supposedly inaccessible to programmers.
Thus, files like this should not compile, but they do:

        class HiddenFieldBug {
            class Inner {
                Object x = this$HiddenFieldBug;
                // A similar problem exists with other synthesized names,
                // such as val$foo, access$2(), HiddenFieldBug$1$Local.
            }
        }

william.maddox@Eng 2000-01-10


======================================================================
from 4094180:


Javac (jdk1.4.0beta-b62, jdk1.3.1-b23) allows access to a local
class by using a synthetic name that compiler constructs to represent
a local class. Through this synthetic name the class is accessible
even outside of its scope.

That contradicts the following assertions of JLS-2,
14.3 "Local class declarations":

  "The scope of a local class declared in a block is the rest of the
   immediately enclosing block, including its own class declaration."
   
  "A local class does not have a canonical name, nor does it have a fully
   qualified name."
   
The following test demonstrates the behavior:

--stmt16503_a.java--------------------------------------------
public class stmt16503_a {
    public static void test() {
        class Local {};
    }
}
--------------------------------------------------------------

--stmt16503.java----------------------------------------------
public class stmt16503 {
    public static void main(String argv[]) {
        Object a = new stmt16503_a$1$Local();
    }
}
--------------------------------------------------------------

The class Local can be accessed from a method of class stmt16503 by using
the name stmt16503_a$1$Local. To reproduce the bug classes should be compiled
in sequence - stmt16503_a.java, then stmt16503.java.
Compiler correctly detects the error when classes are compiled together.

The execution log is following:

$ jdk1.4.0beta-b62/solsparc/bin/java -version                     
java version "1.4.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta-b62)
Java HotSpot(TM) Client VM (build 1.4.0-beta-b62, mixed mode)
$ jdk1.4.0beta-b62/solsparc/bin/javac -d . stmt16503_a.java stmt16503.java; echo 
$?
stmt16503.java:3: cannot resolve symbol
symbol  : class stmt16503_a$1$Local  
location: class stmt16503
        Object a = new stmt16503_a$1$Local();
                       ^
1 error
1
$ jdk1.4.0beta-b62/solsparc/bin/javac -d . stmt16503_a.java; echo $?
0
$ jdk1.4.0beta-b62/solsparc/bin/javac -d . stmt16503.java; echo $?
0
$ jdk1.4.0beta-b62/solsparc/bin/java stmt16503; echo $?
0
$ 


========================================================
from 4480593:

When compiling the following class

class t
{
        public static void main(String[] args)  
        {               
                Class c = t.class;
                System.out.println(c);
        }
        
        int class$t() { return 0; }
}

the following exception trace was obtained.

C:\jdk1.4\bin>javac t.java
An exception has occurred in the compiler (1.4.0-beta). Please file a bug at the
 Java Developer Connection (http://java.sun.com/cgi-bin/bugreport.cgi)  after ch
ecking the Bug Parade for duplicates. Include your program and the following dia
gnostic in your report.  Thank you.
java.lang.ClassCastException: com.sun.tools.javac.v8.code.Symbol$MethodSymbol
        at com.sun.tools.javac.v8.comp.TransInner.cacheSym(TransInner.java:1528)

        at com.sun.tools.javac.v8.comp.TransInner.classOfType(TransInner.java:1582)
        at com.sun.tools.javac.v8.comp.TransInner.classOf(TransInner.java:1547)
        at com.sun.tools.javac.v8.comp.TransInner._case(TransInner.java:1954)
        at com.sun.tools.javac.v8.tree.Tree$Select.visit(Tree.java:1200)
        at com.sun.tools.javac.v8.tree.TreeTranslator.translate(TreeTranslator.java:45)
        at com.sun.tools.javac.v8.tree.TreeTranslator._case(TreeTranslator.java:120)
        at com.sun.tools.javac.v8.tree.Tree$VarDef.visit(Tree.java:604)
        at com.sun.tools.javac.v8.tree.TreeTranslator.translate(TreeTranslator.java:45)
        at com.sun.tools.javac.v8.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.v8.tree.TreeTranslator._case(TreeTranslator.java:129)
        at com.sun.tools.javac.v8.tree.Tree$Block.visit(Tree.java:644)
        at com.sun.tools.javac.v8.tree.TreeTranslator.translate(TreeTranslator.java:45)
        at com.sun.tools.javac.v8.tree.TreeTranslator._case(TreeTranslator.java:114)
        at com.sun.tools.javac.v8.comp.TransInner._case(TransInner.java:1748)
        at com.sun.tools.javac.v8.tree.Tree$MethodDef.visit(Tree.java:569)
        at com.sun.tools.javac.v8.tree.TreeTranslator.translate(TreeTranslator.java:45)
        at com.sun.tools.javac.v8.comp.TransInner._case(TransInner.java:1678)
        at com.sun.tools.javac.v8.tree.Tree$ClassDef.visit(Tree.java:521)
        at com.sun.tools.javac.v8.tree.TreeTranslator.translate(TreeTranslator.java:45)
        at com.sun.tools.javac.v8.comp.TransInner.translate(TransInner.java:1638)
        at com.sun.tools.javac.v8.comp.TransInner.translateTopLevelClass(TransInner.java:1988)
        at com.sun.tools.javac.v8.JavaCompiler.compile(JavaCompiler.java:484)
        at com.sun.tools.javac.v8.Main.compile(Main.java:505)
        at com.sun.tools.javac.Main.compile(Main.java:27)
        at com.sun.tools.javac.Main.main(Main.java:16)

-------------------------------------------------------------------------------
On the other hand, using other internally used names such as 'this$n', compiler
gives proper error message.


Comments
PUBLIC COMMENTS The compiler allows source code to contain names which conflict with those of synthetic members generated by the compiler in support of inner classes, e.g., <code>val$foo</code>.
10-06-2004

EVALUATION The compiler creates fields with names of the form 'val$<name>' to capture the values of final local variables and parameters accessed "up-level" from within an inner class. A field 'int val$prm2' is being introduced in order to capture the parameter 'prm2', which is hiding the previously-declared field of the same name, but of type Object. Such capture is an unavoidable consequence of the "strongly recommended" naming scheme for such synthetic fields. My reading of the inner class spec indicates that names of synthetic members should not be permitted in the source code, though it is unclear exactly what the rule is. It appears reasonable to expect the compiler to reject identifiers of the form 'val$<name>', 'access$<number>', and 'this$<number>' without regard for whether an identical name is actually generated synthetically. I don't find any requirement in the inner classes spec that the compiler choose names for these synthetic membes that do not conflict with source code names. See spec bug 4098167. Note that we might, despite the silence of the innerclasses spec, add further distinguishing digits, e.g., val$foo$1. The pre 1.3 compilar apparently does this in some cases, e.g., when the user has explicitly declared a member val$foo$1. This could be extended to avoid classes with any such name in scope. Bug 4274357 documents the regression in the 1.3 compiler. The fix there should extend to as many addtional cases as possible, but see below. See 4096303 for a related issue involving class names. The issue with class names is more pernicious due to separate compilation. In general, we don't know the set of explicitly-declared names, so we must rely on an a-priori partitioning of the namespace. This is the core issue behind 4098167. Bugs 4096303 and 4030356 have been closed as duplicates, however additional information may be found in their comments and evaluation. william.maddox@Eng 1997-12-04 The compiler should, at the very least, complain when the user names a synthetic. Here is the kind of trouble we can get in: $ cat -n T.java 1 class T { 2 private T(int i) {} 3 class I { 4 T t = new T(3); 5 } 6 private T(int i, T$1 x) {} 7 public void main(String[] args) {} 8 } $ javac T.java $ java T Exception in thread "main" java.lang.ClassFormatError: T (Repeative method name/signature) at java.lang.ClassLoader.defineClass0(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:486) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111) at java.net.URLClassLoader.defineClass(URLClassLoader.java:248) at java.net.URLClassLoader.access$100(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:297) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:286) at java.lang.ClassLoader.loadClass(ClassLoader.java:253) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313) $ neal.gafter@Eng 2001-05-03 While recent compilers don't fail quite as spectacularly as older ones, and many of these cases are diagnosed, the inner class spec is now moot with the existence of JLS2. The compiler has no excuse to reject programs that happen to use names that the compiler wanted to use for synthetics. If the compiler needs to synthesize a name, it should select a name that doesn't conflict with any user name when it has that freedom. Only inner class names don't leave the compiler that freedom. The user should not be allowed to utter the name of a synthetic entity. ###@###.### 2001-08-22 The only remaining compiler crash included in this by report has been moved to the new bug 4524469. The remaining issues are that (1) a user program ought not be able to utter the name of a synthetic, and (2) synthetics ought to be renamed so as not to conflict with user names. ###@###.### 2001-11-07 (1) has been fixed. The only remaining issue is that javac should rename synthetics to avoid conflict with user-defined symbols. Unfortunately, that is impossible because the conflict can occur in a subclass that the compiler hasn't seen yet. For example, the user might write a method in a subclass that overrides a synthetic access method. There is nothing we can do about that, so I'm closing this as will not fix. ###@###.### 2002-07-09
09-07-2002