Bug 509324: [1.8][inference] jdt generics regression in neon.2
Change-Id: Iab0852112ebb3880725ba76b64f28691d4e2424f
Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de>
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 7583065..7907c77 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2016 GK Software AG, and others.
+ * Copyright (c) 2013, 2017 GK Software AG, and others.
* 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
@@ -7349,4 +7349,54 @@
"}\n"
});
}
+ public void testBug509324() {
+ runConformTest(
+ new String[] {
+ "testgenerics/TestGenerics.java",
+ "package testgenerics;\n" +
+ "\n" +
+ "import java.time.Duration;\n" +
+ "import java.util.function.Function;\n" +
+ "import java.util.function.Supplier;\n" +
+ "\n" +
+ "interface Publisher<T> {}\n" +
+ "\n" +
+ "abstract class Mono<T> implements Publisher<T> {\n" +
+ " public static <T> Mono<T> just(T data) { return null; }\n" +
+ " public static <T> Mono<T> empty() { return null; }\n" +
+ " public final <R> Mono<R> then(Function<? super T, ? extends Mono<? extends R>> transformer) {\n" +
+ " return null;\n" +
+ " }\n" +
+ " public T block() { return null; }\n" +
+ " public final T block(Duration timeout) { return null; }\n" +
+ "}\n" +
+ "class Info {\n" +
+ " public String getApplicationSshEndpoint() { return null; }\n" +
+ "}\n" +
+ "class SshHost {\n" +
+ " public SshHost(String host, int port, String fingerPrint) { }\n" +
+ "}\n" +
+ "\n" +
+ "public class TestGenerics {\n" +
+ "\n" +
+ " private Mono<Info> info = Mono.just(new Info());\n" +
+ "\n" +
+ " public static <T> T ru_get(Mono<T> mono) throws Exception {\n" +
+ " return mono.block();\n" +
+ " }\n" +
+ "\n" +
+ " public SshHost getSshHost() throws Exception {\n" +
+ " return ru_get(\n" +
+ " info.then((i) -> {\n" +
+ " String host = i.getApplicationSshEndpoint();\n" +
+ " if (host!=null) {\n" +
+ " return Mono.just(new SshHost(host, 0, host));\n" +
+ " }\n" +
+ " return Mono.empty();\n" +
+ " })\n" +
+ " );\n" +
+ " }\n" +
+ "}\n"
+ });
+ }
}
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 1998a94..8454e95 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2016 GK Software AG, and others.
+ * Copyright (c) 2013, 2017 GK Software AG, and others.
* 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
@@ -403,7 +403,7 @@
}
if (SHOULD_WORKAROUND_BUG_JDK_8153748) { // "before 18.5.2", but should not spill into b3 ... (heuristically)
- ReductionResult jdk8153748result = addJDK_8153748ConstraintsFromInvocation(this.invocationArguments, method);
+ ReductionResult jdk8153748result = addJDK_8153748ConstraintsFromInvocation(this.invocationArguments, method, new InferenceSubstitution(this));
if (jdk8153748result != null) {
this.currentBounds.incorporate(this);
}
@@ -521,15 +521,17 @@
}
// ---
- private ReductionResult addJDK_8153748ConstraintsFromInvocation(Expression[] arguments, MethodBinding method) throws InferenceFailureException {
+ private ReductionResult addJDK_8153748ConstraintsFromInvocation(Expression[] arguments, MethodBinding method, InferenceSubstitution substitution)
+ throws InferenceFailureException
+ {
// not per JLS, trying to mimic javac behavior
boolean constraintAdded = false;
if (arguments != null) {
for (int i = 0; i < arguments.length; i++) {
Expression argument = arguments[i];
TypeBinding parameter = getParameter(method.parameters, i, method.isVarargs());
- parameter = this.substitute(parameter);
- ReductionResult result = addJDK_8153748ConstraintsFromExpression(argument, parameter, method);
+ parameter = substitution.substitute(substitution, parameter);
+ ReductionResult result = addJDK_8153748ConstraintsFromExpression(argument, parameter, method, substitution);
if (result == ReductionResult.FALSE)
return ReductionResult.FALSE;
if (result == ReductionResult.TRUE)
@@ -539,7 +541,10 @@
return constraintAdded ? ReductionResult.TRUE : null;
}
- private ReductionResult addJDK_8153748ConstraintsFromExpression(Expression argument, TypeBinding parameter, MethodBinding method) throws InferenceFailureException {
+ private ReductionResult addJDK_8153748ConstraintsFromExpression(Expression argument, TypeBinding parameter, MethodBinding method,
+ InferenceSubstitution substitution)
+ throws InferenceFailureException
+ {
if (argument instanceof FunctionalExpression) {
return addJDK_8153748ConstraintsFromFunctionalExpr((FunctionalExpression) argument, parameter, method);
} else if (argument instanceof Invocation && argument.isPolyExpression(method)) {
@@ -547,13 +552,14 @@
Expression[] innerArgs = invocation.arguments();
MethodBinding innerMethod = invocation.binding();
if (innerMethod != null && innerMethod.isValidBinding()) {
- return addJDK_8153748ConstraintsFromInvocation(innerArgs, innerMethod.original());
+ substitution = enrichSubstitution(substitution, invocation, innerMethod);
+ return addJDK_8153748ConstraintsFromInvocation(innerArgs, innerMethod.original(), substitution);
}
} else if (argument instanceof ConditionalExpression) {
ConditionalExpression ce = (ConditionalExpression) argument;
- if (addJDK_8153748ConstraintsFromExpression(ce.valueIfTrue, parameter, method) == ReductionResult.FALSE)
+ if (addJDK_8153748ConstraintsFromExpression(ce.valueIfTrue, parameter, method, substitution) == ReductionResult.FALSE)
return ReductionResult.FALSE;
- return addJDK_8153748ConstraintsFromExpression(ce.valueIfFalse, parameter, method);
+ return addJDK_8153748ConstraintsFromExpression(ce.valueIfFalse, parameter, method, substitution);
}
return null;
}
@@ -587,6 +593,15 @@
}
return null;
}
+
+ InferenceSubstitution enrichSubstitution(InferenceSubstitution substitution, Invocation innerInvocation, MethodBinding innerMethod) {
+ if (innerMethod instanceof ParameterizedGenericMethodBinding) {
+ InferenceContext18 innerContext = innerInvocation.getInferenceContext((ParameterizedMethodBinding) innerMethod);
+ if (innerContext != null)
+ return substitution.addContext(innerContext);
+ }
+ return substitution;
+ }
private boolean addConstraintsToC(Expression[] exprs, Set<ConstraintFormula> c, MethodBinding method, int inferenceKindForMethod, InvocationSite site)
throws InferenceFailureException
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
index d482c7a..fa208b6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2015 GK Software AG.
+ * Copyright (c) 2013, 2017 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
@@ -17,18 +17,58 @@
private LookupEnvironment environment;
private InferenceVariable[] variables;
- private InvocationSite site;
+ private InvocationSite[] sites;
public InferenceSubstitution(LookupEnvironment environment, InferenceVariable[] variables, InvocationSite site) {
this.environment = environment;
this.variables = variables;
- this.site = site;
+ this.sites = new InvocationSite[] {site};
}
public InferenceSubstitution(InferenceContext18 context) {
this(context.environment, context.inferenceVariables, context.currentInvocation);
}
+ /** Answer a substitution that is able to substitute into inference variables of several inference contexts (outer & inner) */
+ public InferenceSubstitution addContext(InferenceContext18 otherContext) {
+ InferenceSubstitution subst = new InferenceSubstitution(this.environment, null, null) {
+
+ protected boolean isSameParameter(TypeBinding p1, TypeBinding originalType) {
+ if (TypeBinding.equalsEquals(p1, originalType))
+ return true;
+ if (p1 instanceof TypeVariableBinding && originalType instanceof TypeVariableBinding) {
+ // may need to 'normalize' if inner & outer have different degree of parameterization / original:
+ TypeVariableBinding var1= (TypeVariableBinding) p1, var2 = (TypeVariableBinding) originalType;
+ Binding declaring1 = var1.declaringElement;
+ Binding declaring2 = var2.declaringElement;
+ if (declaring1 instanceof MethodBinding && declaring2 instanceof MethodBinding) {
+ declaring1 = ((MethodBinding) declaring1).original();
+ declaring2 = ((MethodBinding) declaring2).original();
+ }
+ // TODO: handle TypeBinding if needed
+ return declaring1 == declaring2 && var1.rank == var2.rank;
+ }
+ return false;
+ }
+ };
+
+ int l1 = this.sites.length;
+ subst.sites = new InvocationSite[l1+1];
+ System.arraycopy(this.sites, 0, subst.sites, 0, l1);
+ subst.sites[l1] = otherContext.currentInvocation;
+
+ subst.variables = this.variables;
+
+// TODO: switch to also combining variables, if needed (filter duplicates?):
+// l1 = this.variables.length;
+// int l2 = otherContext.inferenceVariables.length;
+// subst.variables = new InferenceVariable[l1+l2];
+// System.arraycopy(this.variables, 0, subst.variables, 0, l1);
+// System.arraycopy(otherContext.inferenceVariables, 0, subst.variables, l1, l2);
+
+ return subst;
+ }
+
/**
* Override method {@link Scope.Substitutor#substitute(Substitution, TypeBinding)},
* to add substitution of types other than type variables.
@@ -36,7 +76,7 @@
public TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
for (int i = 0; i < this.variables.length; i++) {
InferenceVariable variable = this.variables[i];
- if (InferenceContext18.isSameSite(this.site, variable.site) && TypeBinding.equalsEquals(getP(i), originalType)) {
+ if (isInSites(variable.site) && isSameParameter(getP(i), originalType)) {
if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled && originalType.hasNullTypeAnnotations())
return this.environment.createAnnotatedType(variable.withoutToplevelNullAnnotation(), originalType.getTypeAnnotations());
return variable;
@@ -46,6 +86,17 @@
return super.substitute(substitution, originalType);
}
+ private boolean isInSites(InvocationSite otherSite) {
+ for (int i = 0; i < this.sites.length; i++)
+ if (InferenceContext18.isSameSite(this.sites[i], otherSite))
+ return true;
+ return false;
+ }
+
+ protected boolean isSameParameter(TypeBinding p1, TypeBinding originalType) {
+ return TypeBinding.equalsEquals(p1, originalType);
+ }
+
/**
* Get the type corresponding to the ith inference variable.
* Default behavior is to answer the inference variable's type parameter.