JDK-6500701 : Enhanced for loop with generics generates faulty bytecode
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 5.0
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-12-05
  • Updated: 2011-05-18
  • Resolved: 2011-05-18
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 b33Fixed
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
When iterating over generic iterable elements with the enhanced for-each loop, and you do not use typeinformation in the loop, you get a ClassCastException runtime. One would expect this not to happen, since we do not use the generic type information in the for-each loop, but the compiled code still does a cast.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the attached java file, and run it.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected result was to see a regular run of the program with no errors.
ACTUAL -
The actual result is a classcastexception

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.ClassCastException: ForEachBug$B
        at ForEachBug.reproduceBug(ForEachBug.java:19)
        at ForEachBug.main(ForEachBug.java:10)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.ArrayList;
import java.util.List;

public class ForEachBug {
	public class A {}

	public class B {}

	public static void main(String[] args) {
		new ForEachBug().reproduceBug();

	}

	private void reproduceBug() {
		List list = new ArrayList();
		list.add(new B());
		List<A> aList = new ArrayList<A>();
		aList.addAll(list);
		for (Object a : aList) {
			System.out.println(a);
		}
	}

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
make sure you do not use @supressWarnings("unchecked"), and remove all type safety warnings in your code.

Comments
SUGGESTED FIX A webrev of this fix is available at http://sa.sfbay.sun.com/projects/langtools_data/7/6500701/
25-07-2008

EVALUATION The cast that javac generates when translating enhanced-for-loops is mandatory if we want to preserve the type-safety of the translated code: consider the following scenario: void show(String s) { ... }; for (String s : Arrays.asList("a","b")) { show(s); } if javac had followed the spec closely, the genrated code would have been: for (Iterator $i - Arrays.asList("a","b").iterator(); $i.hasNext;) { String s = $i.next(); { show(s); } } Which is wrong, since the runtime type-signature of Iterator.next() is ()Ljava/lang/Object, so we end-up with trying to invoke the method show - whose runtime signature is (Ljava/lang/String;)V - with the argument being a local variable with a wrong type (Object instead of String): the result is clearly a VerifyError. For this reason I filed a spec RFE. In order to solve this problem javac could generate better synthetic cast, as described in the following: for (I #i = Expression.iterator(); #i.hasNext(); ) { VT #i0 = (CT)#i.next(); *) if VT is a reference type, then CT == VT *) if VT is a primitive type, then CT = ub(X), where I is Iterator<X> (as X could be a captured type-var) This should accomodate both type-safety and the problem reported in this CR.
18-04-2008

EVALUATION Suggested fix contributed by R��mi Forax: ----------------------------- by the way bug 6500701 http://bugs.sun.com/view_bug.do?bug_id=6500701 can be fixed in the same time by removing the line: if (iteratorTarget != syms.objectType) vardefinit = make.TypeCast(iteratorTarget, vardefinit); the one just before the patch. R��mi ------------------------------
07-04-2008

EVALUATION According to the JLS, the enhanced for loop: for ( VariableModifiersopt Type Identifier: Expression) Statement translates to: for (I #i = Expression.iterator(); #i.hasNext(); ) { VariableModifiersopt Type Identifier = #i.next(); Statement } See http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.14.2 However, javac translates it to: for (I #i = Expression.iterator(); #i.hasNext(); ) { VariableModifiersopt Type Identifier = (J)#i.next(); Statement } where J is the type argument of the iterator. This detects heap pollution sooner but is not according to the spec. The compiler is wrong.
07-12-2006