8004105: Expression statement lambdas should be void-compatible
authormcimadamore
Fri, 30 Nov 2012 15:14:12 +0000
changeset 14722 aaa39655aa2e
parent 14721 071e3587f212
child 14723 46aa71a5e4e0
8004105: Expression statement lambdas should be void-compatible Summary: Fix lambda compatibility rules as per latest EDR Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java
langtools/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java
langtools/test/tools/javac/lambda/LambdaConv21.java
langtools/test/tools/javac/lambda/LambdaConv21.out
langtools/test/tools/javac/lambda/VoidCompatibility.out
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Nov 29 09:41:48 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Nov 30 15:14:12 2012 +0000
@@ -2244,9 +2244,13 @@
             //with the target-type, it will be recovered anyway in Attr.checkId
             needsRecovery = false;
 
+            FunctionalReturnContext funcContext = that.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
+                    new ExpressionLambdaReturnContext((JCExpression)that.getBody(), resultInfo.checkContext) :
+                    new FunctionalReturnContext(resultInfo.checkContext);
+
             ResultInfo bodyResultInfo = lambdaType.getReturnType() == Type.recoveryType ?
                 recoveryInfo :
-                new ResultInfo(VAL, lambdaType.getReturnType(), new LambdaReturnContext(resultInfo.checkContext));
+                new ResultInfo(VAL, lambdaType.getReturnType(), funcContext);
             localEnv.info.returnResult = bodyResultInfo;
 
             if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
@@ -2327,8 +2331,9 @@
          * type according to both the inherited context and the assignment
          * context.
          */
-        class LambdaReturnContext extends Check.NestedCheckContext {
-            public LambdaReturnContext(CheckContext enclosingContext) {
+        class FunctionalReturnContext extends Check.NestedCheckContext {
+
+            FunctionalReturnContext(CheckContext enclosingContext) {
                 super(enclosingContext);
             }
 
@@ -2344,6 +2349,23 @@
             }
         }
 
+        class ExpressionLambdaReturnContext extends FunctionalReturnContext {
+
+            JCExpression expr;
+
+            ExpressionLambdaReturnContext(JCExpression expr, CheckContext enclosingContext) {
+                super(enclosingContext);
+                this.expr = expr;
+            }
+
+            @Override
+            public boolean compatible(Type found, Type req, Warner warn) {
+                //a void return is compatible with an expression statement lambda
+                return TreeInfo.isExpressionStatement(expr) && req.hasTag(VOID) ||
+                        super.compatible(found, req, warn);
+            }
+        }
+
         /**
         * Lambda compatibility. Check that given return types, thrown types, parameter types
         * are compatible with the expected functional interface descriptor. This means that:
@@ -2560,7 +2582,7 @@
 
         if (!returnType.hasTag(VOID) && !resType.hasTag(VOID)) {
             if (resType.isErroneous() ||
-                    new LambdaReturnContext(checkContext).compatible(resType, returnType, types.noWarnings)) {
+                    new FunctionalReturnContext(checkContext).compatible(resType, returnType, types.noWarnings)) {
                 incompatibleReturnType = null;
             }
         }
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Nov 29 09:41:48 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Nov 30 15:14:12 2012 +0000
@@ -3171,21 +3171,12 @@
     /** Check that given tree is a legal expression statement.
      */
     protected JCExpression checkExprStat(JCExpression t) {
-        switch(t.getTag()) {
-        case PREINC: case PREDEC:
-        case POSTINC: case POSTDEC:
-        case ASSIGN:
-        case BITOR_ASG: case BITXOR_ASG: case BITAND_ASG:
-        case SL_ASG: case SR_ASG: case USR_ASG:
-        case PLUS_ASG: case MINUS_ASG:
-        case MUL_ASG: case DIV_ASG: case MOD_ASG:
-        case APPLY: case NEWCLASS:
-        case ERRONEOUS:
-            return t;
-        default:
+        if (!TreeInfo.isExpressionStatement(t)) {
             JCExpression ret = F.at(t.pos).Erroneous(List.<JCTree>of(t));
             error(ret, "not.stmt");
             return ret;
+        } else {
+            return t;
         }
     }
 
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Thu Nov 29 09:41:48 2012 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Fri Nov 30 15:14:12 2012 +0000
@@ -267,6 +267,25 @@
         return lambda.params.isEmpty() ||
                 lambda.params.head.vartype != null;
     }
+
+    /** Return true if the tree corresponds to an expression statement */
+    public static boolean isExpressionStatement(JCExpression tree) {
+        switch(tree.getTag()) {
+            case PREINC: case PREDEC:
+            case POSTINC: case POSTDEC:
+            case ASSIGN:
+            case BITOR_ASG: case BITXOR_ASG: case BITAND_ASG:
+            case SL_ASG: case SR_ASG: case USR_ASG:
+            case PLUS_ASG: case MINUS_ASG:
+            case MUL_ASG: case DIV_ASG: case MOD_ASG:
+            case APPLY: case NEWCLASS:
+            case ERRONEOUS:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     /**
      * Return true if the AST corresponds to a static select of the kind A.B
      */
--- a/langtools/test/tools/javac/lambda/LambdaConv21.java	Thu Nov 29 09:41:48 2012 +0000
+++ b/langtools/test/tools/javac/lambda/LambdaConv21.java	Fri Nov 30 15:14:12 2012 +0000
@@ -23,7 +23,7 @@
     static void testExpressionLambda() {
         SAM_void s1 = ()->m_void(); //ok
         SAM_java_lang_Void s2 = ()->m_void(); //no - incompatible target
-        SAM_void s3 = ()->m_java_lang_Void(); //no - incompatible target
+        SAM_void s3 = ()->m_java_lang_Void(); //ok - expression statement lambda is compatible with void
         SAM_java_lang_Void s4 = ()->m_java_lang_Void(); //ok
     }
 
--- a/langtools/test/tools/javac/lambda/LambdaConv21.out	Thu Nov 29 09:41:48 2012 +0000
+++ b/langtools/test/tools/javac/lambda/LambdaConv21.out	Fri Nov 30 15:14:12 2012 +0000
@@ -1,6 +1,5 @@
 LambdaConv21.java:25:43: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.inconvertible.types: void, java.lang.Void))
-LambdaConv21.java:26:43: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.inconvertible.types: java.lang.Void, void))
 LambdaConv21.java:32:33: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.missing.ret.val: java.lang.Void))
 LambdaConv21.java:33:53: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.unexpected.ret.val))
 LambdaConv21.java:36:33: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.missing.ret.val: java.lang.Void))
-5 errors
+4 errors
--- a/langtools/test/tools/javac/lambda/VoidCompatibility.out	Thu Nov 29 09:41:48 2012 +0000
+++ b/langtools/test/tools/javac/lambda/VoidCompatibility.out	Fri Nov 30 15:14:12 2012 +0000
@@ -1,2 +1,3 @@
+VoidCompatibility.java:17:9: compiler.err.ref.ambiguous: schedule, kindname.method, schedule(VoidCompatibility.Runnable), VoidCompatibility, kindname.method, schedule(VoidCompatibility.Thunk<?>), VoidCompatibility
 VoidCompatibility.java:23:9: compiler.err.ref.ambiguous: schedule, kindname.method, schedule(VoidCompatibility.Runnable), VoidCompatibility, kindname.method, schedule(VoidCompatibility.Thunk<?>), VoidCompatibility
-1 error
+2 errors