new inference: process additional constraints one after another and use data from previous results (IDEA-124983)

This commit is contained in:
Anna Kozlova
2014-09-01 21:46:11 +04:00
parent 4d3d6e8a8c
commit e5afaa7a58
4 changed files with 120 additions and 39 deletions
@@ -267,11 +267,9 @@ public class InferenceSession {
boolean varargs, boolean toplevel) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(args[i], PsiCallExpression.class));
if (session == null) {
session = this;
}
PsiType parameterType = session.substituteWithInferenceVariables(getParameterType(parameters, i, siteSubstitutor, varargs));
final InferenceSession nestedCallSession = findNestedCallSession(args[i]);
final PsiType parameterType =
nestedCallSession.substituteWithInferenceVariables(getParameterType(parameters, i, siteSubstitutor, varargs));
if (!isPertinentToApplicability(args[i], parentMethod)) {
additionalConstraints.add(new ExpressionCompatibilityConstraint(args[i], parameterType));
}
@@ -881,45 +879,66 @@ public class InferenceSession {
//extract subset of constraints
final Set<ConstraintFormula> subset = buildSubset(additionalConstraints);
//collect all input variables of selection
//collect all input variables of selection
final Set<InferenceVariable> varsToResolve = new LinkedHashSet<InferenceVariable>();
for (ConstraintFormula formula : subset) {
if (formula instanceof InputOutputConstraintFormula) {
final Set<InferenceVariable> inputVariables = ((InputOutputConstraintFormula)formula).getInputVariables(this);
if (inputVariables != null) {
for (InferenceVariable inputVariable : inputVariables) {
varsToResolve.addAll(inputVariable.getDependencies(this));
}
varsToResolve.addAll(inputVariables);
}
collectVarsToResolve(varsToResolve, (InputOutputConstraintFormula)formula);
}
}
//resolve input variables
PsiSubstitutor substitutor = resolveSubset(varsToResolve, siteSubstitutor);
if (substitutor == null) {
for (ConstraintFormula formula : subset) {
if (!processOneConstraint(formula, siteSubstitutor, varsToResolve)) return false;
}
}
return true;
}
private void collectVarsToResolve(Set<InferenceVariable> varsToResolve, InputOutputConstraintFormula formula) {
final Set<InferenceVariable> inputVariables = formula.getInputVariables(this);
if (inputVariables != null) {
for (InferenceVariable inputVariable : inputVariables) {
varsToResolve.addAll(inputVariable.getDependencies(this));
}
varsToResolve.addAll(inputVariables);
}
}
private boolean processOneConstraint(ConstraintFormula formula, PsiSubstitutor siteSubstitutor, Set<InferenceVariable> varsToResolve) {
if (formula instanceof ExpressionCompatibilityConstraint) {
final PsiExpression expression = ((ExpressionCompatibilityConstraint)formula).getExpression();
final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(expression, PsiCallExpression.class, false);
if (callExpression != null) {
final InferenceSession session = myNestedSessions.get(callExpression);
if (session != null) {
formula.apply(session.myInferenceSubstitution, true);
collectVarsToResolve(varsToResolve, (InputOutputConstraintFormula)formula);
}
}
}
//resolve input variables
PsiSubstitutor substitutor = resolveSubset(varsToResolve, siteSubstitutor);
if (substitutor == null) {
return false;
}
if (myContext instanceof PsiCallExpression) {
PsiExpressionList argumentList = ((PsiCallExpression)myContext).getArgumentList();
LOG.assertTrue(argumentList != null);
MethodCandidateInfo.updateSubstitutor(argumentList, substitutor);
}
try {
formula.apply(substitutor, true);
myConstraints.add(formula);
if (!repeatInferencePhases(true)) {
return false;
}
if (myContext instanceof PsiCallExpression) {
PsiExpressionList argumentList = ((PsiCallExpression)myContext).getArgumentList();
LOG.assertTrue(argumentList != null);
MethodCandidateInfo.updateSubstitutor(argumentList, substitutor);
}
try {
for (ConstraintFormula additionalConstraint : subset) {
additionalConstraint.apply(substitutor, true);
}
myConstraints.addAll(subset);
if (!repeatInferencePhases(true)) {
return false;
}
}
finally {
LambdaUtil.ourFunctionTypes.set(null);
}
}
finally {
LambdaUtil.ourFunctionTypes.set(null);
}
return true;
}
@@ -1316,6 +1335,14 @@ public class InferenceSession {
return myInferenceSubstitution.substitute(type);
}
public InferenceSession findNestedCallSession(PsiExpression arg) {
InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(arg, PsiCallExpression.class));
if (session == null) {
session = this;
}
return session;
}
public PsiType startWithFreshVars(PsiType type) {
PsiSubstitutor s = PsiSubstitutor.EMPTY;
for (InferenceVariable variable : myInferenceVariables) {
@@ -3,6 +3,7 @@ package com.intellij.psi.impl.source.resolve.graphInference.constraints;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import java.util.List;
@@ -62,8 +63,9 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
if (returnExpressions.isEmpty() && !myExpression.isValueCompatible()) { //not value-compatible
return false;
}
returnType = session.substituteWithInferenceVariables(substitutor.substitute(returnType));
if (!session.isProperType(returnType)) {
InferenceSession callsession = session.findNestedCallSession(myExpression);
returnType = callsession.substituteWithInferenceVariables(substitutor.substitute(returnType));
if (!callsession.isProperType(returnType)) {
for (PsiExpression returnExpression : returnExpressions) {
constraints.add(new ExpressionCompatibilityConstraint(returnExpression, returnType));
}
@@ -0,0 +1,52 @@
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javafx.scene.Group;
import javafx.scene.shape.Rectangle;
abstract class NoFormalParams {
interface I<T> {
T a(int a);
}
<F> I<F> foo(I<F> i) { return null;}
{
I<Integer> i = foo(a -> foo(b -> 1)).a(0);
foo(a -> foo(b -> 1)).a(0);
}
}
abstract class NoFormalParamTypeInferenceNeeded {
interface I<T> {
T a(int a);
}
abstract <RR> RR map(I<RR> mapper);
abstract <R, V> R zip(Function<V, R> zipper);
{
map(a -> zip(text -> text));
zip(a -> zip(text -> text));
Integer zip = zip(<error descr="Cyclic inference">a -> zip(text -> text)</error>);
}
}
class IDEA124983 {
private final Group gridGroup = new Group();
void createGrid() {
IntStream.range(0, 4)
.mapToObj(i -> IntStream.range(0, 4).mapToObj(j -> {
Rectangle rect2 = new Rectangle(i * 64, j * 64, 64, 64);
return rect2;
}))
.flatMap(s -> s)
.forEach(gridGroup.getChildren()::add);
}
void simplified(final IntStream range) {
range.mapToObj(i -> range.mapToObj(j -> 1)).flatMap(s -> s);
}
}
@@ -76,7 +76,6 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testIDEA122616() { doTest(); }
public void testIDEA122700() { doTest(); }
public void testIDEA122406() { doTest(); }
@Bombed(day = 30, month = Calendar.SEPTEMBER)
public void testNestedCallsInsideLambdaReturnExpression() { doTest(); }
@Bombed(day = 30, month = Calendar.SEPTEMBER)
public void testIDEA123731() { doTest(); }
@@ -95,6 +94,7 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testIDEA126809() { doTest(); }
public void testIDEA124424() { doTest(); }
public void testNestedLambdaExpressions1() { doTest(); }
public void testNestedLambdaExpressionsNoFormalParams() { doTest(); }
public void testIDEA127596() throws Exception {
doTest();