JDK-8166421 : 14.14.2: Bugs in desugaring of for-each
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 8
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2016-09-20
  • Updated: 2017-01-12
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.
Other
tbd_majorUnresolved
Related Reports
Relates :  
Relates :  
Relates :  
Description
The for-each desugaring (14.14.2) looks like this:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    {VariableModifier} TargetType Identifier =
        (TargetType) #i.next();
    Statement
}

Here's the definition of TargetType:

"If the declared type of the local variable in the header of the enhanced for statement is a reference type, then TargetType is that declared type; otherwise, TargetType is the upper bound of the capture conversion (��5.1.10) of the type argument of I, or Object if I is raw."

The usage of 'TargetType' is confusing and incorrect:
- The type of the variable should be UnannType (or, more precisely, a new DeclaredType that accounts for array brackets), not the type of the cast. For example, a primitive variable should not be declared with a reference type.
- The cast is meant to express the implicit cast that arises due to erasure, but this is redundant: it is inherent in the use of assignment (see 5.2)
- The cast also allows a type variable with a box-type upper bound (T extends Integer) to be widened by the cast and then unboxed by the assignment. JDK-8166326 eliminates the need for a cast here.

I propose fixing the variable's declared type and getting rid of the cast entirely.
Comments
I propose deleting this example, first because it's no longer a special case, and second because it's incorrect in the type of #i: List<? extends Integer> l = ... for (Iterator<Integer> #i = l.iterator(); #i.hasNext(); ) { float #i0 = (Integer)#i.next(); ... The actual type of #i should be Iterator<CAP>, where CAP extends Integer (as produced by the capture of 'l'). And since capture variables are non-denoteable, we have no way to properly express this.
21-09-2016

Change for 14.14.2: If the type of Expression is a subtype of Iterable, then the translation is as follows. If the type of Expression is a subtype of Iterable<X> for some type argument X, then let I be the type java.util.Iterator<X>; otherwise, let I be the raw type java.util.Iterator. ***Let DeclType be the declared type of the local variable declared in the header.*** The enhanced 'for' statement is equivalent to a basic 'for' statement of the form: for ( I #i = Expression.iterator(); #i.hasNext(); ) { {VariableModifier} ***DeclType*** Identifier = ***#i.next()***; Statement } #i is an automatically generated identifier that is distinct from any other identifiers (automatically generated or otherwise) that are in scope (��6.3) at the point where the enhanced for statement occurs. ***[Delete the definition of TargetType and the subsequent explanatory example.]*** ***[Note:] The initialization of 'Identifier' follows the normal Java language rules for assignment. In particular, a 'ClassCastException' or 'NullPointerException' may occur, as described in ��5.2.***
21-09-2016

Some history: JDK-6500701, a javac bug, describes the original dilemma that led to the cast, arising from a misunderstanding about how erasure applies to the code in JLS JDK-6690688 introduced the cast in the desugaring, acknowledging that "the translation is meant to be done before type erasure, in which case no cast is needed", but that it could be useful for clarity and for expressing the unboxing properly. Since the unboxing now works properly without a cast, I think the cast obfuscates a lot more than it helps. And there are many problems with computing "the upper bound of the capture conversion (��5.1.10) of the type argument of I". We could always define the cast with "TargetType is the declared type", but at that point it's totally redundant. If clarification about erasure is needed, that can be provided in a note.
20-09-2016