Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Herrmann2013-12-10 15:12:18 +0000
committerStephan Herrmann2013-12-11 00:44:25 +0000
commitd72b33df7e926bdba104646a573cf60af359abd4 (patch)
treeadcad7c148754ab85e9ddf6a9b8143ef93dc5d14
parentd3de41afc4bae04cfee3367369f0e9174a3b79dd (diff)
downloadeclipse.jdt.core-d72b33df7e926bdba104646a573cf60af359abd4.tar.gz
eclipse.jdt.core-d72b33df7e926bdba104646a573cf60af359abd4.tar.xz
eclipse.jdt.core-d72b33df7e926bdba104646a573cf60af359abd4.zip
Resolved Bug 419048
- revamped much of the control for piecemeal / repeated resolving
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java63
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java12
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/OverloadResolutionTest8.java17
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java96
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java19
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java58
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java20
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/PolyExpression.java28
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java14
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java41
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java18
-rw-r--r--org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java2
19 files changed, 360 insertions, 78 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
index 200715895b..bcd6bbdadb 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java
@@ -22,7 +22,7 @@ import junit.framework.Test;
public class LambdaExpressionsTest extends AbstractRegressionTest {
static {
- TESTS_NAMES = new String[] { "testBug419048_1"};
+// TESTS_NAMES = new String[] { "testReferenceExpressionInference3a"};
// TESTS_NUMBERS = new int[] { 50 };
// TESTS_RANGE = new int[] { 11, -1 };
}
@@ -1934,7 +1934,6 @@ public void testReferenceExpressionInference3a() {
}
// previous test demonstrates that a solution exists, just inference doesn't find it.
-// FAIL: the second error is intermittently reported twice
public void testReferenceExpressionInference3b() {
runNegativeTest(
new String[] {
@@ -1955,7 +1954,7 @@ public void testReferenceExpressionInference3b() {
"1. ERROR in X.java (at line 7)\n" +
" I<X,String> x2s = compose(this::bar, this::i2s);\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
- "Type mismatch: cannot convert from I<Object,Object> to I<X,String>\n" +
+ "Type mismatch: cannot convert from I<Object,Object> to I<X,String>\n" +
"----------\n" +
"2. ERROR in X.java (at line 7)\n" +
" I<X,String> x2s = compose(this::bar, this::i2s);\n" +
@@ -2031,6 +2030,64 @@ public void testBug419048_1() {
"}\n"
});
}
+
+public void testBug419048_2() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "import java.util.*;\n" +
+ "import java.util.function.*;\n" +
+ "import java.util.stream.*;\n" +
+ "public class X {\n" +
+ " public void test() {\n" +
+ " List<Person> roster = new ArrayList<>();\n" +
+ " \n" +
+ " Map<String, Person> map = \n" +
+ " roster\n" +
+ " .stream()\n" +
+ " .collect(\n" +
+ " Collectors.toMap(\n" +
+ " Person::getLast,\n" +
+ " Function.identity()\n" +
+ " ));\n" +
+ " }\n" +
+ "}\n" +
+ "class Person {\n" +
+ " public String getLast() { return null; }\n" +
+ "}\n"
+ });
+}
+
+public void testBug419048_3() {
+ runConformTest(
+ new String[] {
+ "X.java",
+ "import java.util.*;\n" +
+ "import java.util.function.*;\n" +
+ "import java.util.stream.*;\n" +
+ "public class X {\n" +
+ " public void test() {\n" +
+ " List<Person> roster = new ArrayList<>();\n" +
+ " \n" +
+ " Map<String, Person> map = \n" +
+ " roster\n" +
+ " .stream()\n" +
+ " .collect(\n" +
+ " Collectors.toMap(\n" +
+ " new Function<Person, String>() {\n" +
+ " public String apply(Person p) { \n" +
+ " return p.getLast(); \n" +
+ " } \n" +
+ " },\n" +
+ " Function.identity()\n" +
+ " ));\n" +
+ " }\n" +
+ "}\n" +
+ "class Person {\n" +
+ " public String getLast() { return null; }\n" +
+ "}\n"
+ });
+}
public static Class testClass() {
return LambdaExpressionsTest.class;
}
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 8b45389090..7baa493779 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
@@ -5744,11 +5744,13 @@ public void test401939a() {
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" foo(()->{ if (1 == 2) throw new RuntimeException(); });\n" +
- " ^^^\n" +
- "The method foo(I) in the type X is not applicable for the arguments (() -> {\n" +
- " if ((1 == 2))\n" +
- " throw new RuntimeException();\n" +
- "})\n" +
+ " ^^^^\n" +
+ "This method must return a result of type int\n" +
+ "----------\n" +
+ "3. WARNING in X.java (at line 8)\n" +
+ " foo(()->{ if (1 == 2) throw new RuntimeException(); });\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
"----------\n");
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=401939, [1.8][compiler] Incorrect shape analysis leads to method resolution failure .
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/OverloadResolutionTest8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/OverloadResolutionTest8.java
index 85009de74b..ff1c896a80 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/OverloadResolutionTest8.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/OverloadResolutionTest8.java
@@ -152,11 +152,18 @@ public void test004() {
"----------\n" +
"1. ERROR in X.java (at line 11)\n" +
" goo(()-> { \n" +
- " ^^^\n" +
- "The method goo(J) in the type X is not applicable for the arguments (() -> {\n" +
- " boolean y = true;\n" +
- " while (y) ;\n" +
- "})\n" +
+ " ^^^^\n" +
+ "This method must return a result of type int\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 15)\n" +
+ " goo(()-> { \n" +
+ " ^^^^\n" +
+ "This method must return a result of type int\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 18)\n" +
+ " goo(()-> { \n" +
+ " ^^^^\n" +
+ "This method must return a result of type int\n" +
"----------\n");
}
public void test005() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
index f99625febd..00a329bcd2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
@@ -53,6 +53,7 @@ import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
@@ -626,51 +627,96 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
return output;
}
- public static boolean resolvePolyExpressionArguments(BlockScope scope, MethodBinding methodBinding, Expression [] arguments, TypeBinding[] argumentTypes) {
+ /**
+ * After a first round of method lookup has produces 'methodBinding' but when poly expressions have been seen as arguments,
+ * inspect the arguments to trigger another round of resolving with improved target types from the methods parameters.
+ * If this resolving produces better types for any arguments, update the 'argumentTypes' array in-place and
+ * signal by returning null that the outer should perform another round of method lookup.
+ * @param invocation the outer invocation which is being resolved
+ * @param scope scope
+ * @param methodBinding the candidate method produced by the first round of lookup
+ * @param argumentTypes the argument types as collected from first resolving the invocation arguments and as used for
+ * the first round of method lookup.
+ * @return true signals that the caller should try another round of method lookup
+ */
+ public static boolean resolvePolyExpressionArguments(Invocation invocation, BlockScope scope, MethodBinding methodBinding, TypeBinding[] argumentTypes) {
+ int problemReason = 0;
MethodBinding candidateMethod;
if (methodBinding.isValidBinding()) {
candidateMethod = methodBinding;
} else if (methodBinding instanceof ProblemMethodBinding) {
+ problemReason = methodBinding.problemId();
candidateMethod = ((ProblemMethodBinding) methodBinding).closestMatch;
} else {
candidateMethod = null;
}
+ boolean polyExpressionsHaveErrors = false;
boolean hasUpdatedInner = false;
if (candidateMethod != null) {
boolean variableArity = candidateMethod.isVarargs();
final TypeBinding[] parameters = candidateMethod.parameters;
final int parametersLength = parameters.length;
+ Expression [] arguments = invocation.arguments();
for (int i = 0, length = arguments == null ? 0 : arguments.length; i < length; i++) {
Expression argument = arguments[i];
TypeBinding parameterType = i < parametersLength ? parameters[i] : variableArity ? parameters[parametersLength - 1] : null;
- if (argumentTypes[i] instanceof PolyTypeBinding) {
+ TypeBinding updatedArgumentType = null;
+
+ if (argument instanceof Invocation) {
+ boolean invocationFinished = false;
+ Invocation innerInvocation = (Invocation) argument;
+ MethodBinding updatedMethod = innerInvocation.binding();
+ if (innerInvocation.hasInferenceFinished() && updatedMethod != null) {
+ // outer invocation already included invocation type inference for this inner
+ invocationFinished = true;
+ } else {
+ // Inner Inference?
+ InferenceContext18 infCtx18 = innerInvocation.inferenceContext();
+ if (infCtx18 != null) {
+ // Previous time around we only performed Invocation Applicability Inference, do the rest now:
+ updatedMethod = infCtx18.getInvocationTypeInferenceSolution(innerInvocation, parameterType);
+ if (updatedMethod != null) {
+ innerInvocation.updateBindings(updatedMethod);
+ invocationFinished = true;
+ }
+ }
+ }
+ if (invocationFinished) {
+ if (updatedMethod.isConstructor())
+ updatedArgumentType = updatedMethod.declaringClass;
+ else
+ updatedArgumentType = updatedMethod.returnType;
+ }
+ }
+
+ if (updatedArgumentType == null && argumentTypes[i] instanceof PolyTypeBinding) {
argument.setExpressionContext(parameterType != null ? ExpressionContext.INVOCATION_CONTEXT: ExpressionContext.ASSIGNMENT_CONTEXT); // force the errors to surface.
if (variableArity && i >= parametersLength - 1)
argument.tagAsEllipsisArgument();
- argument.setExpectedType(parameterType);
- TypeBinding argumentType = argument.resolveType(scope);
-// if (argumentType == null || !argumentType.isValidBinding())
-// polyExpressionsHaveErrors = true;
-// if (argument instanceof LambdaExpression && ((LambdaExpression) argument).hasErrors())
-// polyExpressionsHaveErrors = true;
- } else if (argument instanceof Invocation) {
- Invocation invocation = (Invocation) argument;
- InferenceContext18 infCtx18 = invocation.inferenceContext();
- if (infCtx18 != null) {
- // Previous time around we only performed Invocation Applicability Inference, do the rest now:
- MethodBinding updatedMethod = infCtx18.getInvocationTypeInferenceSolution(invocation, parameterType);
- if (updatedMethod != null) {
- invocation.updateBindings(updatedMethod);
- // update the argumentTypes array (supposed to be owned by the calling method)
- // in order to give better information into a second round of method lookup:
- if (updatedMethod.isConstructor())
- argumentTypes[i] = updatedMethod.declaringClass;
- else
- argumentTypes[i] = updatedMethod.returnType;
- // signal that we need another round of method lookup:
- hasUpdatedInner = true;
- }
+ // perform the level of resolving suitable for the state of affairs at the enclosing context:
+ if (invocation.hasInferenceFinished()) {
+ argument.setExpectedType(parameterType);
+ updatedArgumentType = argument.resolveType(scope);
+ } else {
+ updatedArgumentType = argument.resolveTentatively(scope, parameterType);
}
+ TypeBinding argumentType = argument.resolvedType;
+ if (argumentType == null || !argumentType.isValidBinding())
+ polyExpressionsHaveErrors = true;
+ if (argument instanceof LambdaExpression && ((LambdaExpression) argument).hasErrors())
+ polyExpressionsHaveErrors = true;
+ }
+
+ if (!polyExpressionsHaveErrors // don't propagate typing results from polies with errors
+ && problemReason != ProblemReasons.Ambiguous // preserve this error
+ && updatedArgumentType != null // do we have a relevant update? ...
+ && !(updatedArgumentType instanceof PolyTypeBinding)
+ && TypeBinding.notEquals(updatedArgumentType, argumentTypes[i]))
+ {
+ // update the argumentTypes array (supposed to be owned by the calling method)
+ // in order to give better information into a second round of method lookup:
+ argumentTypes[i] = updatedArgumentType;
+ hasUpdatedInner = true;
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
index 79a9fa6f9d..d3e65f35ab 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
@@ -446,7 +446,7 @@ public TypeBinding resolveType(BlockScope scope) {
}
ReferenceBinding allocationType = (ReferenceBinding) this.resolvedType;
- this.binding = findConstructorBinding(scope, this, allocationType, this.arguments, argumentTypes, polyExpressionSeen);
+ this.binding = findConstructorBinding(scope, this, allocationType, argumentTypes, polyExpressionSeen);
if (!this.binding.isValidBinding()) {
if (this.binding.declaringClass == null) {
@@ -622,11 +622,18 @@ public Expression[] arguments() {
return this.arguments;
}
public int inferenceKind() {
- return this.inferenceKind;
+ return (this.inferenceKind & InferenceContext18.INFERENCE_KIND_MASK);
}
public void setInferenceKind(int checkKind) {
this.inferenceKind = checkKind;
}
+public void markInferenceFinished() {
+ this.inferenceKind |= InferenceContext18.CHECK_FINISHED;
+}
+public boolean hasInferenceFinished() {
+ return this.inferenceKind == 0 // only relevant if inference has been started
+ || (this.inferenceKind & InferenceContext18.CHECK_FINISHED) != 0;
+}
public TypeBinding updateBindings(MethodBinding updatedBinding) {
this.binding = updatedBinding;
return this.resolvedType = updatedBinding.declaringClass;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
index a66fee3ee5..a159f74459 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
@@ -423,7 +423,7 @@ public class ExplicitConstructorCall extends Statement implements Invocation, Ex
if (receiverType == null) {
return;
}
- this.binding = findConstructorBinding(scope, this, receiverType, this.arguments, argumentTypes, polyExpressionSeen);
+ this.binding = findConstructorBinding(scope, this, receiverType, argumentTypes, polyExpressionSeen);
if (this.binding.isValidBinding()) {
if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
@@ -498,11 +498,18 @@ public class ExplicitConstructorCall extends Statement implements Invocation, Ex
return this.inferenceContext;
}
public int inferenceKind() {
- return this.inferenceKind;
+ return (this.inferenceKind & InferenceContext18.INFERENCE_KIND_MASK);
}
public void setInferenceKind(int checkKind) {
this.inferenceKind = checkKind;
}
+ public void markInferenceFinished() {
+ this.inferenceKind |= InferenceContext18.CHECK_FINISHED;
+ }
+ public boolean hasInferenceFinished() {
+ return this.inferenceKind == 0 // only relevant if inference has been started
+ || (this.inferenceKind & InferenceContext18.CHECK_FINISHED) != 0;
+ }
public TypeBinding updateBindings(MethodBinding updatedBinding) {
this.binding = updatedBinding;
return TypeBinding.VOID; // not an expression
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
index c16f25fbb1..59c40f5ede 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
@@ -1053,6 +1053,25 @@ public TypeBinding resolveTypeExpecting(BlockScope scope, TypeBinding expectedTy
}
return expressionType;
}
+
+/**
+ * If we might still be in the context of an unfinished outer inference, use this method to
+ * tentatively resolve this expression without leaving any undesired traces, in case we will
+ * come back with a better target type later.
+ */
+public TypeBinding resolveTentatively(BlockScope scope, TypeBinding targetType) {
+ return resolveType(scope); // default is to do full resolution in just this one step
+}
+
+/**
+ * Once outer contexts have finalized the target type for this expression,
+ * perform any checks that might have been delayed previously.
+ * @param targetType the final target type (aka expectedType) for this expression.
+ */
+public void checkAgainstFinalTargetType(TypeBinding targetType) {
+ // nop, subclasses may choose to do real stuff here
+}
+
/**
* Returns true if the receiver is forced to be of raw type either to satisfy the contract imposed
* by a super type or because it *is* raw and the current type has no control over it (i.e the rawness
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
index 08b9a39556..37c97fcefb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
@@ -35,9 +35,10 @@ import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBindingVisitor;
-public abstract class FunctionalExpression extends Expression {
+public abstract class FunctionalExpression extends Expression implements PolyExpression {
TypeBinding expectedType;
+ boolean expectedIsFinalTargetType = false; // flip to true once the expression context has determined the final target type for this expression
public MethodBinding descriptor;
public MethodBinding binding; // Code generation binding. May include synthetics. See getMethodBinding()
protected MethodBinding actualMethodBinding; // void of synthetics.
@@ -49,7 +50,8 @@ public abstract class FunctionalExpression extends Expression {
public BlockScope enclosingScope;
protected boolean ellipsisArgument;
protected static IErrorHandlingPolicy silentErrorHandlingPolicy = DefaultErrorHandlingPolicies.ignoreAllProblems();
-
+ protected boolean hasInferenceFinished = false;
+
public FunctionalExpression(CompilationResult compilationResult) {
this.compilationResult = compilationResult;
}
@@ -83,6 +85,14 @@ public abstract class FunctionalExpression extends Expression {
public boolean isPolyExpression() {
return true; // always as per introduction of part D, JSR 335
}
+
+ public void markInferenceFinished() {
+ this.hasInferenceFinished = true;
+ }
+
+ public boolean hasInferenceFinished() {
+ return this.hasInferenceFinished;
+ }
public TypeBinding invocationTargetType() {
if (this.expectedType == null) return null;
@@ -135,19 +145,53 @@ public abstract class FunctionalExpression extends Expression {
/** During inference: Try to find an applicable method binding without causing undesired side-effects. */
public MethodBinding findCompileTimeMethodTargeting(TypeBinding targetType, Scope scope) {
- setExpectedType(targetType);
+ if (this.hasInferenceFinished)
+ return this.binding;
+ return (MethodBinding)internalResolveTentatively(targetType, scope)[0];
+ }
+
+ /** During inference: Try to resolve the type of this expression without causing undesired side-effects. */
+ public TypeBinding resolveTentatively(BlockScope scope, TypeBinding targetType) {
+ if (this.hasInferenceFinished)
+ return this.resolvedType;
+ return (TypeBinding)internalResolveTentatively(targetType, scope)[1];
+ }
+
+ private Object[] internalResolveTentatively(TypeBinding targetType, Scope scope) { // TODO: convert return to Pair<MethodBinding,TypeBinding>
+ // FIXME: could enclosingScope still be null here??
IErrorHandlingPolicy oldPolicy = this.enclosingScope.problemReporter().switchErrorHandlingPolicy(silentErrorHandlingPolicy);
+ ExpressionContext previousContext = this.expressionContext;
+ MethodBinding previousBinding = this.binding;
+ MethodBinding previousDescriptor = this.descriptor;
try {
+ setExpressionContext(INVOCATION_CONTEXT);
+ setExpectedType(targetType);
this.binding = null;
- resolveType(this.enclosingScope);
- return this.binding;
+ TypeBinding type = resolveType(this.enclosingScope);
+ return new Object[] { this.binding, type };
} finally {
this.enclosingScope.problemReporter().switchErrorHandlingPolicy(oldPolicy);
- this.binding = null;
- setExpectedType(null);
+ // remove *any relevant* traces of this 'inofficial' resolving:
+ this.binding = previousBinding;
+ this.descriptor = previousDescriptor;
+ this.hasInferenceFinished = false;
+ setExpressionContext(previousContext);
+ this.expectedType = null; // don't call setExpectedType(null), would NPE
+ cleanUpAfterTentativeResolve();
}
}
+ void cleanUpAfterTentativeResolve() {
+ // nop. Subclasses: do your homework!
+ }
+
+ public void checkAgainstFinalTargetType(TypeBinding targetType) {
+ if (this.expectedIsFinalTargetType)
+ return; // already checked
+ this.expectedIsFinalTargetType = true;
+ resolveTypeExpecting(this.enclosingScope, targetType);
+ }
+
class VisibilityInspector extends TypeBindingVisitor {
private Scope scope;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java
index bd0b8503d9..2dec011207 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java
@@ -26,7 +26,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
* <li>Invocation Type Inference (18.5.2).</li>
* </ul>
*/
-public interface Invocation extends InvocationSite {
+public interface Invocation extends InvocationSite, PolyExpression {
Expression[] arguments();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index a743b6959e..b43406cc87 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -330,6 +330,26 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
return this.resolvedType;
}
+ void cleanUpAfterTentativeResolve() {
+ // remove traces of attempts to resolve the argument before we have the final target type:
+ if (this.arguments != null)
+ for (int i = 0; i < this.arguments.length; i++)
+ this.arguments[i].binding = null;
+ // also remove traces of references to this argument
+ if (this.body != null)
+ this.body.traverse(new ASTVisitor() {
+ public boolean visit(SingleNameReference singleNameReference, BlockScope blockScope) {
+ if (singleNameReference.binding instanceof LocalVariableBinding) {
+ singleNameReference.bits &= ~ASTNode.RestrictiveFlagMASK;
+ singleNameReference.bits |= Binding.VARIABLE;
+ singleNameReference.actualReceiverType = null;
+ singleNameReference.binding = null;
+ }
+ return true;
+ }
+ }, this.scope);
+ }
+
public boolean argumentsTypeElided() {
return this.arguments.length > 0 && this.arguments[0].hasElidedType();
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 23fe301e39..6bc7fa0cc5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -846,7 +846,7 @@ protected void findMethodBinding(BlockScope scope, TypeBinding[] argumentTypes,
: scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this);
if (polyExpressionSeen)
- if (resolvePolyExpressionArguments(scope, this.binding, this.arguments, argumentTypes)) {
+ if (resolvePolyExpressionArguments(this, scope, this.binding, argumentTypes)) {
this.binding = this.receiver.isImplicitThis()
? scope.getImplicitMethod(this.selector, argumentTypes, this)
: scope.getMethod(this.actualReceiverType, this.selector, argumentTypes, this);
@@ -968,7 +968,14 @@ public void setInferenceKind(int checkKind) {
this.inferenceKind = checkKind;
}
public int inferenceKind() {
- return this.inferenceKind;
+ return (this.inferenceKind & InferenceContext18.INFERENCE_KIND_MASK);
+}
+public void markInferenceFinished() {
+ this.inferenceKind |= InferenceContext18.CHECK_FINISHED;
+}
+public boolean hasInferenceFinished() {
+ return this.inferenceKind == 0 // only relevant if inference has been started
+ || (this.inferenceKind & InferenceContext18.CHECK_FINISHED) != 0;
}
public TypeBinding updateBindings(MethodBinding updatedBinding) {
this.binding = updatedBinding;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/PolyExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/PolyExpression.java
new file mode 100644
index 0000000000..6b741df020
--- /dev/null
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/PolyExpression.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2013 GK Software AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+ * Contributors:
+ * Stephan Herrmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.ast;
+
+/**
+ * Abstraction of AST nodes that can be poly expressions and participate in type inference.
+ * (not included: ConditionalExpression).
+ */
+public interface PolyExpression {
+
+ /** Mark that inference for this expression has finished. */
+ void markInferenceFinished();
+
+ /** Answer whether type inference for this expression has finished (if needed). */
+ boolean hasInferenceFinished();
+}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
index fd36b1b44b..3301a0e063 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
@@ -476,7 +476,7 @@ public class QualifiedAllocationExpression extends AllocationExpression {
receiverType = this.type.resolvedType = scope.environment().createParameterizedType(((ParameterizedTypeBinding) receiverType).genericType(), inferredTypes, ((ParameterizedTypeBinding) receiverType).enclosingType());
}
ReferenceBinding allocationType = (ReferenceBinding) receiverType;
- this.binding = findConstructorBinding(scope, this, allocationType, this.arguments, argumentTypes, polyExpressionSeen);
+ this.binding = findConstructorBinding(scope, this, allocationType, argumentTypes, polyExpressionSeen);
if (this.binding.isValidBinding()) {
if (isMethodUseDeprecated(this.binding, scope, true)) {
@@ -544,7 +544,7 @@ public class QualifiedAllocationExpression extends AllocationExpression {
}
MethodBinding inheritedBinding = scope.getConstructor(anonymousSuperclass, argumentTypes, this);
if (polyExpressionSeen)
- resolvePolyExpressionArguments(scope, inheritedBinding, this.arguments, argumentTypes);
+ resolvePolyExpressionArguments(this, scope, inheritedBinding, argumentTypes);
if (!inheritedBinding.isValidBinding()) {
if (inheritedBinding.declaringClass == null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index 835213c612..dfec42dc96 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -519,6 +519,17 @@ public class ReferenceExpression extends FunctionalExpression implements Invocat
return this.resolvedType; // Phew !
}
+ public MethodBinding findCompileTimeMethodTargeting(TypeBinding targetType, Scope scope) {
+ if (this.exactMethodBinding != null) {
+ // TODO: shouldn't extactMethodBinding already be parameterized?
+ if (this.exactMethodBinding.typeVariables != Binding.NO_TYPE_VARIABLES && this.resolvedTypeArguments != null) {
+ return scope.environment().createParameterizedGenericMethod(this.exactMethodBinding, this.resolvedTypeArguments);
+ }
+ return this.exactMethodBinding;
+ }
+ return super.findCompileTimeMethodTargeting(targetType, scope);
+ }
+
public boolean isConstructorReference() {
return CharOperation.equals(this.selector, ConstantPool.Init);
}
@@ -610,6 +621,8 @@ public class ReferenceExpression extends FunctionalExpression implements Invocat
}
public boolean isCompatibleWith(TypeBinding left, Scope scope) {
+ if (this.hasInferenceFinished)
+ return this.resolvedType.isCompatibleWith(left, scope);
// 15.28.2
final MethodBinding sam = left.getSingleAbstractMethod(this.enclosingScope);
if (sam == null || !sam.isValidBinding())
@@ -624,6 +637,7 @@ public class ReferenceExpression extends FunctionalExpression implements Invocat
this.enclosingScope.problemReporter().switchErrorHandlingPolicy(oldPolicy);
isCompatible = this.binding != null && this.binding.isValidBinding();
this.binding = null;
+ this.hasInferenceFinished = false;
setExpectedType(null);
}
return isCompatible;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
index 423e020f71..995b68b7a8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
@@ -334,10 +334,10 @@ public ExpressionContext getExpressionContext() {
* after applicability checking (18.5.1) to include more information into the final
* invocation type inference (18.5.2).
*/
-protected MethodBinding findConstructorBinding(BlockScope scope, InvocationSite site, ReferenceBinding receiverType, Expression[] args, TypeBinding[] argumentTypes, boolean polyExpressionSeen) {
+protected MethodBinding findConstructorBinding(BlockScope scope, Invocation site, ReferenceBinding receiverType, TypeBinding[] argumentTypes, boolean polyExpressionSeen) {
MethodBinding ctorBinding = scope.getConstructor(receiverType, argumentTypes, site);
if (polyExpressionSeen) {
- if (resolvePolyExpressionArguments(scope, ctorBinding, args, argumentTypes))
+ if (resolvePolyExpressionArguments(site, scope, ctorBinding, argumentTypes))
return scope.getConstructor(receiverType, argumentTypes, site);
}
return ctorBinding;
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 c293e81f98..b48a3207f5 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
@@ -152,9 +152,8 @@ class ConstraintExpressionFormula extends ConstraintFormula {
MethodBinding functionType = t.getSingleAbstractMethod(inferenceContext.scope);
if (functionType == null)
return FALSE;
- // TODO: check strategy for: potentially-applicable method for the method reference when targeting T (15.28.1),
- reference.resolveTypeExpecting(reference.enclosingScope, t);
- MethodBinding potentiallyApplicable = reference.binding;
+ // potentially-applicable method for the method reference when targeting T (15.28.1),
+ MethodBinding potentiallyApplicable = reference.findCompileTimeMethodTargeting(t, inferenceContext.scope);
if (potentiallyApplicable == null)
return FALSE;
if (reference.isExactMethodReference()) {
@@ -165,7 +164,7 @@ class ConstraintExpressionFormula extends ConstraintFormula {
int k = pPrime.length;
int offset = 0;
if (n == k+1) {
- newConstraints.add(new ConstraintTypeFormula(p[0], reference.receiverType, COMPATIBLE)); // 2nd arg: "ReferenceType"
+ newConstraints.add(new ConstraintTypeFormula(p[0], reference.lhs.resolvedType, COMPATIBLE));
offset = 1;
}
for (int i = offset; i < n; i++)
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 cfd8703589..0b6c6804e8 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
@@ -49,10 +49,13 @@ public class InferenceContext18 {
InvocationSite currentInvocation;
Expression[] invocationArguments;
- // TODO make me an enum:
+ // Bitmask, lower two bits:
public static final int CHECK_STRICT = 1;
public static final int CHECK_LOOSE = 2;
public static final int CHECK_VARARG = 3;
+ public static final int INFERENCE_KIND_MASK = 3;
+ // bit 3:
+ public static final int CHECK_FINISHED = 4;
static class InvocationRecord {
InvocationSite site;
@@ -609,25 +612,43 @@ public class InferenceContext18 {
this.invocationArguments = record.invocationArguments;
}
- public void rebindInnerPolies(BoundSet bounds, TypeBinding[] arguments) {
+ /**
+ * After inference has finished, iterate all inner poly expressions, that have been
+ * included in the inference. For each of these update some type information
+ * from the inference result and perhaps trigger follow-up resolving as needed.
+ */
+ public void rebindInnerPolies(BoundSet bounds, TypeBinding[] argumentTypes) {
int len = this.innerPolies.size();
for (int i = 0; i < len; i++) {
Expression inner = (Expression) this.innerPolies.get(i);
if (inner instanceof Invocation) {
Invocation innerMessage = (Invocation) inner;
MethodBinding original = innerMessage.binding().original();
+
+ // apply inference results onto the binding of the inner invocation:
TypeBinding[] solutions = getSolutions(original.typeVariables(), innerMessage, bounds);
- if (solutions == null) continue; // play safe, but shouldn't happen in a resolved context
- TypeBinding returnType = innerMessage.updateBindings(this.environment.createParameterizedGenericMethod(original, solutions));
- if (returnType != TypeBinding.VOID) {
- for (int j = 0; j < this.invocationArguments.length; j++) {
- if (inner == this.invocationArguments[j]) {
- arguments[j] = returnType;
- break;
- }
+ if (solutions == null)
+ continue; // play safe, but shouldn't happen in a resolved context
+ ParameterizedGenericMethodBinding innerBinding = this.environment.createParameterizedGenericMethod(original, solutions);
+ innerMessage.updateBindings(innerBinding);
+ innerMessage.markInferenceFinished(); // invocation type inference has already happened on the inner, too.
+
+ // finalize resolving of arguments of the inner invocation:
+ TypeBinding[] innerParameters = innerBinding.parameters;
+ int inferenceKind = innerMessage.inferenceKind();
+ TypeBinding varArgsType = inferenceKind == CHECK_VARARG ? ((ArrayBinding)innerParameters[innerParameters.length-1]).elementsType() : null;
+ Expression[] arguments = innerMessage.arguments();
+ if (arguments != null) {
+ for (int j = 0; j < arguments.length; j++) {
+ TypeBinding param = (varArgsType == null || (j < innerParameters.length-1))
+ ? innerParameters[j]
+ : varArgsType;
+ arguments[j].checkAgainstFinalTargetType(param);
}
}
}
+ // inner FunctionalExpression don't seem to be included in inference.
+ // TODO recheck any inquires on those actually involve inference of which the results are included here.
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
index 3ea33b53f8..e53346ed62 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
@@ -22,6 +22,7 @@ package org.eclipse.jdt.internal.compiler.lookup;
import org.eclipse.jdt.internal.compiler.ast.ExpressionContext;
import org.eclipse.jdt.internal.compiler.ast.Invocation;
+import org.eclipse.jdt.internal.compiler.ast.PolyExpression;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -88,10 +89,8 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
boolean invocationTypeInferred = false;
if (expectedType != null || invocationSite.getExpressionContext() == ExpressionContext.VANILLA_CONTEXT) {
result = infCtx18.inferInvocationType(result, expectedType, invocationSite, originalMethod, checkKind);
- if (result != null)
- invocationTypeInferred = true;
- else
- hasReturnProblem = true;
+ invocationTypeInferred = true;
+ hasReturnProblem |= result == null;
if (hasReturnProblem)
result = provisionalResult; // let's prefer a type error regarding the return type over reporting no match at all
} else {
@@ -110,10 +109,15 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
problemMethod.returnType = invocationSite.invocationTargetType();
return problemMethod;
}
- if (invocationSite instanceof Invocation)
- ((Invocation)invocationSite).setInferenceKind(checkKind);
- if (invocationTypeInferred)
+ if (invocationSite instanceof Invocation) {
+ Invocation invocation = (Invocation)invocationSite;
+ invocation.setInferenceKind(checkKind);
+ }
+ if (invocationTypeInferred) {
+ if (invocationSite instanceof PolyExpression)
+ ((PolyExpression) invocationSite).markInferenceFinished();
infCtx18.rebindInnerPolies(result, arguments);
+ }
break computeSubstitutes;
}
}
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java
index 320d45b788..0f0b82b263 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetAllocationExpression.java
@@ -225,7 +225,7 @@ public TypeBinding resolveType(BlockScope scope) {
}
ReferenceBinding allocatedType = (ReferenceBinding) this.resolvedType;
- this.binding = findConstructorBinding(scope, this, allocatedType, this.arguments, argumentTypes, polyExpressionSeen);
+ this.binding = findConstructorBinding(scope, this, allocatedType, argumentTypes, polyExpressionSeen);
if (!this.binding.isValidBinding()) {
if (this.binding instanceof ProblemMethodBinding

Back to the top