8066974: Compiler doesn't infer method's generic type information in lambda body
authormcimadamore
Thu, 18 Dec 2014 13:21:44 +0000
changeset 28148 6415a95b07fd
parent 28147 72e7fdc79b78
child 28149 518c958a4dfa
child 28275 cfbf3c5d12dd
8066974: Compiler doesn't infer method's generic type information in lambda body Summary: Add loghic to avoid post-inference triggers on temporarty AST types Reviewed-by: jlahoda, vromero
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/test/tools/javac/lambda/8066974/T8066974.java
langtools/test/tools/javac/lambda/8066974/T8066974.out
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Dec 17 12:48:04 2014 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Dec 18 13:21:44 2014 +0000
@@ -155,6 +155,8 @@
         unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType);
         unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType);
         recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
+
+        noCheckTree = make.at(-1).Skip();
     }
 
     /** Switch: relax some constraints for retrofit mode.
@@ -214,31 +216,32 @@
                final ResultInfo resultInfo) {
         InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext();
         Type owntype;
-        if (!found.hasTag(ERROR) && !resultInfo.pt.hasTag(METHOD) && !resultInfo.pt.hasTag(FORALL)) {
-            if (!ownkind.subset(resultInfo.pkind)) {
-                log.error(tree.pos(), "unexpected.type",
-                        resultInfo.pkind.kindNames(),
-                        ownkind.kindNames());
-                owntype = types.createErrorType(found);
-            } else if (allowPoly && inferenceContext.free(found)) {
-                //delay the check if there are inference variables in the found type
-                //this means we are dealing with a partially inferred poly expression
-                owntype = resultInfo.pt;
-                inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt), new FreeTypeListener() {
-                    @Override
-                    public void typesInferred(InferenceContext inferenceContext) {
+        boolean shouldCheck = !found.hasTag(ERROR) &&
+                !resultInfo.pt.hasTag(METHOD) &&
+                !resultInfo.pt.hasTag(FORALL);
+        if (shouldCheck && !ownkind.subset(resultInfo.pkind)) {
+            log.error(tree.pos(), "unexpected.type",
+            resultInfo.pkind.kindNames(),
+            ownkind.kindNames());
+            owntype = types.createErrorType(found);
+        } else if (allowPoly && inferenceContext.free(found)) {
+            //delay the check if there are inference variables in the found type
+            //this means we are dealing with a partially inferred poly expression
+            owntype = shouldCheck ? resultInfo.pt : found;
+            inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt),
+                    instantiatedContext -> {
                         ResultInfo pendingResult =
                                 resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
                         check(tree, inferenceContext.asInstType(found), ownkind, pendingResult);
-                    }
-                });
-            } else {
-                owntype = resultInfo.check(tree, found);
-            }
+                    });
         } else {
-            owntype = found;
+            owntype = shouldCheck ?
+            resultInfo.check(tree, found) :
+            found;
         }
-        tree.type = owntype;
+        if (tree != noCheckTree) {
+            tree.type = owntype;
+        }
         return owntype;
     }
 
@@ -514,6 +517,10 @@
      */
     Type result;
 
