From 9dba35315129ef0052ef7b2a0b30690624aac229 Mon Sep 17 00:00:00 2001 From: mcimadamore Date: Thu, 12 Nov 2015 21:20:49 +0000 Subject: [PATCH] 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 --- .../com/sun/tools/javac/comp/Attr.java | 63 +++++++++++++------ test/tools/javac/lambda/8066974/T8066974.java | 44 +++++++++++++ test/tools/javac/lambda/8066974/T8066974.out | 4 ++ 3 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 test/tools/javac/lambda/8066974/T8066974.java create mode 100644 test/tools/javac/lambda/8066974/T8066974.out diff --git a/src/share/classes/com/sun/tools/javac/comp/Attr.java b/src/share/classes/com/sun/tools/javac/comp/Attr.java index f1bbf154..b0b6c5cb 100644 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java @@ -156,6 +156,8 @@ public class Attr extends JCTree.Visitor { 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 @@ public class Attr extends JCTree.Visitor { 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; + } + if (tree != noCheckTree) { + tree.type = owntype; } - tree.type = owntype; return owntype; } @@ -550,6 +555,10 @@ public class Attr extends JCTree.Visitor { */ 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 @@ public class Attr extends JCTree.Visitor { } }); Type constructorType = tree.constructorType = types.createErrorType(clazztype); - constructorType = checkId(tree, site, + constructorType = checkId(noCheckTree, site, constructor, diamondEnv, diamondResult); @@ -2069,7 +2078,7 @@ public class Attr extends JCTree.Visitor { 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 @@ public class Attr extends JCTree.Visitor { 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 @@ public class Attr extends JCTree.Visitor { 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 @@ public class Attr extends JCTree.Visitor { 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 @@ public class Attr extends JCTree.Visitor { 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 @@ public class Attr extends JCTree.Visitor { //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 @@ public class Attr extends JCTree.Visitor { 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 alternatives = ((all_multicatchTypes == null) ? multicatchTypes : all_multicatchTypes).toList(); diff --git a/test/tools/javac/lambda/8066974/T8066974.java b/test/tools/javac/lambda/8066974/T8066974.java new file mode 100644 index 00000000..8f47277f --- /dev/null +++ b/test/tools/javac/lambda/8066974/T8066974.java @@ -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 { } + static class RuntimeThrowing extends Throwing { } + static class CheckedThrowing extends Throwing { } + + interface Parameter { + Object m(Throwing tw) throws E; + } + + interface Mapper { + R m(Parameter p); + } + + Z map(Mapper mz) { return null; } + + Mapper> mapper(Throwing tz) throws Z { return null; } + + static class ThrowingMapper implements Mapper> { + ThrowingMapper(Throwing arg) throws X { } + + @Override + public Throwing 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)); + } +} diff --git a/test/tools/javac/lambda/8066974/T8066974.out b/test/tools/javac/lambda/8066974/T8066974.out new file mode 100644 index 00000000..59772fc5 --- /dev/null +++ b/test/tools/javac/lambda/8066974/T8066974.out @@ -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 -- GitLab