changeset 2987:dcd12fa5b58a

8066974: Compiler doesn't infer method's generic type information in lambda body Summary: Add logic to avoid post-inference triggers on temporarty AST types Reviewed-by: vromero
author mcimadamore
date Thu, 12 Nov 2015 21:20:49 +0000
parents 4044eb07194d
children 0caab0d65a04
files src/share/classes/com/sun/tools/javac/comp/Attr.java test/tools/javac/lambda/8066974/T8066974.java test/tools/javac/lambda/8066974/T8066974.out
diffstat 3 files changed, 91 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/Attr.java	Tue Oct 27 10:35:14 2015 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Nov 12 21:20:49 2015 +0000
@@ -156,6 +156,8 @@
         unknownTypeInfo = new ResultInfo(TYP, Type.noType);
         unknownTypeExprInfo = new ResultInfo(Kinds.TYP | Kinds.VAL, Type.noType);
         recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
+
+        noCheckTree = make.at(-1).Skip();
     }
 
     /** Switch: relax some constraints for retrofit mode.
@@ -253,31 +255,34 @@
     Type check(final JCTree tree, final Type found, final int ownkind, final ResultInfo resultInfo) {
         InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext();
         Type owntype;
-        if (!found.hasTag(ERROR) && !resultInfo.pt.hasTag(METHOD) && !resultInfo.pt.hasTag(FORALL)) {
-            if ((ownkind & ~resultInfo.pkind) != 0) {
-                log.error(tree.pos(), "unexpected.type",
+        boolean shouldCheck = !found.hasTag(ERROR) &&
+                !resultInfo.pt.hasTag(METHOD) &&
+                !resultInfo.pt.hasTag(FORALL);
+        if (shouldCheck && (ownkind & ~resultInfo.pkind) != 0) {
+            log.error(tree.pos(), "unexpected.type",
                         kindNames(resultInfo.pkind),
                         kindName(ownkind));
-                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() {
+            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), new FreeTypeListener() {
                     @Override
                     public void typesInferred(InferenceContext inferenceContext) {
                         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;
     }
 
@@ -550,6 +555,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.
      *
@@ -2043,7 +2052,7 @@
                     }
                 });
                 Type constructorType = tree.constructorType = types.createErrorType(clazztype);
-                constructorType = checkId(tree, site,
+                constructorType = checkId(noCheckTree, site,
                         constructor,
                         diamondEnv,
                         diamondResult);
@@ -2069,7 +2078,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,
@@ -2150,7 +2159,7 @@
                     tree.pos(), localEnv, clazztype, argtypes, typeargtypes);
                 Assert.check(sym.kind < AMBIGUOUS);
                 tree.constructor = sym;
-                tree.constructorType = checkId(tree,
+                tree.constructorType = checkId(noCheckTree,
                     clazztype,
                     tree.constructor,
                     localEnv,
@@ -2161,6 +2170,17 @@
                 owntype = clazztype;
         }
         result = check(tree, owntype, 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),
+                    new FreeTypeListener() {
+                        @Override
+                        public void typesInferred(InferenceContext instantiatedContext) {
+                            tree.constructorType = instantiatedContext.asInstType(tree.constructorType);
+                        }
+                    });
+        }
         chk.validate(tree.typeargs, localEnv);
     }
     //where
@@ -2388,6 +2408,7 @@
             preFlow(that);
             flow.analyzeLambda(env, that, make, isSpeculativeRound);
 
+            that.type = currentTarget; //avoids recovery at this stage
             checkLambdaCompatible(that, lambdaType, resultInfo.checkContext);
 
             if (!isSpeculativeRound) {
@@ -2826,7 +2847,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)) {
@@ -2848,6 +2869,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);
@@ -3968,7 +3991,7 @@
                 all_multicatchTypes.append(ctype);
             }
         }
-        Type t = check(tree, types.lub(multicatchTypes.toList()), TYP, resultInfo);
+        Type t = check(noCheckTree, types.lub(multicatchTypes.toList()), TYP, resultInfo);
         if (t.hasTag(CLASS)) {
             List<Type> alternatives =
                 ((all_multicatchTypes == null) ? multicatchTypes : all_multicatchTypes).toList();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/8066974/T8066974.java	Thu Nov 12 21:20:49 2015 +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/test/tools/javac/lambda/8066974/T8066974.out	Thu Nov 12 21:20:49 2015 +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