+    /** Synthetic tree to be used during 'fake' checks.
+     */
+    JCTree noCheckTree;
+
     /** Visitor method: attribute a tree, catching any completion failure
      *  exceptions. Return the tree's type.
      *
@@ -2007,7 +2014,7 @@
                     }
                 });
                 Type constructorType = tree.constructorType = types.createErrorType(clazztype);
-                constructorType = checkId(tree, site,
+                constructorType = checkId(noCheckTree, site,
                         constructor,
                         diamondEnv,
                         diamondResult);
@@ -2033,7 +2040,7 @@
                 tree.constructor = rs.resolveConstructor(
                     tree.pos(), rsEnv, clazztype, argtypes, typeargtypes);
                 if (cdef == null) { //do not check twice!
-                    tree.constructorType = checkId(tree,
+                    tree.constructorType = checkId(noCheckTree,
                             clazztype,
                             tree.constructor,
                             rsEnv,
@@ -2103,7 +2110,7 @@
                     tree.pos(), localEnv, clazztype, argtypes, typeargtypes);
                 Assert.check(!sym.kind.isOverloadError());
                 tree.constructor = sym;
-                tree.constructorType = checkId(tree,
+                tree.constructorType = checkId(noCheckTree,
                     clazztype,
                     tree.constructor,
                     localEnv,
@@ -2114,6 +2121,14 @@
                 owntype = clazztype;
         }
         result = check(tree, owntype, KindSelector.VAL, resultInfo);
+        InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext();
+        if (tree.constructorType != null && inferenceContext.free(tree.constructorType)) {
+            //we need to wait for inference to finish and then replace inference vars in the constructor type
+            inferenceContext.addFreeTypeListener(List.of(tree.constructorType),
+                    instantiatedContext -> {
+                        tree.constructorType = instantiatedContext.asInstType(tree.constructorType);
+                    });
+        }
         chk.validate(tree.typeargs, localEnv);
     }
 
@@ -2290,6 +2305,7 @@
             preFlow(that);
             flow.analyzeLambda(env, that, make, isSpeculativeRound);
 
+            that.type = currentTarget; //avoids recovery at this stage
             checkLambdaCompatible(that, lambdaType, resultInfo.checkContext);
 
             if (!isSpeculativeRound) {
@@ -2730,7 +2746,7 @@
                         that.kind.isUnbound() ? argtypes.tail : argtypes, typeargtypes),
                         new FunctionalReturnContext(resultInfo.checkContext));
 
-            Type refType = checkId(that, lookupHelper.site, refSym, localEnv, checkInfo);
+            Type refType = checkId(noCheckTree, lookupHelper.site, refSym, localEnv, checkInfo);
 
             if (that.kind.isUnbound() &&
                     resultInfo.checkContext.inferenceContext().free(argtypes.head)) {
@@ -2752,6 +2768,8 @@
             //is a no-op (as this has been taken care during method applicability)
             boolean isSpeculativeRound =
                     resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.SPECULATIVE;
+
+            that.type = currentTarget; //avoids recovery at this stage
             checkReferenceCompatible(that, desc, refType, resultInfo.checkContext, isSpeculativeRound);
             if (!isSpeculativeRound) {
                 checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget);
@@ -3882,7 +3900,7 @@
                 all_multicatchTypes.append(ctype);
             }
         }
-        Type t = check(tree, types.lub(multicatchTypes.toList()),
+        Type t = check(noCheckTree, types.lub(multicatchTypes.toList()),
                 KindSelector.TYP, resultInfo);
         if (t.hasTag(CLASS)) {
             List<Type> alternatives =
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8066974/T8066974.java	Thu Dec 18 13:21:44 2014 +0000
@@ -0,0 +1,44 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8066974
+ * @summary Compiler doesn't infer method's generic type information in lambda body
+ * @compile/fail/ref=T8066974.out -XDrawDiagnostics T8066974.java
+ */
+class T8066974 {
+    static class Throwing<E extends Throwable> { }
+    static class RuntimeThrowing extends Throwing<RuntimeException> { }
+    static class CheckedThrowing extends Throwing<Exception> { }
+
+    interface Parameter {
+        <E extends Throwable> Object m(Throwing<E> tw) throws E;
+    }
+
+    interface Mapper<R> {
+        R m(Parameter p);
+    }
+
+    <Z> Z map(Mapper<Z> mz) { return null; }
+
+    <Z extends Throwable> Mapper<Throwing<Z>> mapper(Throwing<Z> tz) throws Z { return null; }
+
+    static class ThrowingMapper<X extends Throwable> implements Mapper<Throwing<X>> {
+        ThrowingMapper(Throwing<X> arg) throws X { }
+
+        @Override
+        public Throwing<X> m(Parameter p) {
+        return null;
+        }
+    }
+
+    void testRuntime(RuntimeThrowing rt) {
+        map(p->p.m(rt));
+        map(mapper(rt));
+        map(new ThrowingMapper<>(rt));
+    }
+
+    void testChecked(CheckedThrowing ct) {
+        map(p->p.m(ct));
+        map(mapper(ct));
+        map(new ThrowingMapper<>(ct));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8066974/T8066974.out	Thu Dec 18 13:21:44 2014 +0000
@@ -0,0 +1,4 @@
+T8066974.java:40:19: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception
+T8066974.java:41:19: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception
+T8066974.java:42:13: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception
+3 errors