new inference: start inference from top to bottom; ensure getTargetType doesn't perform any inference; cache intermediate results

This commit is contained in:
Anna Kozlova
2015-11-19 10:47:21 +01:00
parent be5f4165ab
commit d32e6ec080
14 changed files with 484 additions and 156 deletions
@@ -363,6 +363,10 @@ public class MethodCandidateInfo extends CandidateInfo{
return myInferenceError;
}
public CurrentCandidateProperties createProperties() {
return new CurrentCandidateProperties(this, getSiteSubstitutor(), isVarargs(), false);
}
public static class CurrentCandidateProperties {
private final MethodCandidateInfo myMethod;
private PsiSubstitutor mySubstitutor;
@@ -339,4 +339,8 @@ public class InferenceIncorporationPhase {
}
}
}
public List<Pair<PsiTypeParameter[], PsiClassType>> getCaptures() {
return myCaptures;
}
}
@@ -18,7 +18,6 @@ package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
@@ -65,7 +64,7 @@ public class InferenceSession {
private final Set<InferenceVariable> myInferenceVariables = new LinkedHashSet<InferenceVariable>();
private final List<ConstraintFormula> myConstraints = new ArrayList<ConstraintFormula>();
private final Set<ConstraintFormula> myConstraintsCopy = new HashSet<ConstraintFormula>();
private final InferenceSessionContainer myInferenceSessionContainer = new InferenceSessionContainer();
private InferenceSessionContainer myInferenceSessionContainer = new InferenceSessionContainer();
private PsiSubstitutor mySiteSubstitutor;
private final PsiManager myManager;
@@ -81,8 +80,22 @@ public class InferenceSession {
private PsiSubstitutor myInferenceSubstitution = PsiSubstitutor.EMPTY;
public InferenceSession(InitialInferenceState initialState) {
myContext = initialState.getContext();
myManager = myContext.getManager();
myInferenceSubstitution = initialState.getInferenceSubstitutor();
myInferenceVariables.addAll(initialState.getInferenceVariables());
mySiteSubstitutor = initialState.getSiteSubstitutor();
for (Pair<PsiTypeParameter[], PsiClassType> capture : initialState.getCaptures()) {
myIncorporationPhase.addCapture(capture.first, capture.second);
}
myInferenceSessionContainer = initialState.getInferenceSessionContainer();
}
public InferenceSession(PsiTypeParameter[] typeParams,
PsiType[] leftTypes,
PsiType[] leftTypes,
PsiType[] rightTypes,
PsiSubstitutor siteSubstitutor,
PsiManager manager,
@@ -278,23 +291,49 @@ public class InferenceSession {
return infer(null, null, null);
}
public PsiSubstitutor collectAdditionalAndInfer(@NotNull PsiParameter[] parameters,
@NotNull PsiExpression[] args,
@NotNull MethodCandidateInfo.CurrentCandidateProperties properties,
@NotNull PsiSubstitutor psiSubstitutor) {
return doInfer(parameters, args, myContext, properties, psiSubstitutor);
}
@NotNull
public PsiSubstitutor infer(@Nullable PsiParameter[] parameters,
@Nullable PsiExpression[] args,
@Nullable PsiElement parent) {
final MethodCandidateInfo.CurrentCandidateProperties properties = getCurrentProperties(parent);
return infer(parameters, args, parent, getCurrentProperties(parent));
}
@NotNull
public PsiSubstitutor infer(@Nullable PsiParameter[] parameters,
@Nullable PsiExpression[] args,
@Nullable PsiElement parent,
@Nullable MethodCandidateInfo.CurrentCandidateProperties properties) {
return doInfer(parameters, args, parent, properties, null);
}
@NotNull
private PsiSubstitutor doInfer(@Nullable PsiParameter[] parameters,
@Nullable PsiExpression[] args,
@Nullable PsiElement parent,
@Nullable MethodCandidateInfo.CurrentCandidateProperties properties,
PsiSubstitutor initialSubstitutor) {
try {
if (!repeatInferencePhases(true)) {
if (initialSubstitutor == null && !repeatInferencePhases(true)) {
//inferred result would be checked as candidate won't be applicable
return resolveSubset(myInferenceVariables, mySiteSubstitutor);
}
if (properties != null && !properties.isApplicabilityCheck()) {
initReturnTypeConstraint(properties.getMethod(), (PsiCall)parent);
if (!repeatInferencePhases(true)) {
return prepareSubstitution();
if (initialSubstitutor == null) {
initReturnTypeConstraint(properties.getMethod(), (PsiCall)parent);
if (!repeatInferencePhases(true)) {
return prepareSubstitution();
}
}
if (parameters != null && args != null) {
final Set<ConstraintFormula> additionalConstraints = new LinkedHashSet<ConstraintFormula>();
if (parameters.length > 0) {
@@ -307,7 +346,11 @@ public class InferenceSession {
}
}
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, PsiSubstitutor.EMPTY);
if (initialSubstitutor == null) {
initialSubstitutor = PsiSubstitutor.EMPTY;
}
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, initialSubstitutor);
if (substitutor != null) {
if (myContext != null) {
myContext.putUserData(ERASED, myErased);
@@ -373,16 +416,16 @@ public class InferenceSession {
collectAdditionalConstraints(additionalConstraints, (PsiCall)arg);
}
}
else if (arg instanceof PsiLambdaExpression &&
isPertinentToApplicability(arg, parentMethod) &&
!isProperType(retrieveNonPrimitiveEqualsBounds(myInferenceVariables).substitute(parameterType))) {
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)arg, parameterType);
else if (arg instanceof PsiLambdaExpression &&
isPertinentToApplicability(arg, parentMethod)) {
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)arg, parameterType,
!isProperType(retrieveNonPrimitiveEqualsBounds(myInferenceVariables).substitute(parameterType)));
}
}
}
}
private static PsiMethod getCalledMethod(PsiCall arg) {
public static PsiMethod getCalledMethod(PsiCall arg) {
final PsiExpressionList argumentList = arg.getArgumentList();
if (argumentList == null) {
return null;
@@ -392,7 +435,7 @@ public class InferenceSession {
if (properties != null) {
return properties.getMethod();
}
final JavaResolveResult resolveResult = getMethodResult(arg);
final JavaResolveResult resolveResult = getResolveResult(arg);
if (resolveResult instanceof MethodCandidateInfo) {
return (PsiMethod)resolveResult.getElement();
}
@@ -403,34 +446,41 @@ public class InferenceSession {
private void collectLambdaReturnExpression(Set<ConstraintFormula> additionalConstraints,
PsiLambdaExpression lambdaExpression,
PsiType parameterType) {
PsiType parameterType,
boolean addConstraint) {
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(parameterType);
if (interfaceReturnType != null) {
final List<PsiExpression> returnExpressions = LambdaUtil.getReturnExpressions(lambdaExpression);
for (PsiExpression returnExpression : returnExpressions) {
processReturnExpression(additionalConstraints, returnExpression, interfaceReturnType);
processReturnExpression(additionalConstraints, returnExpression, interfaceReturnType, addConstraint);
}
}
}
private void processReturnExpression(Set<ConstraintFormula> additionalConstraints,
PsiExpression returnExpression,
PsiType functionalType) {
PsiType functionalType,
boolean addConstraint) {
if (returnExpression instanceof PsiCallExpression) {
final PsiMethod calledMethod = getCalledMethod((PsiCallExpression)returnExpression);
if (calledMethod != null && PsiPolyExpressionUtil.isMethodCallPolyExpression(returnExpression, calledMethod)) {
collectAdditionalConstraints(additionalConstraints, (PsiCallExpression)returnExpression);
if (addConstraint) {
final PsiMethod calledMethod = getCalledMethod((PsiCallExpression)returnExpression);
if (calledMethod != null && PsiPolyExpressionUtil.isMethodCallPolyExpression(returnExpression, calledMethod)) {
collectAdditionalConstraints(additionalConstraints, (PsiCallExpression)returnExpression);
}
}
else {
getInferenceSessionContainer().registerNestedSession(this, functionalType, returnExpression);
}
}
else if (returnExpression instanceof PsiParenthesizedExpression) {
processReturnExpression(additionalConstraints, ((PsiParenthesizedExpression)returnExpression).getExpression(), functionalType);
processReturnExpression(additionalConstraints, ((PsiParenthesizedExpression)returnExpression).getExpression(), functionalType, addConstraint);
}
else if (returnExpression instanceof PsiConditionalExpression) {
processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getThenExpression(), functionalType);
processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getElseExpression(), functionalType);
processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getThenExpression(), functionalType, addConstraint);
processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getElseExpression(), functionalType, addConstraint);
}
else if (returnExpression instanceof PsiLambdaExpression) {
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)returnExpression, functionalType);
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)returnExpression, functionalType, myErased);
}
}
@@ -438,9 +488,9 @@ public class InferenceSession {
final PsiCall callExpression) {
PsiExpressionList argumentList = callExpression.getArgumentList();
if (argumentList != null) {
final JavaResolveResult result = getMethodResult(callExpression);
MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList);
final PsiMethod method = result instanceof MethodCandidateInfo ? ((MethodCandidateInfo)result).getElement() : properties != null ? properties.getMethod() : null;
final JavaResolveResult result = properties != null ? null : getResolveResult(callExpression);
final PsiMethod method = properties != null ? properties.getMethod() : result instanceof MethodCandidateInfo ? ((MethodCandidateInfo)result).getElement() : null;
if (method != null) {
final PsiExpression[] newArgs = argumentList.getExpressions();
final PsiParameter[] newParams = method.getParameterList().getParameters();
@@ -451,23 +501,6 @@ public class InferenceSession {
}
}
private static JavaResolveResult getMethodResult(final PsiCall callExpression) {
final PsiExpressionList argumentList = callExpression.getArgumentList();
final PsiLambdaExpression expression = PsiTreeUtil.getParentOfType(argumentList, PsiLambdaExpression.class);
final Computable<JavaResolveResult> computableResolve = new Computable<JavaResolveResult>() {
@Override
public JavaResolveResult compute() {
return getResolveResult(callExpression);
}
};
MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList);
return properties != null ? null :
expression == null || !PsiResolveHelper.ourGraphGuard.currentStack().contains(expression)
? computableResolve.compute()
: PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, false, computableResolve);
}
public static JavaResolveResult getResolveResult(final PsiCall callExpression) {
if (callExpression instanceof PsiNewExpression) {
PsiUtilCore.ensureValid(callExpression);
@@ -543,6 +576,15 @@ public class InferenceSession {
return mySiteSubstitutor;
}
public InitialInferenceState createInitialState() {
return new InitialInferenceState(myInferenceVariables,
myContext,
myInferenceSubstitution,
mySiteSubstitutor,
myIncorporationPhase.getCaptures(),
myInferenceSessionContainer);
}
public void initBounds(PsiTypeParameter... typeParameters) {
initBounds(myContext, typeParameters);
}
@@ -580,7 +622,7 @@ public class InferenceSession {
PsiPolyExpressionUtil.isMethodCallPolyExpression((PsiExpression)context, method)) {
PsiType returnType = method.getReturnType();
if (!PsiType.VOID.equals(returnType) && returnType != null) {
PsiType targetType = getTargetType(context);
PsiType targetType = getTargetTypeFromParent(context, false);
if (targetType != null && !PsiType.VOID.equals(targetType)) {
registerReturnTypeConstraints(
PsiUtil.isRawSubstitutor(method, mySiteSubstitutor) ? returnType : mySiteSubstitutor.substitute(returnType), targetType);
@@ -691,6 +733,15 @@ public class InferenceSession {
}
public static PsiType getTargetType(final PsiElement context) {
return getTargetTypeFromParent(context, true);
}
/**
* @param inferParent false during inference;
* conditional expression type can't be asked during inference as it is a poly expression and
* {@link ExpressionCompatibilityConstraint} should be created instead
*/
private static PsiType getTargetTypeFromParent(final PsiElement context, boolean inferParent) {
PsiType targetType = PsiTypesUtil.getExpectedTypeByParent(context);
if (targetType != null) {
return targetType;
@@ -708,42 +759,35 @@ public class InferenceSession {
if (properties != null && properties.isApplicabilityCheck()) {
return getTypeByMethod(context, argumentList, properties.getMethod(), properties.isVarargs(), properties.getSubstitutor());
}
final JavaResolveResult result = properties != null ? properties.getInfo() : ((PsiCall)gParent).resolveMethodGenerics();
final boolean varargs = chooseVarargsMode(properties, result);
PsiSubstitutor substitutor = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(context, false,
new Computable<PsiSubstitutor>() {
@Override
public PsiSubstitutor compute() {
return result.getSubstitutor();
}
}
);
if (substitutor == null && properties != null) {
substitutor = properties.getSubstitutor();
if (inferParent) {
final JavaResolveResult result = ((PsiCall)gParent).resolveMethodGenerics();
final boolean varargs = result instanceof MethodCandidateInfo && ((MethodCandidateInfo)result).isVarargs();
return getTypeByMethod(context, argumentList, result.getElement(), varargs, result.getSubstitutor());
}
return getTypeByMethod(context, argumentList, result.getElement(), varargs, substitutor);
}
}
} else if (parent instanceof PsiConditionalExpression) {
return getTargetType(parent);
}
else if (parent instanceof PsiConditionalExpression) {
return getTargetTypeFromParent(parent, inferParent);
}
else if (parent instanceof PsiLambdaExpression) {
return getTargetTypeByContainingLambda((PsiLambdaExpression)parent);
return getTargetTypeFromParentLambda((PsiLambdaExpression)parent, inferParent);
}
else if (parent instanceof PsiReturnStatement) {
return getTargetTypeByContainingLambda(PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class));
return getTargetTypeFromParentLambda(PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, true, PsiMethod.class), inferParent);
}
return null;
}
private static PsiType getTargetTypeByContainingLambda(PsiLambdaExpression lambdaExpression) {
private static PsiType getTargetTypeFromParentLambda(PsiLambdaExpression lambdaExpression, boolean inferParent) {
if (lambdaExpression != null) {
if (PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent()) instanceof PsiExpressionList) {
final PsiType typeTypeByParentCall = getTargetType(lambdaExpression);
return LambdaUtil.getFunctionalInterfaceReturnType(
FunctionalInterfaceParameterizationUtil.getGroundTargetType(typeTypeByParentCall, lambdaExpression));
final PsiType typeTypeByParentCall = getTargetTypeFromParent(lambdaExpression, inferParent);
if (typeTypeByParentCall != null) {
return LambdaUtil.getFunctionalInterfaceReturnType(FunctionalInterfaceParameterizationUtil.getGroundTargetType(typeTypeByParentCall, lambdaExpression));
}
return LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression.getFunctionalInterfaceType());
return inferParent || !(PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent()) instanceof PsiExpressionList)
? LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression.getFunctionalInterfaceType()) : null;
}
return null;
}
@@ -995,8 +1039,8 @@ public class InferenceSession {
@NotNull
private PsiSubstitutor resolveSubset(Collection<InferenceVariable> vars, PsiSubstitutor substitutor) {
for (InferenceVariable var : vars) {
LOG.assertTrue(var.getInstantiation() == PsiType.NULL);
final PsiType type = checkBoundsConsistency(substitutor, var);
final PsiType instantiation = var.getInstantiation();
final PsiType type = instantiation == PsiType.NULL ? checkBoundsConsistency(substitutor, var) : instantiation;
if (type != PsiType.NULL) {
substitutor = substitutor.put(var, type);
}
@@ -1013,10 +1057,9 @@ public class InferenceSession {
PsiType type;
if (eqBound != PsiType.NULL && (myErased || eqBound != null)) {
if (lowerBound != PsiType.NULL && !TypeConversionUtil.isAssignable(eqBound, lowerBound)) {
registerIncompatibleErrorMessage(
incompatibleBoundsMessage(var, substitutor, InferenceBound.EQ, EQUALITY_CONSTRAINTS_PRESENTATION, InferenceBound.LOWER, LOWER_BOUNDS_PRESENTATION)
);
return PsiType.NULL;
final String incompatibleBoundsMessage =
incompatibleBoundsMessage(var, substitutor, InferenceBound.EQ, EQUALITY_CONSTRAINTS_PRESENTATION, InferenceBound.LOWER, LOWER_BOUNDS_PRESENTATION);
return registerIncompatibleErrorMessage(var, incompatibleBoundsMessage);
} else {
type = eqBound;
}
@@ -1036,8 +1079,7 @@ public class InferenceSession {
if (type instanceof PsiIntersectionType) {
final String conflictingConjunctsMessage = ((PsiIntersectionType)type).getConflictingConjunctsMessage();
if (conflictingConjunctsMessage != null) {
registerIncompatibleErrorMessage("Type parameter " + var.getName() + " has incompatible upper bounds: " + conflictingConjunctsMessage);
return PsiType.NULL;
return registerIncompatibleErrorMessage(var, "Type parameter " + var.getName() + " has incompatible upper bounds: " + conflictingConjunctsMessage);
}
}
}
@@ -1052,8 +1094,7 @@ public class InferenceSession {
incompatibleBoundsMessage = incompatibleBoundsMessage(var, substitutor, InferenceBound.LOWER, LOWER_BOUNDS_PRESENTATION, InferenceBound.UPPER, UPPER_BOUNDS_PRESENTATION);
}
if (incompatibleBoundsMessage != null) {
registerIncompatibleErrorMessage(incompatibleBoundsMessage);
return PsiType.NULL;
return registerIncompatibleErrorMessage(var, incompatibleBoundsMessage);
}
}
}
@@ -1061,13 +1102,16 @@ public class InferenceSession {
return type;
}
private void registerIncompatibleErrorMessage(String value) {
if (myErrorMessages == null) {
myErrorMessages = new ArrayList<String>();
}
if (!myErrorMessages.contains(value)) {
myErrorMessages.add(value);
private PsiType registerIncompatibleErrorMessage(InferenceVariable var, @NotNull String incompatibleBoundsMessage) {
if (var.getCallContext() == myContext) {
if (myErrorMessages == null) {
myErrorMessages = new ArrayList<String>();
}
if (!myErrorMessages.contains(incompatibleBoundsMessage)) {
myErrorMessages.add(incompatibleBoundsMessage);
}
}
return PsiType.NULL;
}
private String incompatibleBoundsMessage(final InferenceVariable var,
@@ -1234,9 +1278,7 @@ public class InferenceSession {
PsiExpression expression = ((ExpressionCompatibilityConstraint)formula).getExpression();
if (expression instanceof PsiLambdaExpression) {
PsiType parameterType = ((ExpressionCompatibilityConstraint)formula).getT();
if (!isProperType(parameterType)) {
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)expression, parameterType);
}
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)expression, parameterType, !isProperType(parameterType));
}
}
}
@@ -15,23 +15,32 @@
*/
package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.psi.PsiCall;
import com.intellij.psi.PsiElement;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.constraints.ExpressionCompatibilityConstraint;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class InferenceSessionContainer {
private static final Logger LOG = Logger.getInstance("#" + InferenceSessionContainer.class.getName());
private final Map<PsiElement, InferenceSession> myNestedSessions = new HashMap<PsiElement, InferenceSession>();
public InferenceSessionContainer() {
}
public void registerNestedSession(InferenceSession targetSession, InferenceSession session) {
targetSession.propagateVariables(session.getInferenceVariables());
public void registerNestedSession(InferenceSession session) {
myNestedSessions.put(session.getContext(), session);
myNestedSessions.putAll(session.getInferenceSessionContainer().myNestedSessions);
}
@@ -41,4 +50,151 @@ public class InferenceSessionContainer {
InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(arg, PsiCall.class));
return session == null ? defaultSession : session;
}
public void registerNestedSession(InferenceSession session,
PsiType returnType,
PsiExpression returnExpression) {
final InferenceSession callSession = findNestedCallSession(((PsiCallExpression)returnExpression).getArgumentList(), null);
if (callSession == null) {
final InferenceSession inferenceSession =
ExpressionCompatibilityConstraint.reduceExpressionCompatibilityConstraint(session, returnExpression, returnType);
if (inferenceSession != null && inferenceSession != session) {
registerNestedSession(inferenceSession);
}
}
}
static PsiSubstitutor infer(@NotNull PsiTypeParameter[] typeParameters,
@NotNull PsiParameter[] parameters,
@NotNull PsiExpression[] arguments,
@NotNull PsiSubstitutor partialSubstitutor,
@NotNull final PsiElement parent) {
if (parent instanceof PsiCall) {
final PsiExpressionList argumentList = ((PsiCall)parent).getArgumentList();
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList);
if (properties != null && !properties.isApplicabilityCheck()) {
final Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>>
session = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(parent, false,
new Computable<Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>>>() {
@Override
public Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>> compute() {
return createValue(parent);
}
});
if (session != null) {
final InitialInferenceState initialInferenceState = session.second.get(PsiTreeUtil.getParentOfType(argumentList, PsiCall.class));
if (initialInferenceState != null) {
return new InferenceSession(initialInferenceState).collectAdditionalAndInfer(parameters, arguments, properties, session.first);
}
}
}
}
final InferenceSession inferenceSession = new InferenceSession(typeParameters, partialSubstitutor, parent.getManager(), parent);
inferenceSession.initExpressionConstraints(parameters, arguments, parent, null);
return inferenceSession.infer(parameters, arguments, parent);
}
private static Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>> createValue(@NotNull final PsiElement parent) {
if (MethodCandidateInfo.isOverloadCheck()) {
return startTopLevelInference(parent);
}
return CachedValuesManager.getCachedValue(parent,
new CachedValueProvider<Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>>>() {
@Nullable
@Override
public Result<Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>>> compute() {
return new Result<Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>>>(
startTopLevelInference(parent), PsiModificationTracker.MODIFICATION_COUNT);
}
});
}
private static Pair<PsiSubstitutor, Map<PsiElement, InitialInferenceState>> startTopLevelInference(@NotNull final PsiElement parent) {
final PsiCall topLevelCall = treeWalkUp(parent);
if (topLevelCall != null) {
final JavaResolveResult result = topLevelCall.resolveMethodGenerics();
if (result instanceof MethodCandidateInfo) {
final PsiMethod method = ((MethodCandidateInfo)result).getElement();
final PsiParameter[] topLevelParameters = method.getParameterList().getParameters();
final PsiExpressionList topLevelCallArgumentList = topLevelCall.getArgumentList();
LOG.assertTrue(topLevelCallArgumentList != null, topLevelCall);
final PsiExpression[] topLevelArguments = topLevelCallArgumentList.getExpressions();
final InferenceSession topLevelSession =
new InferenceSession(method.getTypeParameters(), ((MethodCandidateInfo)result).getSiteSubstitutor(), topLevelCall.getManager(), topLevelCall);
topLevelSession.initExpressionConstraints(topLevelParameters, topLevelArguments, topLevelCall, method, ((MethodCandidateInfo)result).isVarargs());
topLevelSession.infer(topLevelParameters, topLevelArguments, topLevelCall, ((MethodCandidateInfo)result).createProperties());
final Map<PsiElement, InferenceSession> nestedSessions = topLevelSession.getInferenceSessionContainer().myNestedSessions;
Map<PsiElement, InitialInferenceState> nestedStates = new LinkedHashMap<PsiElement, InitialInferenceState>();
for (Map.Entry<PsiElement, InferenceSession> entry : nestedSessions.entrySet()) {
nestedStates.put(entry.getKey(), entry.getValue().createInitialState());
}
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
for (InferenceVariable variable : topLevelSession.getInferenceVariables()) {
final PsiType instantiation = variable.getInstantiation();
if (instantiation != PsiType.NULL) {
substitutor = substitutor.put(variable, instantiation);
}
}
return Pair.create(substitutor, nestedStates);
}
}
return null;
}
@Nullable
private static PsiCall treeWalkUp(PsiElement context) {
PsiCall top = null;
PsiElement parent = PsiTreeUtil.getParentOfType(context, PsiExpressionList.class, PsiLambdaExpression.class, PsiCodeBlock.class);
while (true) {
if (parent instanceof PsiCodeBlock && PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class) == null) {
break;
}
if (parent instanceof PsiLambdaExpression) {
boolean inReturnExpressions = false;
for (PsiExpression expression : LambdaUtil.getReturnExpressions((PsiLambdaExpression)parent)) {
inReturnExpressions |= PsiTreeUtil.isAncestor(expression, context, false);
}
if (!inReturnExpressions) {
break;
}
}
final PsiCall psiCall = PsiTreeUtil.getParentOfType(parent, PsiCall.class);
if (psiCall == null) {
break;
}
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(psiCall.getArgumentList());
if (properties != null && properties.isApplicabilityCheck()) {
break;
}
top = psiCall;
if (top instanceof PsiExpression && PsiPolyExpressionUtil.isPolyExpression((PsiExpression)top)) {
parent = PsiTreeUtil.getParentOfType(parent, PsiExpressionList.class, PsiLambdaExpression.class, PsiCodeBlock.class);
}
else {
break;
}
}
if (top == null) {
return null;
}
final PsiExpressionList argumentList = top.getArgumentList();
if (argumentList == null) {
return null;
}
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList);
if (properties != null) {
return null;
}
return top;
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Set;
public class InitialInferenceState {
private final Set<InferenceVariable> myInferenceVariables;
private final PsiElement myContext;
private final PsiSubstitutor myInferenceSubstitutor;
private final PsiSubstitutor mySiteSubstitutor;
private final List<Pair<PsiTypeParameter[], PsiClassType>> myCaptures;
private final InferenceSessionContainer myInferenceSessionContainer;
public InitialInferenceState(Set<InferenceVariable> inferenceVariables,
PsiElement context,
PsiSubstitutor inferenceSubstitutor,
PsiSubstitutor siteSubstitutor,
List<Pair<PsiTypeParameter[], PsiClassType>> captures,
InferenceSessionContainer inferenceSessionContainer) {
myInferenceVariables = inferenceVariables;
myContext = context;
myInferenceSubstitutor = inferenceSubstitutor;
mySiteSubstitutor = siteSubstitutor;
myCaptures = captures;
myInferenceSessionContainer = inferenceSessionContainer;
}
@NotNull
static PsiSubstitutor copyVariables(List<InferenceVariable> targetVars,
Set<InferenceVariable> inferenceVariables,
PsiElement context) {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final InferenceVariable[] oldVars = inferenceVariables.toArray(new InferenceVariable[inferenceVariables.size()]);
for (InferenceVariable variable : oldVars) {
final InferenceVariable newVariable = new InferenceVariable(context, variable.getParameter());
substitutor = substitutor.put(variable, JavaPsiFacade.getElementFactory(variable.getProject()).createType(newVariable));
targetVars.add(newVariable);
}
for (int i = 0; i < targetVars.size(); i++) {
InferenceVariable var = targetVars.get(i);
for (InferenceBound boundType : InferenceBound.values()) {
for (PsiType bound : oldVars[i].getBounds(boundType)) {
var.addBound(substitutor.substitute(bound), boundType);
}
}
}
return substitutor;
}
public InferenceSessionContainer getInferenceSessionContainer() {
return myInferenceSessionContainer;
}
public Set<InferenceVariable> getInferenceVariables() {
return myInferenceVariables;
}
public PsiElement getContext() {
return myContext;
}
public PsiSubstitutor getInferenceSubstitutor() {
return myInferenceSubstitutor;
}
public PsiSubstitutor getSiteSubstitutor() {
return mySiteSubstitutor;
}
public List<Pair<PsiTypeParameter[], PsiClassType>> getCaptures() {
return myCaptures;
}
}
@@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.resolve.graphInference;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,9 +39,22 @@ public class PsiGraphInferenceHelper implements PsiInferenceHelper {
@NotNull PsiSubstitutor partialSubstitutor,
@Nullable PsiElement parent,
@NotNull ParameterTypeInferencePolicy policy) {
final InferenceSession inferenceSession = new InferenceSession(new PsiTypeParameter[]{typeParameter}, partialSubstitutor, myManager, parent);
inferenceSession.initExpressionConstraints(parameters, arguments, parent, null);
return inferenceSession.infer(parameters, arguments, parent).substitute(typeParameter);
final PsiSubstitutor substitutor;
if (parent != null) {
substitutor = inferTypeArguments(new PsiTypeParameter[]{typeParameter},
parameters,
arguments,
partialSubstitutor,
parent,
policy,
PsiUtil.getLanguageLevel(parent));
}
else {
final InferenceSession inferenceSession = new InferenceSession(new PsiTypeParameter[]{typeParameter}, partialSubstitutor, myManager, null);
inferenceSession.initExpressionConstraints(parameters, arguments, null, null);
substitutor = inferenceSession.infer();
}
return substitutor.substitute(typeParameter);
}
@NotNull
@@ -53,9 +67,8 @@ public class PsiGraphInferenceHelper implements PsiInferenceHelper {
@NotNull ParameterTypeInferencePolicy policy,
@NotNull LanguageLevel languageLevel) {
if (typeParameters.length == 0) return partialSubstitutor;
final InferenceSession inferenceSession = new InferenceSession(typeParameters, partialSubstitutor, myManager, parent);
inferenceSession.initExpressionConstraints(parameters, arguments, parent, null);
return inferenceSession.infer(parameters, arguments, parent);
return InferenceSessionContainer.infer(typeParameters, parameters, arguments, partialSubstitutor, parent);
}
@NotNull
@@ -82,58 +82,13 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
}
if (myExpression instanceof PsiCall) {
final PsiExpressionList argumentList = ((PsiCall)myExpression).getArgumentList();
if (argumentList != null) {
final MethodCandidateInfo.CurrentCandidateProperties candidateProperties = MethodCandidateInfo.getCurrentMethod(argumentList);
PsiType returnType = null;
PsiTypeParameter[] typeParams = null;
final JavaResolveResult resolveResult = candidateProperties != null ? null : InferenceSession.getResolveResult((PsiCall)myExpression);
PsiMethod method = null;
if (candidateProperties != null) {
method = candidateProperties.getMethod();
}
else {
final PsiElement element = resolveResult.getElement();
if (element instanceof PsiMethod) {
method = (PsiMethod)element;
}
}
if (method != null && !method.isConstructor()) {
returnType = method.getReturnType();
if (returnType != null) {
typeParams = method.getTypeParameters();
}
}
else if (resolveResult != null) {
final PsiClass psiClass = method != null ? method.getContainingClass() : (PsiClass)resolveResult.getElement();
if (psiClass != null) {
returnType = JavaPsiFacade.getElementFactory(argumentList.getProject()).createType(psiClass, PsiSubstitutor.EMPTY);
typeParams = psiClass.getTypeParameters();
}
}
if (typeParams != null) {
PsiSubstitutor siteSubstitutor = InferenceSession.chooseSiteSubstitutor(candidateProperties, resolveResult, method);
final InferenceSession callSession = new InferenceSession(typeParams, siteSubstitutor, myExpression.getManager(), myExpression);
callSession.propagateVariables(session.getInferenceVariables());
if (method != null) {
final PsiExpression[] args = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();
callSession.initExpressionConstraints(parameters, args, myExpression, method, InferenceSession
.chooseVarargsMode(candidateProperties, resolveResult));
}
final boolean accepted = callSession.repeatInferencePhases(true);
if (!accepted) {
return false;
}
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), myT);
if (callSession.repeatInferencePhases(true)) {
session.getInferenceSessionContainer().registerNestedSession(session, callSession);
} else {
return false;
}
}
final InferenceSession callSession = reduceExpressionCompatibilityConstraint(session, myExpression, myT);
if (callSession == null) {
return false;
}
if (callSession != session) {
session.getInferenceSessionContainer().registerNestedSession(callSession);
session.propagateVariables(callSession.getInferenceVariables());
}
return true;
}
@@ -152,6 +107,57 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
return true;
}
public static InferenceSession reduceExpressionCompatibilityConstraint(InferenceSession session,
PsiExpression expression,
PsiType targetType) {
final PsiExpressionList argumentList = ((PsiCall)expression).getArgumentList();
if (argumentList != null) {
final MethodCandidateInfo.CurrentCandidateProperties candidateProperties = MethodCandidateInfo.getCurrentMethod(argumentList);
PsiType returnType = null;
PsiTypeParameter[] typeParams = null;
final JavaResolveResult resolveResult = candidateProperties != null ? null : InferenceSession.getResolveResult((PsiCall)expression);
final PsiMethod method = InferenceSession.getCalledMethod((PsiCall)expression);
if (method != null && !method.isConstructor()) {
returnType = method.getReturnType();
if (returnType != null) {
typeParams = method.getTypeParameters();
}
}
else if (resolveResult != null) {
final PsiClass psiClass = method != null ? method.getContainingClass() : (PsiClass)resolveResult.getElement();
if (psiClass != null) {
returnType = JavaPsiFacade.getElementFactory(argumentList.getProject()).createType(psiClass, PsiSubstitutor.EMPTY);
typeParams = psiClass.getTypeParameters();
}
}
if (typeParams != null) {
PsiSubstitutor siteSubstitutor = InferenceSession.chooseSiteSubstitutor(candidateProperties, resolveResult, method);
final InferenceSession callSession = new InferenceSession(typeParams, siteSubstitutor, expression.getManager(), expression);
callSession.propagateVariables(session.getInferenceVariables());
if (method != null) {
final PsiExpression[] args = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();
callSession.initExpressionConstraints(parameters, args, expression, method, InferenceSession
.chooseVarargsMode(candidateProperties, resolveResult));
}
final boolean accepted = callSession.repeatInferencePhases(true);
if (!accepted) {
return null;
}
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), targetType);
if (callSession.repeatInferencePhases(true)) {
return callSession;
}
else {
return null;
}
}
}
return session;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -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.impl.source.resolve.graphInference.InferenceSessionContainer;
import com.intellij.psi.util.PsiUtil;
import java.util.List;
@@ -69,6 +70,14 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
constraints.add(new ExpressionCompatibilityConstraint(returnExpression, returnType));
}
}
else {
for (PsiExpression returnExpression : returnExpressions) {
if (returnExpression instanceof PsiCallExpression) {
final InferenceSessionContainer sessionContainer = session.getInferenceSessionContainer();
sessionContainer.registerNestedSession(session, returnType, returnExpression);
}
}
}
}
}
return true;
@@ -10,7 +10,7 @@ class NachCollections<K,V> {
Collection<? super Map.Entry<K,V>> c2,
Consumer<Map.Entry<K, V>> a) {
c1.forEach(consumer(a));
c2.forEach(consumer<error descr="'consumer(java.util.function.Consumer<java.util.Map.Entry<K1,V1>>)' in 'NachCollections' cannot be applied to '(java.util.function.Consumer<java.util.Map.Entry<K,V>>)'">(a)</error>);
c2.forEach<error descr="'forEach(java.util.function.Consumer<capture<? super java.util.Map.Entry<K,V>>>)' in 'java.lang.Iterable' cannot be applied to '(java.util.function.Consumer<java.util.Map.Entry<K,V>>)'">(consumer(a))</error>;
}
}
@@ -20,7 +20,7 @@ class TestIDEA128101 {
public static void test() {
construct(String.class, createPath(integerAttribute), createPath(stringAttribute));
construct1(String.class, createPath<error descr="'createPath(TestIDEA128101.Attribute<Y>)' in 'TestIDEA128101' cannot be applied to '(TestIDEA128101.Attribute<java.lang.Integer>)'">(integerAttribute)</error>, createPath<error descr="'createPath(TestIDEA128101.Attribute<Y>)' in 'TestIDEA128101' cannot be applied to '(TestIDEA128101.Attribute<java.lang.String>)'">(stringAttribute)</error>);
construct1(String.class, createPath<error descr="'createPath(TestIDEA128101.Attribute<Y>)' in 'TestIDEA128101' cannot be applied to '(TestIDEA128101.Attribute<java.lang.Integer>)'">(integerAttribute)</error>, createPath(stringAttribute));
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>
@@ -29,7 +29,7 @@ public class ConcurrentCollectors {
static <T, K, D, M1 extends Map<K, D>> C<T, M1> groupingBy(F<M1> f,
C<T, D> c,
BiConsumer<M1, T> consumer) {
return new CImpl<>(f, consumer, arg<error descr="'arg(ConcurrentCollectors.BiOp<V>)' in 'ConcurrentCollectors.Test3' cannot be applied to '(ConcurrentCollectors.BiOp<D>)'">(c.getOp())</error>);
return new CImpl<><error descr="'CImpl(ConcurrentCollectors.F<M1>, ConcurrentCollectors.BiConsumer<M1,T>, ConcurrentCollectors.BiOp<M1>)' in 'ConcurrentCollectors.CImpl' cannot be applied to '(ConcurrentCollectors.F<M1>, ConcurrentCollectors.BiConsumer<M1,T>, ConcurrentCollectors.BiOp<ConcurrentCollectors.ConcurrentMap<java.lang.Object,D>>)'">(f, consumer, arg(c.getOp()))</error>;
}
static <K, V, M2 extends ConcurrentMap<K, V>> BiOp<M2> arg(BiOp<V> op) {
@@ -32,7 +32,7 @@ class Test1 {
}
{
bar(l -> baz<error descr="'baz(java.lang.Object)' in 'Test1' cannot be applied to '(<lambda parameter>)'">(l)</error>);
bar(l -> <error descr="Unhandled exception: Test1.MyEx">baz(l)</error>);
bar(<error descr="Unhandled exception: Test1.MyEx">this::baz</error>);
}
}
@@ -28,7 +28,7 @@ abstract class NoFormalParamTypeInferenceNeeded {
zip(a -> zip(text -> text));
Integer zip = zip(a -> zip(<error descr="inference variable R has incompatible bounds:
lower bounds: Object
upper bounds: Object, R">text -> text</error>));
upper bounds: Object, R, Integer">text -> text</error>));
}
}
@@ -6,7 +6,7 @@ public class Bug
{
final I<CRN> f = null;
Bug.<String>create(fn<error descr="'fn(Bug.I<FN>)' in 'Bug' cannot be applied to '(Bug.I<CRN>)'">(f)</error>);
Bug.<String>create<error descr="'create(Bug.I<java.lang.String>)' in 'Bug' cannot be applied to '(Bug.I<CRN>)'">(fn(f))</error>;
return create(fn(f));