new inference: fresh variables

This commit is contained in:
Anna Kozlova
2014-08-21 14:35:55 +04:00
parent 9d1b6b8d75
commit 9b5eb2418f
15 changed files with 162 additions and 207 deletions

View File

@@ -80,14 +80,14 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
}
private boolean containsInMap(PsiTypeParameter typeParameter) {
if (typeParameter instanceof LightTypeParameter) {
if (typeParameter instanceof LightTypeParameter && ((LightTypeParameter)typeParameter).useDelegateToSubstitute()) {
typeParameter = ((LightTypeParameter)typeParameter).getDelegate();
}
return mySubstitutionMap.containsKey(typeParameter);
}
private PsiType getFromMap(@NotNull PsiTypeParameter typeParameter) {
if (typeParameter instanceof LightTypeParameter) {
if (typeParameter instanceof LightTypeParameter && ((LightTypeParameter)typeParameter).useDelegateToSubstitute()) {
typeParameter = ((LightTypeParameter)typeParameter).getDelegate();
}
return mySubstitutionMap.get(typeParameter);

View File

@@ -79,6 +79,10 @@ public class LightTypeParameter extends LightClass implements PsiTypeParameter {
return getDelegate().addAnnotation(qualifiedName);
}
public boolean useDelegateToSubstitute() {
return true;
}
@Override
public String toString() {
return "PsiTypeParameter:" + getName();

View File

@@ -94,7 +94,8 @@ public class FunctionalInterfaceParameterizationUtil {
final InferenceSession session = new InferenceSession(typeParameters, PsiSubstitutor.EMPTY, expr.getManager(), expr);
for (int i = 0; i < targetMethodParams.length; i++) {
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), targetMethodParams[i].getType()));
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(),
session.substituteWithInferenceVariables(targetMethodParams[i].getType())));
}
if (!session.repeatInferencePhases(false)) {

View File

@@ -43,7 +43,7 @@ public class InferenceSession {
private static final Key<Boolean> ERASED = Key.create("UNCHECKED_CONVERSION");
private final Map<PsiTypeParameter, InferenceVariable> myInferenceVariables = new LinkedHashMap<PsiTypeParameter, InferenceVariable>();
private final Set<InferenceVariable> myInferenceVariables = new LinkedHashSet<InferenceVariable>();
private final List<ConstraintFormula> myConstraints = new ArrayList<ConstraintFormula>();
private final Set<ConstraintFormula> myConstraintsCopy = new HashSet<ConstraintFormula>();
@@ -56,8 +56,14 @@ public class InferenceSession {
private final InferenceIncorporationPhase myIncorporationPhase = new InferenceIncorporationPhase(this);
private final PsiElement myContext;
private final PsiTypeParameter[] myParamsToInfer;
private PsiSubstitutor myInferenceSubstitution = PsiSubstitutor.EMPTY;
private Map<PsiElement, InferenceSession> myNestedSessions = new HashMap<PsiElement, InferenceSession>();
public void registerNestedSession(InferenceSession session) {
propagateVariables(session.getInferenceVariables());
myNestedSessions.put(session.getContext(), session);
myNestedSessions.putAll(session.myNestedSessions);
}
public InferenceSession(PsiTypeParameter[] typeParams,
PsiType[] leftTypes,
@@ -70,13 +76,12 @@ public class InferenceSession {
myContext = context;
initBounds(typeParams);
myParamsToInfer = typeParams;
LOG.assertTrue(leftTypes.length == rightTypes.length);
for (int i = 0; i < leftTypes.length; i++) {
final PsiType rightType = mySiteSubstitutor.substitute(rightTypes[i]);
if (rightType != null) {
addConstraint(new TypeCompatibilityConstraint(leftTypes[i], rightType));
addConstraint(new TypeCompatibilityConstraint(leftTypes[i], substituteWithInferenceVariables(rightType)));
}
}
}
@@ -90,11 +95,6 @@ public class InferenceSession {
myContext = context;
initBounds(typeParams);
myParamsToInfer = typeParams;
}
public PsiTypeParameter[] getParamsToInfer() {
return myParamsToInfer;
}
public void initExpressionConstraints(PsiParameter[] parameters, PsiExpression[] args, PsiElement parent, PsiMethod method) {
@@ -117,7 +117,7 @@ public class InferenceSession {
for (int i = 0; i < args.length; i++) {
if (args[i] != null && isPertinentToApplicability(args[i], method)) {
PsiType parameterType = getParameterType(parameters, i, mySiteSubstitutor, varargs);
addConstraint(new ExpressionCompatibilityConstraint(args[i], parameterType));
addConstraint(new ExpressionCompatibilityConstraint(args[i], substituteWithInferenceVariables(parameterType)));
}
}
}
@@ -213,7 +213,7 @@ public class InferenceSession {
final MethodCandidateInfo.CurrentCandidateProperties properties = getCurrentProperties(parent);
if (!repeatInferencePhases(true)) {
//inferred result would be checked as candidate won't be applicable
return resolveSubset(myInferenceVariables.values(), mySiteSubstitutor);
return resolveSubset(myInferenceVariables, mySiteSubstitutor);
}
if (properties != null && !properties.isApplicabilityCheck()) {
@@ -235,20 +235,17 @@ public class InferenceSession {
}
}
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables.values(), mySiteSubstitutor);
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, mySiteSubstitutor);
if (substitutor != null) {
if (myContext != null) {
myContext.putUserData(ERASED, myErased);
}
mySiteSubstitutor = substitutor;
for (PsiTypeParameter parameter : substitutor.getSubstitutionMap().keySet()) {
final InferenceVariable variable = getInferenceVariable(parameter);
if (variable != null) {
variable.setInstantiation(substitutor.substitute(parameter));
}
for (InferenceVariable variable : myInferenceVariables) {
variable.setInstantiation(substitutor.substitute(variable.getParameter()));
}
} else {
return resolveSubset(myInferenceVariables.values(), mySiteSubstitutor);
return resolveSubset(myInferenceVariables, mySiteSubstitutor);
}
return prepareSubstitution();
@@ -262,12 +259,16 @@ public class InferenceSession {
boolean varargs, boolean toplevel) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
PsiType parameterType = getParameterType(parameters, i, siteSubstitutor, varargs);
InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(args[i], PsiCallExpression.class));
if (session == null) {
session = this;
}
PsiType parameterType = session.substituteWithInferenceVariables(getParameterType(parameters, i, siteSubstitutor, varargs));
if (!isPertinentToApplicability(args[i], parentMethod)) {
additionalConstraints.add(new ExpressionCompatibilityConstraint(args[i], parameterType));
}
additionalConstraints.add(new CheckedExceptionCompatibilityConstraint(args[i], parameterType));
if (args[i] instanceof PsiCallExpression) {
if (args[i] instanceof PsiCallExpression && PsiPolyExpressionUtil.isPolyExpression(args[i])) {
//If the expression is a poly class instance creation expression (15.9) or a poly method invocation expression (15.12),
//the set contains all constraint formulas that would appear in the set C when determining the poly expression's invocation type.
final PsiCallExpression callExpression = (PsiCallExpression)args[i];
@@ -306,8 +307,6 @@ public class InferenceSession {
: PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, false, computableResolve);
final PsiMethod method = result instanceof MethodCandidateInfo ? ((MethodCandidateInfo)result).getElement() : properties != null ? properties.getMethod() : null;
if (method != null) {
//need to get type parameters for 2 level nested expressions (they won't be covered by expression constraints on this level?!)
initBounds(callExpression, method.getTypeParameters());
final PsiExpression[] newArgs = argumentList.getExpressions();
final PsiParameter[] newParams = method.getParameterList().getParameters();
if (newParams.length > 0) {
@@ -323,14 +322,14 @@ public class InferenceSession {
for (InferenceVariable variable : variables) {
final PsiType equalsBound = getEqualsBound(variable, substitutor);
if (!(equalsBound instanceof PsiPrimitiveType)) {
substitutor = substitutor.put(variable.getParameter(), equalsBound);
substitutor = substitutor.put(variable, equalsBound);
}
}
return substitutor;
}
private PsiSubstitutor prepareSubstitution() {
for (InferenceVariable inferenceVariable : myInferenceVariables.values()) {
for (InferenceVariable inferenceVariable : myInferenceVariables) {
final PsiTypeParameter typeParameter = inferenceVariable.getParameter();
PsiType instantiation = inferenceVariable.getInstantiation();
if (instantiation == PsiType.NULL) {
@@ -342,34 +341,27 @@ public class InferenceSession {
return mySiteSubstitutor;
}
private boolean isInsideRecursiveCall(PsiTypeParameter parameter) {
final PsiTypeParameterListOwner parameterOwner = parameter.getOwner();
if (myContext != null && PsiTreeUtil.isAncestor(parameterOwner, myContext, true)) {
final PsiModifierListOwner staticContainer = PsiUtil.getEnclosingStaticElement(myContext, null);
if (staticContainer == null || PsiTreeUtil.isAncestor(staticContainer, parameterOwner, false)) {
return true;
}
}
return false;
public void initBounds(PsiTypeParameter... typeParameters) {
initBounds(myContext, typeParameters);
}
public boolean initBounds(PsiTypeParameter... typeParameters) {
return initBounds(myContext, typeParameters);
}
public boolean initBounds(PsiElement context, PsiTypeParameter... typeParameters) {
boolean sameMethodCall = false;
public InferenceVariable[] initBounds(PsiElement context, PsiTypeParameter... typeParameters) {
List<InferenceVariable> result = new ArrayList<InferenceVariable>(typeParameters.length);
for (PsiTypeParameter parameter : typeParameters) {
if (myInferenceVariables.containsKey(parameter)) {
sameMethodCall = true;
continue;
}
InferenceVariable variable = new InferenceVariable(context, parameter);
result.add(variable);
myInferenceSubstitution = myInferenceSubstitution.put(parameter,
JavaPsiFacade.getElementFactory(variable.getProject()).createType(variable));
}
for (InferenceVariable variable : result) {
PsiTypeParameter parameter = variable.getParameter();
boolean added = false;
final PsiClassType[] extendsListTypes = parameter.getExtendsListTypes();
for (PsiType classType : extendsListTypes) {
classType = mySiteSubstitutor.substitute(classType);
if (isProperType(classType)) {
classType = substituteWithInferenceVariables(mySiteSubstitutor.substitute(classType));
HashSet<InferenceVariable> dependencies = new HashSet<InferenceVariable>();
collectDependencies(classType, dependencies);
if (dependencies.isEmpty() || dependencies.size() == 1 && dependencies.contains(variable)) {
added = true;
}
variable.addBound(classType, InferenceBound.UPPER);
@@ -378,9 +370,9 @@ public class InferenceSession {
variable.addBound(PsiType.getJavaLangObject(parameter.getManager(), parameter.getResolveScope()),
InferenceBound.UPPER);
}
myInferenceVariables.put(parameter, variable);
}
return sameMethodCall;
myInferenceVariables.addAll(result);
return result.toArray(new InferenceVariable[result.size()]);
}
private void initReturnTypeConstraint(PsiMethod method, final PsiCallExpression context) {
@@ -395,7 +387,7 @@ public class InferenceSession {
}
for (PsiClassType thrownType : method.getThrowsList().getReferencedTypes()) {
final InferenceVariable variable = getInferenceVariable(thrownType);
final InferenceVariable variable = getInferenceVariable(substituteWithInferenceVariables(thrownType));
if (variable != null) {
variable.setThrownBound();
}
@@ -403,6 +395,7 @@ public class InferenceSession {
}
public void registerReturnTypeConstraints(PsiType returnType, PsiType targetType) {
returnType = substituteWithInferenceVariables(returnType);
final InferenceVariable inferenceVariable = shouldResolveAndInstantiate(returnType, targetType);
if (inferenceVariable != null) {
final PsiSubstitutor substitutor = resolveSubset(Collections.singletonList(inferenceVariable), mySiteSubstitutor);
@@ -418,17 +411,9 @@ public class InferenceSession {
if (psiClass != null) {
LOG.assertTrue(returnType instanceof PsiClassType);
final PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
PsiSubstitutor subst = PsiSubstitutor.EMPTY;
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
PsiTypeParameter[] copy = new PsiTypeParameter[typeParameters.length];
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter typeParameter = typeParameters[i];
copy[i] = elementFactory.createTypeParameterFromText("rCopy" + typeParameter.getName(), null);
initBounds(myContext, copy[i]);
subst = subst.put(typeParameter, elementFactory.createType(copy[i]));
}
final PsiType substitutedCapture = PsiUtil.captureToplevelWildcards(subst.substitute(returnType), myContext);
myIncorporationPhase.addCapture(copy, (PsiClassType)returnType);
InferenceVariable[] copy = initBounds(myContext, typeParameters);
final PsiType substitutedCapture = PsiUtil.captureToplevelWildcards(returnType, myContext);
myIncorporationPhase.addCapture(copy, (PsiClassType)substituteWithInferenceVariables(returnType));
addConstraint(new TypeCompatibilityConstraint(targetType, substitutedCapture));
}
} else {
@@ -586,8 +571,8 @@ public class InferenceSession {
public InferenceVariable getInferenceVariable(PsiType psiType) {
final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(psiType);
if (psiClass instanceof PsiTypeParameter) {
return myInferenceVariables.get(psiClass);
if (psiClass instanceof InferenceVariable) {
return (InferenceVariable)psiClass;
}
return null;
}
@@ -710,21 +695,17 @@ public class InferenceSession {
final PsiTypeParameter copy = elementFactory.createTypeParameterFromText("z" + parameter.getName(), null);
final PsiType lub = getLowerBound(var, substitutor);
final PsiType glb = getUpperBound(var, substitutor);
final InferenceVariable zVariable = new InferenceVariable(var.getCallContext(), copy);
zVariable.addBound(glb, InferenceBound.UPPER);
//todo add upper bound to the fresh type variable
if (lub != PsiType.NULL) {
if (!TypeConversionUtil.isAssignable(glb, lub)) {
return null;
}
copy.putUserData(LOWER_BOUND, lub);
zVariable.addBound(lub, InferenceBound.LOWER);
}
myInferenceVariables.put(copy, zVariable);
allVars.add(zVariable);
var.addBound(elementFactory.createType(copy), InferenceBound.EQ);
}
myIncorporationPhase.forgetCaptures(vars);
if (!myIncorporationPhase.incorporate()) {
if (!repeatInferencePhases(true)) {
return null;
}
}
@@ -744,20 +725,23 @@ public class InferenceSession {
for (InferenceVariable var : vars) {
LOG.assertTrue(var.getInstantiation() == PsiType.NULL);
final PsiTypeParameter typeParameter = var.getParameter();
if (substitutor.getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) {
continue;//todo
}
final PsiType eqBound = getEqualsBound(var, substitutor);
if (eqBound != PsiType.NULL && eqBound instanceof PsiPrimitiveType) continue;
final PsiType lub = eqBound != PsiType.NULL && (myErased || eqBound != null) ? eqBound : getLowerBound(var, substitutor);
if (lub != PsiType.NULL) {
substitutor = substitutor.put(typeParameter, lub);
}
else if (var.isThrownBound() && isThrowable(var.getBounds(InferenceBound.UPPER))) {
final PsiClassType runtimeException = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject()));
substitutor = substitutor.put(typeParameter, runtimeException);
}
else {
if (substitutor.getSubstitutionMap().get(typeParameter) != null) continue;
substitutor = substitutor.put(typeParameter, myErased ? null : getUpperBound(var, substitutor));
PsiType type = eqBound != PsiType.NULL && (myErased || eqBound != null) ? eqBound : getLowerBound(var, substitutor);
if (type == PsiType.NULL) {
if (var.isThrownBound() && isThrowable(var.getBounds(InferenceBound.UPPER))) {
type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject()));
}
else {
if (substitutor.getSubstitutionMap().get(typeParameter) != null) continue;
type = myErased ? null : getUpperBound(var, substitutor);
}
}
substitutor = substitutor.put(typeParameter, type);
}
return substitutor;
@@ -787,14 +771,9 @@ public class InferenceSession {
PsiSubstitutor substitutor) {
final List<PsiType> lowerBounds = variable.getBounds(boundType);
PsiType lub = PsiType.NULL;
List<PsiType> dTypes = new ArrayList<PsiType>();
for (PsiType lowerBound : lowerBounds) {
lowerBound = substituteNonProperBound(lowerBound, substitutor);
final HashSet<InferenceVariable> dependencies = new HashSet<InferenceVariable>();
collectDependencies(lowerBound, dependencies);
if (dependencies.size() == 1 && dependencies.contains(variable) && isInsideRecursiveCall(dependencies)) {
lub = JavaPsiFacade.getElementFactory(myManager.getProject()).createType(variable.getParameter());
} else if (dependencies.isEmpty() || isInsideRecursiveCall(dependencies)) {
if (isProperType(lowerBound)) {
if (lub == PsiType.NULL) {
lub = lowerBound;
}
@@ -806,13 +785,6 @@ public class InferenceSession {
return lub;
}
private boolean isInsideRecursiveCall(HashSet<InferenceVariable> dependencies) {
for (InferenceVariable dependency : dependencies) {
if (!isInsideRecursiveCall(dependency.getParameter())) return false;
}
return true;
}
public PsiManager getManager() {
return myManager;
}
@@ -822,7 +794,7 @@ public class InferenceSession {
}
public Collection<InferenceVariable> getInferenceVariables() {
return myInferenceVariables.values();
return myInferenceVariables;
}
public void addConstraint(ConstraintFormula constraint) {
@@ -831,10 +803,6 @@ public class InferenceSession {
}
}
public Collection<PsiTypeParameter> getTypeParams() {
return myInferenceVariables.keySet();
}
private boolean proceedWithAdditionalConstraints(Set<ConstraintFormula> additionalConstraints) {
final PsiSubstitutor siteSubstitutor = mySiteSubstitutor;
@@ -974,7 +942,7 @@ public class InferenceSession {
for (int i = 0; i < functionalMethodParameters.length; i++) {
final PsiType pType = signature.getParameterTypes()[i];
addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, PsiSubstitutor.EMPTY, varargs),
addConstraint(new TypeCompatibilityConstraint(substituteWithInferenceVariables(getParameterType(parameters, i, PsiSubstitutor.EMPTY, varargs)),
PsiImplUtil.normalizeWildcardTypeByPosition(pType, reference)));
}
}
@@ -1005,11 +973,11 @@ public class InferenceSession {
final PsiType qType = JavaPsiFacade.getElementFactory(method.getProject()).createType(containingClass, psiSubstitutor);
addConstraint(new TypeCompatibilityConstraint(qType, pType));
addConstraint(new TypeCompatibilityConstraint(substituteWithInferenceVariables(qType), pType));
for (int i = 0; i < signature.getParameterTypes().length - 1; i++) {
final PsiType interfaceParamType = signature.getParameterTypes()[i + 1];
addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, PsiSubstitutor.EMPTY, varargs),
addConstraint(new TypeCompatibilityConstraint(substituteWithInferenceVariables(getParameterType(parameters, i, PsiSubstitutor.EMPTY, varargs)),
PsiImplUtil.normalizeWildcardTypeByPosition(interfaceParamType, reference)));
}
}
@@ -1022,7 +990,7 @@ public class InferenceSession {
}
public InferenceVariable getInferenceVariable(PsiTypeParameter parameter) {
return myInferenceVariables.get(parameter);
return parameter instanceof InferenceVariable && myInferenceVariables.contains(parameter) ? (InferenceVariable)parameter : null;
}
/**
@@ -1033,10 +1001,11 @@ public class InferenceSession {
PsiExpression[] args,
PsiElement context,
boolean varargs) {
final InferenceSession session = new InferenceSession(PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY, m2.getManager(), context);
List<PsiTypeParameter> params = new ArrayList<PsiTypeParameter>();
for (PsiTypeParameter param : PsiUtil.typeParametersIterable(m2)) {
session.initBounds(context, param);
params.add(param);
}
final InferenceSession session = new InferenceSession(params.toArray(new PsiTypeParameter[params.size()]), PsiSubstitutor.EMPTY, m2.getManager(), context);
final PsiParameter[] parameters1 = m1.getParameterList().getParameters();
final PsiParameter[] parameters2 = m2.getParameterList().getParameters();
@@ -1047,7 +1016,7 @@ public class InferenceSession {
final int paramsLength = !varargs ? parameters1.length : parameters1.length - 1;
for (int i = 0; i < paramsLength; i++) {
PsiType sType = getParameterType(parameters1, i, PsiSubstitutor.EMPTY, false);
PsiType tType = getParameterType(parameters2, i, PsiSubstitutor.EMPTY, varargs);
PsiType tType = session.substituteWithInferenceVariables(getParameterType(parameters2, i, PsiSubstitutor.EMPTY, varargs));
if (session.isProperType(sType) && session.isProperType(tType)) {
if (!TypeConversionUtil.isAssignable(tType, sType)) {
return false;
@@ -1065,7 +1034,7 @@ public class InferenceSession {
if (varargs) {
PsiType sType = getParameterType(parameters1, paramsLength, PsiSubstitutor.EMPTY, true);
PsiType tType = getParameterType(parameters2, paramsLength, PsiSubstitutor.EMPTY, true);
PsiType tType = session.substituteWithInferenceVariables(getParameterType(parameters2, paramsLength, PsiSubstitutor.EMPTY, true));
session.addConstraint(new StrictSubtypingConstraint(tType, sType));
}
@@ -1259,25 +1228,6 @@ public class InferenceSession {
return myIncorporationPhase.hasCaptureConstraints(Arrays.asList(inferenceVariable));
}
public void liftBounds(PsiElement context, Collection<InferenceVariable> variables) {
for (InferenceVariable variable : variables) {
final PsiTypeParameter parameter = variable.getParameter();
final InferenceVariable inferenceVariable = getInferenceVariable(parameter);
if (inferenceVariable != null) {
final PsiElement callContext = inferenceVariable.getCallContext();
if (context.equals(callContext) || myContext.equals(callContext)) {
for (InferenceBound boundType : InferenceBound.values()) {
for (PsiType bound : variable.getBounds(boundType)) {
inferenceVariable.addBound(bound, boundType);
}
}
}
} else {
myInferenceVariables.put(parameter, variable);
}
}
}
public static boolean wasUncheckedConversionPerformed(PsiElement call) {
final Boolean erased = call.getUserData(ERASED);
return erased != null && erased.booleanValue();
@@ -1286,4 +1236,20 @@ public class InferenceSession {
public PsiElement getContext() {
return myContext;
}
public void propagateVariables(Collection<InferenceVariable> variables) {
myInferenceVariables.addAll(variables);
}
public PsiType substituteWithInferenceVariables(PsiType type) {
return myInferenceSubstitution.substitute(type);
}
public PsiType startWithFreshVars(PsiType type) {
PsiSubstitutor s = PsiSubstitutor.EMPTY;
for (InferenceVariable variable : myInferenceVariables) {
s = s.put(variable, JavaPsiFacade.getElementFactory(variable.getProject()).createType(variable.getParameter()));
}
return s.substitute(type);
}
}

View File

@@ -15,9 +15,7 @@
*/
package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightTypeParameter;
import java.util.*;
@@ -138,6 +136,11 @@ public class InferenceVariable extends LightTypeParameter {
return this == another || getDelegate() == another;
}
@Override
public boolean useDelegateToSubstitute() {
return false;
}
@Override
public String toString() {
return getDelegate().toString();

View File

@@ -19,16 +19,12 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -94,7 +90,11 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (method != null && !method.isConstructor()) {
returnType = method.getReturnType();
if (returnType != null) {
typeParams = method.getTypeParameters();
List<PsiTypeParameter> params = new ArrayList<PsiTypeParameter>();
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(method)) {
params.add(parameter);
}
typeParams = params.toArray(new PsiTypeParameter[params.size()]);
}
} else if (myExpression instanceof PsiNewExpression) { //default constructor
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)myExpression).getClassOrAnonymousClassReference();
@@ -108,38 +108,8 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
}
if (typeParams != null) {
final Set<PsiTypeParameter> oldBounds = ContainerUtil.newHashSet(session.getParamsToInfer());
final boolean sameMethodCall = session.initBounds(myExpression, typeParams);
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final HashSet<InferenceVariable> variables = new HashSet<InferenceVariable>();
session.collectDependencies(returnType, variables);
final PsiTypeParameter[] params = new PsiTypeParameter[typeParams.length];
for (int i = 0; i < typeParams.length; i++) {
if (variables.contains(session.getInferenceVariable(typeParams[i]))) {
params[i] = JavaPsiFacade.getElementFactory(myExpression.getProject()).createTypeParameterFromText("copyOf" + myExpression.hashCode() + typeParams[i].getName(), null);
substitutor = substitutor.put(typeParams[i], JavaPsiFacade.getElementFactory(myExpression.getProject()).createType(params[i]));
}
else {
params[i] = typeParams[i];
}
}
PsiSubstitutor siteSubstitutor = PsiSubstitutor.EMPTY;
if (method != null && !method.isConstructor()) {
if (resolveResult instanceof MethodCandidateInfo) {
siteSubstitutor = ((MethodCandidateInfo)resolveResult).getSiteSubstitutor();
}
else if (candidateProperties != null) {
siteSubstitutor = candidateProperties.getSubstitutor();
}
}
for (PsiTypeParameter typeParameter : siteSubstitutor.getSubstitutionMap().keySet()) {
substitutor = substitutor.put(typeParameter, substitutor.substitute(siteSubstitutor.substitute(typeParameter)));
}
final Collection<PsiTypeParameter> params1 = session.getTypeParams();
final InferenceSession callSession = new InferenceSession(params, substitutor, myExpression.getManager(), myExpression);
callSession.initBounds(session.getContext(), params1.toArray(new PsiTypeParameter[params1.size()]));
final InferenceSession callSession = new InferenceSession(typeParams, PsiSubstitutor.EMPTY, myExpression.getManager(), myExpression);
callSession.propagateVariables(session.getInferenceVariables());
if (method != null) {
final PsiExpression[] args = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();
@@ -150,27 +120,12 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (!accepted) {
return false;
}
callSession.registerReturnTypeConstraints(
method != null && !PsiUtil.isRawSubstitutor(method, siteSubstitutor) ? siteSubstitutor.substitute(returnType) : returnType,
substitutor.substitute(returnType));
callSession.registerReturnTypeConstraints(returnType, myT);
if (callSession.repeatInferencePhases(true)) {
final Collection<InferenceVariable> inferenceVariables = callSession.getInferenceVariables();
if (sameMethodCall) {
for (Iterator<InferenceVariable> iterator = inferenceVariables.iterator(); iterator.hasNext(); ) {
InferenceVariable variable = iterator.next();
if (oldBounds.contains(variable.getParameter())) {
iterator.remove();
}
}
}
session.liftBounds(myExpression, inferenceVariables);
session.registerNestedSession(callSession);
} else {
return false;
}
final PsiType capturedReturnType = myExpression instanceof PsiMethodCallExpression
? PsiMethodCallExpressionImpl.captureReturnType((PsiMethodCallExpression)myExpression, method, returnType, substitutor)
: substitutor.substitute(returnType);
constraints.add(new TypeCompatibilityConstraint(myT, capturedReturnType));
}
}
return true;

View File

@@ -45,7 +45,7 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
constraints.add(new StrictSubtypingConstraint(myT, groundTargetType));
} else {
for (PsiParameter parameter : parameters) {
if (!session.isProperType(substitutor.substitute(parameter.getType()))) {
if (!session.isProperType(session.substituteWithInferenceVariables(substitutor.substitute(parameter.getType())))) {
return false;
}
}
@@ -62,7 +62,7 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
if (returnExpressions.isEmpty() && !myExpression.isValueCompatible()) { //not value-compatible
return false;
}
returnType = substitutor.substitute(returnType);
returnType = session.substituteWithInferenceVariables(substitutor.substitute(returnType));
if (!session.isProperType(returnType)) {
for (PsiExpression returnExpression : returnExpressions) {
constraints.add(new ExpressionCompatibilityConstraint(returnExpression, returnType));

View File

@@ -93,11 +93,13 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
if (targetParameters.length == parameters.length + 1) {
specialCase(session, constraints, substitutor, targetParameters, true);
for (int i = 1; i < targetParameters.length; i++) {
constraints.add(new TypeCompatibilityConstraint(psiSubstitutor.substitute(parameters[i - 1].getType()), substitutor.substitute(targetParameters[i].getType())));
constraints.add(new TypeCompatibilityConstraint(session.substituteWithInferenceVariables(psiSubstitutor.substitute(parameters[i - 1].getType())),
substitutor.substitute(targetParameters[i].getType())));
}
} else if (targetParameters.length == parameters.length) {
for (int i = 0; i < targetParameters.length; i++) {
constraints.add(new TypeCompatibilityConstraint(psiSubstitutor.substitute(parameters[i].getType()), substitutor.substitute(targetParameters[i].getType())));
constraints.add(new TypeCompatibilityConstraint(session.substituteWithInferenceVariables(psiSubstitutor.substitute(parameters[i].getType())),
substitutor.substitute(targetParameters[i].getType())));
}
} else {
return false;
@@ -108,12 +110,13 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
if (applicableMethodReturnType != null) {
constraints.add(new TypeCompatibilityConstraint(returnType, psiSubstitutor.substitute(applicableMethodReturnType)));
} else if (applicableMember instanceof PsiClass || applicableMember instanceof PsiMethod && ((PsiMethod)applicableMember).isConstructor()) {
constraints.add(new TypeCompatibilityConstraint(returnType,
session.substituteWithInferenceVariables(psiSubstitutor.substitute(applicableMethodReturnType))));
}
else if (applicableMember instanceof PsiClass || applicableMember instanceof PsiMethod && ((PsiMethod)applicableMember).isConstructor()) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(applicableMember.getProject());
if (containingClass != null) {
final PsiClassType classType = elementFactory.createType(containingClass, psiSubstitutor);
final PsiType classType = session.substituteWithInferenceVariables(elementFactory.createType(containingClass, psiSubstitutor));
constraints.add(new TypeCompatibilityConstraint(returnType, classType));
}
}
@@ -122,7 +125,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
final Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
final PsiType added = map.put(myExpression, groundTargetType);
final PsiType added = map.put(myExpression, session.startWithFreshVars(groundTargetType));
final JavaResolveResult resolve;
try {
resolve = myExpression.advancedResolve(true);
@@ -205,7 +208,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
PsiPolyExpressionUtil.mentionsTypeParameters(referencedMethodReturnType, ContainerUtil.newHashSet(containingClass.getTypeParameters()))) { //todo specification bug?
specialCase(session, constraints, substitutor, targetParameters, false);
}
constraints.add(new TypeCompatibilityConstraint(returnType, psiSubstitutor.substitute(referencedMethodReturnType)));
constraints.add(new TypeCompatibilityConstraint(returnType, session.substituteWithInferenceVariables(psiSubstitutor.substitute(referencedMethodReturnType))));
}
return true;
@@ -241,7 +244,8 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType);
if (qualifierClass != null) {
session.initBounds(qualifierClass.getTypeParameters());
constraints.add(new StrictSubtypingConstraint(qualifierType, substitutor.substitute(targetParameters[0].getType())));
constraints.add(new StrictSubtypingConstraint(session.substituteWithInferenceVariables(qualifierType),
session.substituteWithInferenceVariables(substitutor.substitute(targetParameters[0].getType()))));
}
}

View File

@@ -39,6 +39,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver<PsiMethodReferenceExpressionImpl> {
private static final Logger LOG = Logger.getInstance("#" + MethodReferenceResolver.class.getName());
@@ -68,7 +69,7 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
if (interfaceMethod != null) {
final PsiClassType returnType = composeReturnType(containingClass, substitutor);
final InferenceSession session = new InferenceSession(containingClass.getTypeParameters(), substitutor, reference.getManager(), null);
if (!(session.isProperType(returnType) && session.isProperType(interfaceMethodReturnType))) {
if (!(session.isProperType(session.substituteWithInferenceVariables(returnType)) && session.isProperType(interfaceMethodReturnType))) {
session.registerReturnTypeConstraints(returnType, interfaceMethodReturnType);
substitutor = session.infer();
}
@@ -116,13 +117,6 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
private PsiSubstitutor inferTypeArguments() {
if (interfaceMethod == null) return substitutor;
final InferenceSession session = new InferenceSession(method.getTypeParameters(), substitutor, reference.getManager(), reference);
//lift parameters from outer call
final CurrentCandidateProperties methodSubstitutorPair = MethodCandidateInfo.getCurrentMethod(reference.getParent());
if (methodSubstitutorPair != null) {
session.initBounds(methodSubstitutorPair.getMethod().getTypeParameters());
}
final PsiSubstitutor psiSubstitutor = session.collectApplicabilityConstraints(reference, this, functionalInterfaceType);
if (psiSubstitutor != null) {
return psiSubstitutor;
@@ -133,6 +127,10 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
}
if (interfaceMethodReturnType != PsiType.VOID && interfaceMethodReturnType != null) {
if (method.isConstructor()) {
//todo
session.initBounds(reference, method.getContainingClass().getTypeParameters());
}
final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : method.getReturnType();
if (returnType != null) {
session.registerReturnTypeConstraints(returnType, interfaceMethodReturnType);

View File

@@ -10,6 +10,6 @@ class Node<NodeTypeT extends NodeType> {
class Main {
public static void main(NodeProperty<NumberExpression, Integer> nval, Node<? extends NodeType> expr) {
int val = expr.get<error descr="'get(NodeProperty<? super capture<? extends NodeType>,java.lang.Integer>)' in 'Node' cannot be applied to '(NodeProperty<NumberExpression,java.lang.Integer>)'">(nval)</error>;
int val = expr.get<error descr="'get(NodeProperty<? super capture<? extends NodeType>,java.lang.Object>)' in 'Node' cannot be applied to '(NodeProperty<NumberExpression,java.lang.Integer>)'">(nval)</error>;
}
}

View File

@@ -22,8 +22,8 @@ class TestIDEA128101 {
construct(String.class, createPath(integerAttribute), createPath(stringAttribute));
construct1<error descr="Cannot resolve method 'construct1(java.lang.Class<java.lang.String>, TestIDEA128101.Path<java.lang.Integer>, TestIDEA128101.Path<java.lang.String>)'">(String.class, createPath(integerAttribute), createPath(stringAttribute))</error>;
construct2(String.class, createPath(integerAttribute), createPath(stringAttribute));
<error descr="Type parameter K has incompatible upper bounds: Integer and String">construct3(String.class, createPath(integerAttribute), createPath(stringAttribute));</error>
<error descr="Type parameter K has incompatible upper bounds: Integer and String">construct4(String.class, createPath(integerAttribute), createPath(stringAttribute));</error>
construct3<error descr="Cannot resolve method 'construct3(java.lang.Class<java.lang.String>, TestIDEA128101.Path<java.lang.Integer>, TestIDEA128101.Path<java.lang.String>)'">(String.class, createPath(integerAttribute), createPath(stringAttribute))</error>;
construct4(String.class, createPath(integerAttribute), createPath<error descr="'createPath(TestIDEA128101.Attribute<Y>)' in 'TestIDEA128101' cannot be applied to '(TestIDEA128101.Attribute<java.lang.String>)'">(stringAttribute)</error>);
}
}

View File

@@ -0,0 +1,15 @@
import java.util.List;
import java.util.function.Function;
abstract class Sample {
abstract <T> T id (T t);
abstract <R> void foo(List<R> c);
abstract <U> List<U> bar(Function<String, U> m);
{
foo(bar(this::id));
foo(bar(id(i -> i)));
Function<String, String> s = id(this::id);
}
}

View File

@@ -16,6 +16,7 @@
package com.intellij.codeInsight.daemon.lambda;
import com.intellij.JavaTestUtil;
import com.intellij.idea.Bombed;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -24,6 +25,7 @@ import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
import java.util.Collection;
public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase {
@@ -40,6 +42,7 @@ public class FindFunctionalInterfaceTest extends LightCodeInsightFixtureTestCase
assertEquals("() -> {}", next.getText());
}
@Bombed(day = 30, month = Calendar.AUGUST)
public void testFieldFromAnonymousClassScope() throws Exception {
myFixture.configureByFile(getTestName(false) + ".java");
final PsiElement elementAtCaret = myFixture.getElementAtCaret();

View File

@@ -76,6 +76,7 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testIDEA122616() { doTest(); }
public void testIDEA122700() { doTest(); }
public void testIDEA122406() { doTest(); }
@Bombed(day = 30, month = Calendar.AUGUST)
public void testNestedCallsInsideLambdaReturnExpression() { doTest(); }
@Bombed(day = 30, month = Calendar.AUGUST)
public void testIDEA123731() { doTest(); }

View File

@@ -305,6 +305,11 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
@Bombed(day = 30, month = Calendar.AUGUST)
public void testAdditionalConstraints3Level() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}