diff options
author | Stephan Herrmann | 2016-10-20 18:00:12 +0000 |
---|---|---|
committer | Stephan Herrmann | 2016-10-20 21:13:19 +0000 |
commit | 2bdcd6f13b13ec00844affdb10db3ef2ded868da (patch) | |
tree | 244b2ccd1445ca93b73baa11c0395e2c5bdf6dbf | |
parent | 92a3e8d9e72ad7550c19af076fad5fed3497b8b7 (diff) | |
download | eclipse.jdt.core-I20161024-1000.tar.gz eclipse.jdt.core-I20161024-1000.tar.xz eclipse.jdt.core-I20161024-1000.zip |
Bug 499725: Failure to infer type in lambdasI20161027-0700I20161027-0200I20161026-2000I20161026-1100I20161026-0800I20161025-2000I20161024-2000I20161024-1000I20161024-0410I20161023-2000
Change-Id: I83ffe3f1eb9285f6d28dfa29f8119891b7284133
4 files changed, 137 insertions, 22 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java index c82d1c27b3..5b965903c4 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java @@ -7253,4 +7253,60 @@ public void testBug502568() { "}\n" }); } +public void testBug499725() { + runConformTest( + new String[] { + "Try22.java", + "import java.rmi.RemoteException;\n" + + "import java.util.Arrays;\n" + + "import java.util.Collection;\n" + + "import java.util.Collections;\n" + + "import java.util.List;\n" + + "import java.util.function.Function;\n" + + "import java.util.stream.Collectors;\n" + + "\n" + + "\n" + + "public class Try22 {\n" + + " public static class RemoteExceptionWrapper {\n" + + " @FunctionalInterface\n" + + " public static interface FunctionRemote<T, R> {\n" + + " R apply(T t) throws RemoteException;\n" + + " }\n" + + " \n" + + " public static <T, R> Function<T, R> wrapFunction(FunctionRemote<T, R> f) {\n" + + " return x -> {\n" + + " try {\n" + + " return f.apply(x);\n" + + " }\n" + + " catch(RemoteException e) {\n" + + " throw new RuntimeException(e);\n" + + " }\n" + + " };\n" + + " }\n" + + " }\n" + + "\n" + + "\n" + + " private static class ThrowingThingy {\n" + + " public Collection<String> listStuff(String in) throws RemoteException {\n" + + " return Collections.emptyList();\n" + + " }\n" + + " }\n" + + "\n" + + " \n" + + " public static void main(String[] args) {\n" + + " List<String> stagedNodes = Arrays.asList(\"a\", \"b\", \"c\");\n" + + " ThrowingThingy remoteThing = new ThrowingThingy(); // simulation of a rmi remote, hence the exceptio\n" + + " \n" + + " List<String> resultingStuff = stagedNodes.stream()\n" + + " .flatMap(RemoteExceptionWrapper.wrapFunction(\n" + + " node -> remoteThing.listStuff(node) // HERE\n" + + " .stream()\n" + + " .map(sub -> node + \"/\" + sub)))\n" + + " .collect(Collectors.toList());\n" + + " \n" + + " System.out.println(resultingStuff);\n" + + " }\n" + + "}\n" + }); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java index 799b42e193..75eb903342 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java @@ -8576,7 +8576,7 @@ public void test428177() { "5. ERROR in X.java (at line 38)\n" + " return stream.collect(Collectors.toList()); // NO ERROR\n" + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from List<capture#18-of ? extends String> to Stream<String>\n" + + "Type mismatch: cannot convert from List<capture#19-of ? extends String> to Stream<String>\n" + "----------\n"); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428795, - [1.8]Internal compiler error: java.lang.NullPointerException at org.eclipse.jdt.internal.compiler.ast.MessageSend.analyseCode diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java index 6432e23c1e..f903676f4c 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java @@ -121,7 +121,7 @@ class ConstraintExpressionFormula extends ConstraintFormula { inferInvocationApplicability(inferenceContext, method, argumentTypes, isDiamond, inferenceContext.inferenceKind); // b2 has been lifted, inferring poly invocation type amounts to lifting b3. } - if (!inferPolyInvocationType(inferenceContext, invocation, this.right, method)) + if (!inferenceContext.computeB3(invocation, this.right, method)) return FALSE; return null; // already incorporated } finally { @@ -296,7 +296,7 @@ class ConstraintExpressionFormula extends ConstraintFormula { InferenceContext18 innerContext = reference.getInferenceContext((ParameterizedMethodBinding) compileTimeDecl); int innerInferenceKind = determineInferenceKind(compileTimeDecl, argumentTypes, innerContext); inferInvocationApplicability(inferenceContext, original, argumentTypes, original.isConstructor()/*mimic a diamond?*/, innerInferenceKind); - if (!inferPolyInvocationType(inferenceContext, reference, r, original)) + if (!inferenceContext.computeB3(reference, r, original)) return FALSE; return null; // already incorporated } catch (InferenceFailureException e) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java index 0710d9b904..07851ae047 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java @@ -159,6 +159,10 @@ public class InferenceContext18 { ReferenceBinding object; // java.lang.Object public BoundSet b2; private BoundSet b3; + /** Not per JLS: inbox for emulation of how javac passes type bounds from inner to outer */ + private BoundSet innerInbox; + /** Not per JLS: signal when current is ready to directly merge all bounds from inner. */ + private boolean directlyAcceptingInnerBounds = false; // InferenceVariable interning: private InferenceVariable[] internedVariables; @@ -394,6 +398,19 @@ public class InferenceContext18 { ConstraintExpressionFormula.inferInvocationApplicability(this, method, arguments, isDiamond, this.inferenceKind); } + /** Perform steps from JLS 18.5.2. needed for computing the bound set B3. */ + boolean computeB3(InvocationSite invocationSite, TypeBinding targetType, MethodBinding method) + throws InferenceFailureException + { + boolean result = ConstraintExpressionFormula.inferPolyInvocationType(this, invocationSite, targetType, method); + if (result) { + mergeInnerBounds(); + if (this.b3 == null) + this.b3 = this.currentBounds.copy(); + } + return result; + } + /** JLS 18.5.2 Invocation Type Inference */ public BoundSet inferInvocationType(TypeBinding expectedType, InvocationSite invocationSite, MethodBinding method) throws InferenceFailureException @@ -412,11 +429,13 @@ public class InferenceContext18 { && ((Expression)invocationSite).isPolyExpression(method)) { // 3. bullet: special treatment for poly expressions - if (!ConstraintExpressionFormula.inferPolyInvocationType(this, invocationSite, expectedType, method)) { + if (!computeB3(invocationSite, expectedType, method)) { return null; } + } else { + mergeInnerBounds(); + this.b3 = this.currentBounds.copy(); } - this.b3 = this.currentBounds.copy(); if (SHOULD_WORKAROUND_BUG_JDK_8153748) { // "before 18.5.2", but should not spill into b3 ... (heuristically) ReductionResult jdk8153748result = addJDK_8153748ConstraintsFromInvocation(this.invocationArguments, method); @@ -425,9 +444,12 @@ public class InferenceContext18 { } } + pushBoundsToOuter(); + this.directlyAcceptingInnerBounds = true; + // 4. bullet: assemble C: Set<ConstraintFormula> c = new HashSet<ConstraintFormula>(); - if (!addConstraintsToC(this.invocationArguments, c, method, this.inferenceKind, false, invocationSite)) + if (!addConstraintsToC(this.invocationArguments, c, method, this.inferenceKind, invocationSite)) return null; // 5. bullet: determine B4 from C List<Set<InferenceVariable>> components = this.currentBounds.computeConnectedComponents(this.inferenceVariables); @@ -480,6 +502,42 @@ public class InferenceContext18 { } } + // --- not per JLS: emulate how javac passes type bounds from inner to outer: --- + /** Not per JLS: push current bounds to outer inference if outer is ready for it. */ + private void pushBoundsToOuter() { + InferenceContext18 outer = this.outerContext; + if (outer != null && outer.stepCompleted >= APPLICABILITY_INFERRED) { + if (outer.directlyAcceptingInnerBounds) { + outer.currentBounds.addBounds(this.currentBounds, this.environment); + } else if (outer.innerInbox == null) { + outer.innerInbox = this.currentBounds.copy(); + } else { + outer.innerInbox.addBounds(this.currentBounds, this.environment); + } + } + } + /** Not JLS: merge pending bounds of inner inference into current. */ + private void mergeInnerBounds() { + if (this.innerInbox != null) { + this.currentBounds.addBounds(this.innerInbox, this.environment); + this.innerInbox = null; + } + } + + interface InferenceOperation { + boolean perform() throws InferenceFailureException; + } + /** Not per JLS: if operation succeeds merge new bounds from inner into current. */ + private boolean collectingInnerBounds(InferenceOperation operation) throws InferenceFailureException { + boolean result = operation.perform(); + if (result) + mergeInnerBounds(); + else + this.innerInbox = null; + return result; + } + // --- + private ReductionResult addJDK_8153748ConstraintsFromInvocation(Expression[] arguments, MethodBinding method) throws InferenceFailureException { // not per JLS, trying to mimic javac behavior boolean constraintAdded = false; @@ -519,12 +577,12 @@ public class InferenceContext18 { private ReductionResult addJDK_8153748ConstraintsFromFunctionalExpr(FunctionalExpression functionalExpr, TypeBinding targetType, MethodBinding method) throws InferenceFailureException { if (!functionalExpr.isPertinentToApplicability(targetType, method)) { - ConstraintFormula newConstraint = new ConstraintExpressionFormula(functionalExpr, targetType, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT); - if (newConstraint.inputVariables(this).isEmpty()) { // input variable would signal: not ready for inference - if (!reduceAndIncorporate(newConstraint)) + ConstraintFormula exprConstraint = new ConstraintExpressionFormula(functionalExpr, targetType, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT); + if (collectingInnerBounds(() -> exprConstraint.inputVariables(this).isEmpty())) { // input variable would signal: not ready for inference + if (!collectingInnerBounds(() -> reduceAndIncorporate(exprConstraint))) return ReductionResult.FALSE; - newConstraint = new ConstraintExceptionFormula(functionalExpr, targetType); // ?? - if (!reduceAndIncorporate(newConstraint)) + ConstraintFormula excConstraint = new ConstraintExceptionFormula(functionalExpr, targetType); // ?? + if (!collectingInnerBounds(() -> reduceAndIncorporate(excConstraint))) return ReductionResult.FALSE; return ReductionResult.TRUE; } @@ -532,7 +590,7 @@ public class InferenceContext18 { return null; } - private boolean addConstraintsToC(Expression[] exprs, Set<ConstraintFormula> c, MethodBinding method, int inferenceKindForMethod, boolean interleaved, InvocationSite site) + private boolean addConstraintsToC(Expression[] exprs, Set<ConstraintFormula> c, MethodBinding method, int inferenceKindForMethod, InvocationSite site) throws InferenceFailureException { TypeBinding[] fs; @@ -559,14 +617,14 @@ public class InferenceContext18 { TypeBinding fsi = fs[Math.min(i, p-1)]; InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this.environment, this.inferenceVariables, site); TypeBinding substF = inferenceSubstitution.substitute(inferenceSubstitution,fsi); - if (!addConstraintsToC_OneExpr(exprs[i], c, fsi, substF, method, interleaved)) + if (!addConstraintsToC_OneExpr(exprs[i], c, fsi, substF, method)) return false; } } return true; } - private boolean addConstraintsToC_OneExpr(Expression expri, Set<ConstraintFormula> c, TypeBinding fsi, TypeBinding substF, MethodBinding method, boolean interleaved) + private boolean addConstraintsToC_OneExpr(Expression expri, Set<ConstraintFormula> c, TypeBinding fsi, TypeBinding substF, MethodBinding method) throws InferenceFailureException { // -- not per JLS, emulate javac behavior: @@ -595,7 +653,7 @@ public class InferenceContext18 { Expression[] resultExpressions = lambda.resultExpressions(); for (int i = 0, length = resultExpressions == null ? 0 : resultExpressions.length; i < length; i++) { Expression resultExpression = resultExpressions[i]; - if (!addConstraintsToC_OneExpr(resultExpression, c, r.original(), r, method, true)) + if (!addConstraintsToC_OneExpr(resultExpression, c, r.original(), r, method)) return false; } } @@ -619,22 +677,22 @@ public class InferenceContext18 { if (innerMethod instanceof ParameterizedGenericMethodBinding) innerContext = invocation.getInferenceContext((ParameterizedGenericMethodBinding) innerMethod); - if (interleaved && innerContext != null) { + if (innerContext != null) { MethodBinding shallowMethod = innerMethod.shallowOriginal(); innerContext.outerContext = this; if (innerContext.stepCompleted < InferenceContext18.APPLICABILITY_INFERRED) // shouldn't happen, but let's play safe innerContext.inferInvocationApplicability(shallowMethod, argumentTypes, shallowMethod.isConstructor()); - if (!ConstraintExpressionFormula.inferPolyInvocationType(innerContext, invocation, substF, shallowMethod)) + if (!innerContext.computeB3(invocation, substF, shallowMethod)) return false; - return innerContext.addConstraintsToC(arguments, c, innerMethod.genericMethod(), innerContext.inferenceKind, interleaved, invocation); + return innerContext.addConstraintsToC(arguments, c, innerMethod.genericMethod(), innerContext.inferenceKind, invocation); } else { int applicabilityKind = getInferenceKind(innerMethod, argumentTypes); - return this.addConstraintsToC(arguments, c, innerMethod.genericMethod(), applicabilityKind, interleaved, invocation); + return this.addConstraintsToC(arguments, c, innerMethod.genericMethod(), applicabilityKind, invocation); } } else if (expri instanceof ConditionalExpression) { ConditionalExpression ce = (ConditionalExpression) expri; - return addConstraintsToC_OneExpr(ce.valueIfTrue, c, fsi, substF, method, interleaved) - && addConstraintsToC_OneExpr(ce.valueIfFalse, c, fsi, substF, method, interleaved); + return addConstraintsToC_OneExpr(ce.valueIfTrue, c, fsi, substF, method) + && addConstraintsToC_OneExpr(ce.valueIfFalse, c, fsi, substF, method); } return true; } @@ -1504,7 +1562,8 @@ public class InferenceContext18 { this.currentBounds.addBounds(innerCtx.b2, this.environment); this.inferenceVariables = innerCtx.inferenceVariables; this.inferenceKind = innerCtx.inferenceKind; - innerCtx.outerContext = this; + if (!isSameSite(innerCtx.currentInvocation, this.currentInvocation)) + innerCtx.outerContext = this; this.usesUncheckedConversion = innerCtx.usesUncheckedConversion; for (InferenceVariable variable : this.inferenceVariables) if (!isInterned(variable)) |