# HG changeset patch # User mcimadamore # Date 1447363249 0 # Node ID dcd12fa5b58a18ffee5aa32533385f8f15c52225 # Parent 4044eb07194db0e85514ef76bc7e92e04755d488 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 diff -r 4044eb07194d -r dcd12fa5b58a src/share/classes/com/sun/tools/javac/comp/Attr.java --- 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 alternatives = ((all_multicatchTypes == null) ? multicatchTypes : all_multicatchTypes).toList(); diff -r 4044eb07194d -r dcd12fa5b58a test/tools/javac/lambda/8066974/T8066974.java --- /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 { } + 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 -r 4044eb07194d -r dcd12fa5b58a test/tools/javac/lambda/8066974/T8066974.out --- /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