new inference: wildcard parameterization inference

This commit is contained in:
Anna Kozlova
2014-02-06 19:34:13 +01:00
parent dd1c5d9cee
commit 865a535fa7
6 changed files with 50 additions and 42 deletions
@@ -47,12 +47,12 @@ public class FunctionalInterfaceParameterizationUtil {
}
@Nullable
public static PsiType getGroundTargetType(@Nullable PsiType psiClassType, PsiLambdaExpression expr, boolean resolve) {
public static PsiType getGroundTargetType(@Nullable PsiType psiClassType, PsiLambdaExpression expr) {
if (!isWildcardParameterized(psiClassType)) {
return psiClassType;
}
if (expr.hasFormalParameterTypes()) return getFunctionalTypeExplicit(psiClassType, expr, resolve);
if (expr.hasFormalParameterTypes()) return getFunctionalTypeExplicit(psiClassType, expr);
return getFunctionalTypeImplicit(psiClassType);
}
@@ -90,11 +90,11 @@ public class FunctionalInterfaceParameterizationUtil {
return psiClassType;
}
private static PsiType getFunctionalTypeExplicit(PsiType psiClassType, PsiLambdaExpression expr, boolean resolve) {
private static PsiType getFunctionalTypeExplicit(PsiType psiClassType, PsiLambdaExpression expr) {
final PsiParameter[] lambdaParams = expr.getParameterList().getParameters();
if (psiClassType instanceof PsiIntersectionType) {
for (PsiType psiType : ((PsiIntersectionType)psiClassType).getConjuncts()) {
final PsiType functionalType = getFunctionalTypeExplicit(psiType, expr, false);
final PsiType functionalType = getFunctionalTypeExplicit(psiType, expr);
if (functionalType != null) return functionalType;
}
return null;
@@ -110,28 +110,46 @@ public class FunctionalInterfaceParameterizationUtil {
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) return null;
final InferenceSession session = new InferenceSession(PsiSubstitutor.EMPTY, expr);
PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
if (typeParameters.length != parameters.length) {
return null;
}
for (int i = 0; i < typeParameters.length; i++) {
session.addVariable(typeParameters[i], parameters[i]);
}
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
final PsiParameter[] targetMethodParams = interfaceMethod.getParameterList().getParameters();
if (targetMethodParams.length != lambdaParams.length) {
return null;
}
final InferenceSession session = new InferenceSession(typeParameters, PsiSubstitutor.EMPTY, expr.getManager(), expr);
for (int i = 0; i < targetMethodParams.length; i++) {
if (resolve) {
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), targetMethodParams[i].getType()));
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), targetMethodParams[i].getType()));
}
if (!session.repeatInferencePhases(false)) {
return null;
}
final PsiSubstitutor substitutor = session.resolveBounds(true);
final PsiType[] newTypeParameters = new PsiType[parameters.length];
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter typeParameter = typeParameters[i];
final InferenceVariable variable = session.getInferenceVariable(typeParameter);
final PsiType instantiation = variable.getInstantiation();
if (instantiation != PsiType.NULL) {
newTypeParameters[i] = instantiation;
} else {
newTypeParameters[i] = parameters[i];
}
}
final PsiClassType parameterization = elementFactory.createType(psiClass, session.infer());
final PsiClassType parameterization = elementFactory.createType(psiClass, newTypeParameters);
if (//todo !TypeConversionUtil.isAssignable(psiClassType, parameterization) ||
!GenericsUtil.isTypeArgumentsApplicable(typeParameters, PsiSubstitutor.EMPTY.putAll(psiClass, newTypeParameters), expr)) {
return null;
}
if (!TypeConversionUtil.containsWildcards(parameterization)) return parameterization;
}
return null;
@@ -60,11 +60,6 @@ public class InferenceSession {
private final PsiElement myContext;
public InferenceSession(PsiSubstitutor siteSubstitutor, PsiElement expr) {
mySiteSubstitutor = siteSubstitutor;
myContext = expr;
}
public InferenceSession(PsiTypeParameter[] typeParams,
PsiType[] leftTypes,
PsiType[] rightTypes,
@@ -205,6 +200,7 @@ public class InferenceSession {
@Nullable PsiElement parent,
ParameterTypeInferencePolicy policy) {
boolean doesNotContainFalseBound = repeatInferencePhases(parameters == null);
// if (!doesNotContainFalseBound) return prepareSubstitution();
resolveBounds(myInferenceVariables.values(), mySiteSubstitutor, false);
@@ -215,6 +211,7 @@ public class InferenceSession {
inferenceVariable.ignoreInstantiation();
}
doesNotContainFalseBound = repeatInferencePhases(true);
// if (!doesNotContainFalseBound) return prepareSubstitution();
PsiSubstitutor substitutor = resolveBounds(myInferenceVariables.values(), mySiteSubstitutor, !policy.allowPostponeInference());
LOG.assertTrue(parent != null);
@@ -242,6 +239,7 @@ public class InferenceSession {
inferenceVariable.ignoreInstantiation();
}
doesNotContainFalseBound = proceedWithAdditionalConstraints(additionalConstraints);
// if (!doesNotContainFalseBound) return prepareSubstitution();
}
}
@@ -250,6 +248,10 @@ public class InferenceSession {
}
mySiteSubstitutor = resolveBounds(myInferenceVariables.values(), mySiteSubstitutor, !policy.allowPostponeInference());
return prepareSubstitution();
}
private PsiSubstitutor prepareSubstitution() {
for (InferenceVariable inferenceVariable : myInferenceVariables.values()) {
final PsiTypeParameter typeParameter = inferenceVariable.getParameter();
PsiType instantiation = inferenceVariable.getInstantiation();
@@ -572,7 +574,7 @@ public class InferenceSession {
return dependencies != null ? !dependencies.isEmpty() : isProper;
}
private boolean repeatInferencePhases(boolean incorporate) {
protected boolean repeatInferencePhases(boolean incorporate) {
do {
if (!reduceConstraints()) {
//inference error occurred
@@ -603,7 +605,13 @@ public class InferenceSession {
return true;
}
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables, PsiSubstitutor substitutor, boolean acceptInitialUpperBound) {
protected PsiSubstitutor resolveBounds(boolean acceptInitialUpperBound) {
return resolveBounds(getInferenceVariables(), mySiteSubstitutor, acceptInitialUpperBound);
}
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables,
PsiSubstitutor substitutor,
boolean acceptInitialUpperBound) {
final List<List<InferenceVariable>> independentVars = InferenceVariablesOrder.resolveOrder(inferenceVariables, this);
for (List<InferenceVariable> variables : independentVars) {
for (InferenceVariable inferenceVariable : variables) {
@@ -734,21 +742,6 @@ public class InferenceSession {
return myInferenceVariables.keySet();
}
public void addVariable(PsiTypeParameter typeParameter, final PsiType parameter) {
InferenceVariable variable = new InferenceVariable(typeParameter);
if (parameter instanceof PsiWildcardType) {
PsiType bound = ((PsiWildcardType)parameter).getBound();
if (bound != null) {
variable.addBound(bound, ((PsiWildcardType)parameter).isExtends() ? InferenceBound.UPPER : InferenceBound.LOWER);
} else {
variable.addBound(PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope()), InferenceBound.UPPER);
}
} else {
variable.addBound(parameter, InferenceBound.EQ);
}
myInferenceVariables.put(typeParameter, variable);
}
private boolean proceedWithAdditionalConstraints(Set<ConstraintFormula> additionalConstraints) {
while (!additionalConstraints.isEmpty()) {
final Set<InferenceVariable> outputVariables = new HashSet<InferenceVariable>();
@@ -52,7 +52,7 @@ public abstract class InputOutputConstraintFormula implements ConstraintFormula
if (LambdaHighlightingUtil.checkInterfaceFunctional(type) == null) {
final PsiType functionType =
psiExpression instanceof PsiLambdaExpression
? FunctionalInterfaceParameterizationUtil.getGroundTargetType(type, (PsiLambdaExpression)psiExpression, true)
? FunctionalInterfaceParameterizationUtil.getGroundTargetType(type, (PsiLambdaExpression)psiExpression)
: type;
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
@@ -1,13 +1,10 @@
package com.intellij.psi.impl.source.resolve.graphInference.constraints;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.IntArrayList;
import java.util.Collection;
import java.util.List;
/**
@@ -28,7 +25,7 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
return false;
}
final PsiType groundTargetType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT, myExpression, false);
final PsiType groundTargetType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT, myExpression);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(groundTargetType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) {
@@ -65,7 +65,7 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
@Nullable
@Override
public PsiType getFunctionalInterfaceType() {
return FunctionalInterfaceParameterizationUtil.getGroundTargetType(LambdaUtil.getFunctionalInterfaceType(this, true), this, true);
return FunctionalInterfaceParameterizationUtil.getGroundTargetType(LambdaUtil.getFunctionalInterfaceType(this, true), this);
}
@Override
@@ -19,7 +19,7 @@ public class SampleExtendsWildcard {
class SampleSuperWildcard {
public void highlightsTheBug(Stream<String> stream) {
stream.flatMap((<error descr="Incompatible parameter types in lambda expression">Block<?> sink</error>, String element) -> {});
stream.flatMap(<error descr="Cannot infer functional interface type">(Block<?> sink, String element) -> {}</error>);
}
public interface Block<B> {