new inference: cache substitutor for outer call conflict resolution

This commit is contained in:
Anna Kozlova
2014-03-07 14:09:39 +01:00
parent a5adf26660
commit dccb826526
9 changed files with 164 additions and 74 deletions

View File

@@ -18,7 +18,6 @@ package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
@@ -312,6 +311,14 @@ public class LambdaUtil {
if (gParent instanceof PsiCall) {
final PsiCall contextCall = (PsiCall)gParent;
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(contextCall.getArgumentList());
if (properties != null && properties.isApplicabilityCheck()) { //todo simplification
final PsiParameter[] parameters = properties.getMethod().getParameterList().getParameters();
final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, properties.getMethod(), parameters);
if (finalLambdaIdx < parameters.length) {
return properties.getSubstitutor().substitute(getNormalizedType(parameters[finalLambdaIdx]));
}
}
final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
final PsiElement resolve = resolveResult.getElement();
if (resolve instanceof PsiMethod) {
@@ -462,8 +469,8 @@ public class LambdaUtil {
if (parent instanceof PsiExpressionList) {
final PsiElement gParent = parent.getParent();
if (gParent instanceof PsiCall) {
final Pair<PsiMethod, PsiSubstitutor> pair = MethodCandidateInfo.getCurrentMethod(parent);
myMethod = pair != null ? pair.first : null;
final MethodCandidateInfo.CurrentCandidateProperties pair = MethodCandidateInfo.getCurrentMethod(parent);
myMethod = pair != null ? pair.getMethod() : null;
if (myMethod == null) {
myMethod = ((PsiCall)gParent).resolveMethod();
}

View File

@@ -19,7 +19,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.pom.java.LanguageLevel;
@@ -39,7 +38,7 @@ import java.util.Map;
*/
public class MethodCandidateInfo extends CandidateInfo{
public static final RecursionGuard ourOverloadGuard = RecursionManager.createGuard("overload.guard");
public static final ThreadLocal<Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>>> CURRENT_CANDIDATE = new ThreadLocal<Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>>>();
public static final ThreadLocal<Map<PsiElement, CurrentCandidateProperties>> CURRENT_CANDIDATE = new ThreadLocal<Map<PsiElement, CurrentCandidateProperties>>();
@ApplicabilityLevelConstant
private int myApplicabilityLevel; // benign race
private final PsiElement myArgumentList;
@@ -102,27 +101,39 @@ public class MethodCandidateInfo extends CandidateInfo{
@ApplicabilityLevelConstant
public int getPertinentApplicabilityLevel() {
if (myTypeArguments != null) {
return getApplicabilityLevel();
}
@ApplicabilityLevelConstant int level;
Integer boxedLevel = ourOverloadGuard.doPreventingRecursion(myArgumentList, false, new Computable<Integer>() {
@Override
public Integer compute() {
if (PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
final PsiType[] argumentTypes = getArgumentTypes();
if (argumentTypes == null) {
return ApplicabilityLevel.NOT_APPLICABLE;
PsiSubstitutor substitutor = getSubstitutor(false);
Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
if (map == null) {
map = new ConcurrentWeakHashMap<PsiElement, CurrentCandidateProperties>();
CURRENT_CANDIDATE.set(map);
}
final PsiMethod method = getElement();
final CurrentCandidateProperties properties = new CurrentCandidateProperties(method, substitutor, false, true);
final CurrentCandidateProperties alreadyThere = map.put(getMarkerList(), properties);
try {
properties.setSubstitutor(substitutor);
PsiType[] argumentTypes = getArgumentTypes();
if (argumentTypes == null) {
return ApplicabilityLevel.NOT_APPLICABLE;
}
return PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(false), argumentTypes, myLanguageLevel);
return PsiUtil.getApplicabilityLevel(method, substitutor, argumentTypes, myLanguageLevel);
}
finally {
if (alreadyThere == null) map.remove(getMarkerList());
}
}
return getApplicabilityLevelInner();
}
});
level = boxedLevel != null ? boxedLevel : getApplicabilityLevel();
assert boxedLevel != null;
level = boxedLevel;
if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(false)) level = ApplicabilityLevel.NOT_APPLICABLE;
return level;
}
@@ -158,7 +169,7 @@ public class MethodCandidateInfo extends CandidateInfo{
final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint);
if (!stackStamp.mayCacheNow() || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
if (!stackStamp.mayCacheNow() || !ourOverloadGuard.currentStack().isEmpty() || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
return inferredSubstitutor;
}
@@ -225,15 +236,15 @@ public class MethodCandidateInfo extends CandidateInfo{
public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy,
@NotNull PsiExpression[] arguments,
boolean includeReturnConstraint) {
Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> map = CURRENT_CANDIDATE.get();
Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
if (map == null) {
map = new ConcurrentWeakHashMap<PsiElement, Pair<PsiMethod, PsiSubstitutor>>();
map = new ConcurrentWeakHashMap<PsiElement, CurrentCandidateProperties>();
CURRENT_CANDIDATE.set(map);
}
final PsiMethod method = getElement();
final Pair<PsiMethod, PsiSubstitutor> alreadyThere = includeReturnConstraint
? map.put(getMarkerList(), Pair.create(method, super.getSubstitutor()))
: null;
final CurrentCandidateProperties alreadyThere = map.put(getMarkerList(),
new CurrentCandidateProperties(method, super.getSubstitutor(), false, //todo
!includeReturnConstraint));
try {
PsiTypeParameter[] typeParameters = method.getTypeParameters();
@@ -267,17 +278,17 @@ public class MethodCandidateInfo extends CandidateInfo{
}
public static Pair<PsiMethod, PsiSubstitutor> getCurrentMethod(PsiElement context) {
final Map<PsiElement,Pair<PsiMethod,PsiSubstitutor>> currentMethodCandidates = CURRENT_CANDIDATE.get();
public static CurrentCandidateProperties getCurrentMethod(PsiElement context) {
final Map<PsiElement, CurrentCandidateProperties> currentMethodCandidates = CURRENT_CANDIDATE.get();
return currentMethodCandidates != null ? currentMethodCandidates.get(context) : null;
}
public static void updateSubstitutor(PsiElement context, PsiSubstitutor newSubstitutor) {
final Map<PsiElement,Pair<PsiMethod,PsiSubstitutor>> currentMethodCandidates = CURRENT_CANDIDATE.get();
final Map<PsiElement, CurrentCandidateProperties> currentMethodCandidates = CURRENT_CANDIDATE.get();
if (currentMethodCandidates != null) {
final Pair<PsiMethod, PsiSubstitutor> pair = currentMethodCandidates.get(context);
if (pair != null) {
currentMethodCandidates.put(context, Pair.create(pair.first, newSubstitutor));
final CurrentCandidateProperties properties = currentMethodCandidates.get(context);
if (properties != null) {
properties.setSubstitutor(newSubstitutor);
}
}
}
@@ -286,6 +297,48 @@ public class MethodCandidateInfo extends CandidateInfo{
return myArgumentTypes;
}
public static class CurrentCandidateProperties {
private final PsiMethod myMethod;
private PsiSubstitutor mySubstitutor;
private boolean myVarargs;
private boolean myApplicabilityCheck;
public CurrentCandidateProperties(PsiMethod method, PsiSubstitutor substitutor, boolean varargs, boolean applicabilityCheck) {
myMethod = method;
mySubstitutor = substitutor;
myVarargs = varargs;
myApplicabilityCheck = applicabilityCheck;
}
public PsiMethod getMethod() {
return myMethod;
}
public PsiSubstitutor getSubstitutor() {
return mySubstitutor;
}
public void setSubstitutor(PsiSubstitutor substitutor) {
mySubstitutor = substitutor;
}
public boolean isVarargs() {
return myVarargs;
}
public void setVarargs(boolean varargs) {
myVarargs = varargs;
}
public boolean isApplicabilityCheck() {
return myApplicabilityCheck && !ourOverloadGuard.currentStack().isEmpty();
}
public void setApplicabilityCheck(boolean applicabilityCheck) {
myApplicabilityCheck = applicabilityCheck;
}
}
public static class ApplicabilityLevel {
public static final int NOT_APPLICABLE = 1;
public static final int VARARGS = 2;

View File

@@ -30,6 +30,7 @@ import com.intellij.psi.impl.AnyPsiChangeListener;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.Function;
@@ -90,7 +91,7 @@ public class JavaResolveCache {
final RecursionGuard.StackStamp dStackStamp = PsiDiamondType.ourDiamondGuard.markStack();
final RecursionGuard.StackStamp gStackStamp = PsiResolveHelper.ourGraphGuard.markStack();
type = f.fun(expr);
if (!dStackStamp.mayCacheNow() || !gStackStamp.mayCacheNow()) {
if (!dStackStamp.mayCacheNow() || !gStackStamp.mayCacheNow() || !MethodCandidateInfo.ourOverloadGuard.currentStack().isEmpty()) {
return type;
}
if (type == null) type = TypeConversionUtil.NULL_TYPE;

View File

@@ -91,9 +91,9 @@ public class InferenceSession {
public void initExpressionConstraints(PsiParameter[] parameters, PsiExpression[] args, PsiElement parent, PsiMethod method) {
if (method == null) {
final Pair<PsiMethod, PsiCallExpression> pair = getPair(parent);
final MethodCandidateInfo.CurrentCandidateProperties pair = getCurrentProperties(parent);
if (pair != null) {
method = pair.first;
method = pair.getMethod();
}
}
if (parameters.length > 0) {
@@ -106,12 +106,9 @@ public class InferenceSession {
}
}
private static Pair<PsiMethod, PsiCallExpression> getPair(PsiElement parent) {
private static MethodCandidateInfo.CurrentCandidateProperties getCurrentProperties(PsiElement parent) {
if (parent instanceof PsiCallExpression) {
final Pair<PsiMethod, PsiSubstitutor> pair = MethodCandidateInfo.getCurrentMethod(((PsiCallExpression)parent).getArgumentList());
if (pair != null) {
return Pair.create(pair.first, (PsiCallExpression)parent);
}
return MethodCandidateInfo.getCurrentMethod(((PsiCallExpression)parent).getArgumentList());
}
return null;
}
@@ -167,7 +164,7 @@ public class InferenceSession {
return true;
}
private static PsiType getParameterType(PsiParameter[] parameters, PsiExpression[] args, int i, @Nullable PsiSubstitutor substitutor) {
private PsiType getParameterType(PsiParameter[] parameters, PsiExpression[] args, int i, @Nullable PsiSubstitutor substitutor) {
if (substitutor == null) return null;
PsiType parameterType = substitutor.substitute(parameters[i < parameters.length ? i : parameters.length - 1].getType());
if (parameterType instanceof PsiEllipsisType) {
@@ -204,14 +201,14 @@ public class InferenceSession {
private PsiSubstitutor tryToInfer(@Nullable PsiParameter[] parameters,
@Nullable PsiExpression[] args,
@Nullable PsiCallExpression parent,
PsiMethod parentMethod) {
@Nullable MethodCandidateInfo.CurrentCandidateProperties properties) {
if (!repeatInferencePhases(true)) {
//inferred result would be checked as candidate won't be applicable
return resolveSubset(myInferenceVariables.values(), mySiteSubstitutor);
}
if (parentMethod != null) {
initReturnTypeConstraint(parentMethod, parent);
if (properties != null && !properties.isApplicabilityCheck()) {
initReturnTypeConstraint(properties.getMethod(), parent);
if (!repeatInferencePhases(true)) {
return prepareSubstitution();
}
@@ -219,7 +216,7 @@ public class InferenceSession {
if (parameters != null && args != null) {
final Set<ConstraintFormula> additionalConstraints = new HashSet<ConstraintFormula>();
if (parameters.length > 0) {
collectAdditionalConstraints(parameters, args, parentMethod, PsiSubstitutor.EMPTY, additionalConstraints);
collectAdditionalConstraints(parameters, args, properties.getMethod(), PsiSubstitutor.EMPTY, additionalConstraints);
}
if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints)) {
@@ -231,11 +228,11 @@ public class InferenceSession {
return null;
}
private static void collectAdditionalConstraints(PsiParameter[] parameters,
PsiExpression[] args,
PsiMethod parentMethod,
PsiSubstitutor siteSubstitutor,
Set<ConstraintFormula> additionalConstraints) {
private void collectAdditionalConstraints(PsiParameter[] parameters,
PsiExpression[] args,
PsiMethod parentMethod,
PsiSubstitutor siteSubstitutor,
Set<ConstraintFormula> additionalConstraints) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
PsiType parameterType = getParameterType(parameters, args, i, siteSubstitutor);
@@ -269,13 +266,8 @@ public class InferenceSession {
public PsiSubstitutor infer(@Nullable PsiParameter[] parameters,
@Nullable PsiExpression[] args,
@Nullable PsiElement parent) {
final Pair<PsiMethod, PsiCallExpression> pair = getPair(parent);
return infer(parameters, args, parent, pair != null ? pair.first : null);
}
@NotNull
public PsiSubstitutor infer(PsiParameter[] parameters, PsiExpression[] args, PsiElement parent, PsiMethod parentMethod) {
final PsiSubstitutor subst = tryToInfer(parameters, args, parent instanceof PsiCallExpression ? ((PsiCallExpression)parent) : null, parentMethod);
final MethodCandidateInfo.CurrentCandidateProperties properties = getCurrentProperties(parent);
final PsiSubstitutor subst = tryToInfer(parameters, args, parent instanceof PsiCallExpression ? ((PsiCallExpression)parent) : null, properties);
if (subst != null) {
return subst;
}
@@ -483,7 +475,7 @@ public class InferenceSession {
return false;
}
private static PsiType getTargetType(final PsiExpression context) {
private PsiType getTargetType(final PsiExpression context) {
final PsiElement parent = PsiUtil.skipParenthesizedExprUp(context.getParent());
if (parent instanceof PsiExpressionList) {
PsiElement gParent = parent.getParent();
@@ -493,8 +485,22 @@ public class InferenceSession {
if (gParent instanceof PsiCallExpression) {
final PsiExpressionList argumentList = ((PsiCallExpression)gParent).getArgumentList();
if (argumentList != null) {
final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList);
if (properties != null && properties.isApplicabilityCheck()) {
return getTypeByMethod(context, argumentList, properties.getMethod(), properties.getSubstitutor());
}
final JavaResolveResult result = ((PsiCallExpression)gParent).resolveMethodGenerics();
return getTypeByMethod(context, argumentList, result, result.getElement());
return getTypeByMethod(context, argumentList, result.getElement(),
PsiResolveHelper.ourGraphGuard.doPreventingRecursion(argumentList.getParent(), false,
new Computable<PsiSubstitutor>() {
@Override
public PsiSubstitutor compute() {
return result
.getSubstitutor();
}
}
)
);
}
}
} else if (parent instanceof PsiConditionalExpression) {
@@ -514,9 +520,9 @@ public class InferenceSession {
return null;
}
private static PsiType getTypeByMethod(PsiExpression context,
PsiExpressionList argumentList,
final JavaResolveResult result, PsiElement parentMethod) {
private PsiType getTypeByMethod(PsiExpression context,
PsiExpressionList argumentList,
PsiElement parentMethod, PsiSubstitutor substitutor) {
if (parentMethod instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)parentMethod).getParameterList().getParameters();
if (parameters.length == 0) return null;
@@ -528,13 +534,7 @@ public class InferenceSession {
}
final int i = ArrayUtilRt.find(args, arg);
if (i < 0) return null;
return getParameterType(parameters, args, i, PsiResolveHelper.ourGraphGuard.doPreventingRecursion(argumentList.getParent(), false,
new Computable<PsiSubstitutor>() {
@Override
public PsiSubstitutor compute() {
return result.getSubstitutor();
}
}));
return getParameterType(parameters, args, i, substitutor);
}
return null;
}

View File

@@ -18,7 +18,6 @@ package com.intellij.psi.impl.source.tree.java;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
@@ -51,7 +50,10 @@ import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl");
@@ -456,7 +458,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
@NotNull
@Override
public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
return inferTypeArguments(false);
return inferTypeArguments(false); //includeReturnConstraint == vararg todo
}
public PsiSubstitutor inferTypeArguments(boolean varargs) {
@@ -465,9 +467,9 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final InferenceSession session = new InferenceSession(method.getTypeParameters(), substitutor, getManager(), reference);
//lift parameters from outer call
final Pair<PsiMethod,PsiSubstitutor> methodSubstitutorPair = MethodCandidateInfo.getCurrentMethod(reference.getParent());
final CurrentCandidateProperties methodSubstitutorPair = MethodCandidateInfo.getCurrentMethod(reference.getParent());
if (methodSubstitutorPair != null) {
session.initBounds(methodSubstitutorPair.first.getTypeParameters());
session.initBounds(methodSubstitutorPair.getMethod().getTypeParameters());
}
final PsiParameter[] functionalMethodParameters = interfaceMethod.getParameterList().getParameters();

View File

@@ -7,8 +7,8 @@ public class Sample {
<B> B bar(G<B> gb) {return null;}
void f(G1 g1) {
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'Sample.G<java.lang.String>'">G<String> l11 = bar(g1);</error>
<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.String'">String l1 = bar(g1);</error>
<error descr="Incompatible types. Found: 'B', required: 'Sample.G<java.lang.String>'">G<String> l11 = bar(g1);</error>
<error descr="Incompatible types. Found: 'B', required: 'java.lang.String'">String l1 = bar(g1);</error>
Object o = bar(g1);
}
}

View File

@@ -13,12 +13,12 @@ class SortedOp<T> implements StatefulOp<T, T> {
}
}
class Usage<T> {
public <E, S extends BaseStream<E>> S pipeline(IntermediateOp<T, E> newOp) { return null; }
public <R> R pipeline(TerminalOp<T, R> terminal) { return null;}
class Usage<Ts> {
public <E, S extends BaseStream<E>> S pipeline(IntermediateOp<Ts, E> newOp) { return null; }
public <R> R pipeline(TerminalOp<Ts, R> terminal) { return null;}
public Stream<T> sorted(Comparator<? super T> comparator) {
public Stream<Ts> sorted(Comparator<? super Ts> comparator) {
return pipeline(new SortedOp<>(comparator));
}
}

View File

@@ -0,0 +1,23 @@
import java.util.*;
class Test {
void m(Runnable p) { }
void m(List<Runnable> p) { }
{
m(foo());
m<error descr="Cannot resolve method 'm(java.lang.Object)'">(bar())</error>;
}
<T> List<T> foo() {
return null;
}
<T> T bar() {
return null;
}
}

View File

@@ -172,6 +172,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
doTest();
}
public void testOuterCallConflictResolution() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false);
}