Bug 421926 - [1.8][compiler] Compiler tolerates illegal forward reference from lambda in initializer
Summary: [1.8][compiler] Compiler tolerates illegal forward reference from lambda in i...
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.4   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: BETA J8   Edit
Assignee: Srikanth Sankaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-11-17 22:43 EST by Srikanth Sankaran CLA
Modified: 2014-04-25 13:52 EDT (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Srikanth Sankaran CLA 2013-11-17 22:43:32 EST
BETA_JAVA8:
-----------

The following program compiles with ECJ, but fails to compile with
8b115.

It appears ECJ behavior is incorrect.

// --
interface I { 
	int run(int s1, int s2); 
}

class X {
	
    static int f = ((I) (int x5, int x2) -> x1).run(10,  20);
    static int x1 = 2;
}
Comment 1 Noopur Gupta CLA 2013-11-26 03:44:27 EST
Another example, which compiles with ECJ, but not with 8b115:

----------------------------------------------
package test.inline;

interface I {
	I run(int s1);
}

class X {	
	public static final I fi = x -> fi;
}
---------------------------------------------
Comment 2 Markus Keller CLA 2013-11-26 11:47:08 EST
Srikanth, I think this needs to be taken to the 335 EG.

JLS7 8.3.2.3 "Restrictions on the use of Fields during Initialization" says as point 4: "C is the innermost class or interface enclosing the usage".

This should be expanded to include lambda bodies, e.g.:
"C is the innermost class or interface enclosing the usage, or the usage occurs inside a lambda expression that occurs inside C".

The informative text later in that section confirms this intention:
"The restrictions above are designed to catch, at compile time, circular or otherwise malformed initializations."

In case of lambda bodies, self-references don't cause malformed initializations.


Example that shows that javac only disallows unqualified access as per 8.3.2.3:

public class C {
    // OK:
    Runnable qi= () -> executeAndRepost(this.qi);
    static Runnable qs= () -> executeAndRepost(C.qs);
    
    // javac 1.8.0-ea-b115 says: error: self-reference in initializer
    Runnable i= () -> executeAndRepost(i);
    static Runnable s= () -> executeAndRepost(s);
    
    static void executeAndRepost(Runnable r) { }
}
Comment 4 Markus Keller CLA 2013-12-18 13:15:37 EST
Updated the spec request: http://mail.openjdk.java.net/pipermail/lambda-spec-observers/2013-December/000623.html

The problem is actually not only about self-references from the lambda 
body of a field initializer, but about any forward reference that is 
forbidden by JLS7 8.3.2.3.

A better example:
----------
public class D {
    int v1 = value; // Error for 8.3.2.3 (good).
    int v2 = D.this.value; // No simple name -> no error (good).
 
    IntSupplier s1 = () -> value; // Why error?
    IntSupplier s2 = new IntSupplier() {
        @Override
        public int getAsInt() {
            return value; // No error (good).
        }
    };
 
    int value = 42;
}
----------

There's absolutely no problem with the reference in s1's initializer. The 
only problem is that 8.3.2.3 needs to be updated to make lambda bodies 
behave the same as nested class bodies.

REQUEST:
========
8.3.2.3 should get a fifth point:
"- The usage is not in a lambda expression body that occurs inside c."

The next sentence could also be clarified to:
"It is a compile-time error if any of the five requirements above are not 
met for such a forward reference."
========

FWIW, this still excludes the forward reference to "VALUE" here:

    public static final IntConsumer c = (@A(VALUE) int x) -> {};
    public static final int VALUE = 42;
Comment 5 Srikanth Sankaran CLA 2014-01-15 00:50:10 EST
The plan is to conform to the present spec and reference compiler behavior and
react at the right time to any changes as they happen.