@Contract("_, !null -> !null") public PsiSubstitutor findNestedSubstitutor( PsiElement arg, @Nullable PsiSubstitutor defaultSession) { InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(arg, PsiCall.class)); return session == null ? defaultSession : session.getInferenceSubstitution(); }
public Set<InferenceVariable> getDependencies(InferenceSession session) { final Set<InferenceVariable> dependencies = new LinkedHashSet<InferenceVariable>(); for (List<PsiType> boundTypes : myBounds.values()) { if (boundTypes != null) { for (PsiType bound : boundTypes) { session.collectDependencies(bound, dependencies); } } } if (!session.hasCapture(this) && dependencies.isEmpty()) { return dependencies; } if (!session.hasCapture(this)) { return dependencies; } for (Iterator<InferenceVariable> iterator = dependencies.iterator(); iterator.hasNext(); ) { if (!session.hasCapture(iterator.next())) { iterator.remove(); } } session.collectCaptureDependencies(this, dependencies); return dependencies; }
private static CompoundInitialState createState(InferenceSession topLevelSession) { final PsiSubstitutor topInferenceSubstitutor = replaceVariables(topLevelSession.getInferenceVariables()); final Map<PsiElement, InitialInferenceState> nestedStates = new LinkedHashMap<PsiElement, InitialInferenceState>(); final InferenceSessionContainer copy = new InferenceSessionContainer() { @Override public PsiSubstitutor findNestedSubstitutor( PsiElement arg, @Nullable PsiSubstitutor defaultSession) { // for the case foo(bar(a -> m())): top level inference won't touch lambda "a -> m()" // for the case foo(a -> bar(b -> m())): top level inference would go till nested lambda // "b -> m()" and the state from top level could be found here by "bar(b -> m())" // but proceeding with additional constraints from saved point would produce new // expression constraints with different inference variables (could be found in // myNestedSessions) // which won't be found in the system if we won't reject stored sessions in such cases final PsiSubstitutor substitutor = super.findNestedSubstitutor(arg, null); if (substitutor != null) { return substitutor; } final InitialInferenceState state = nestedStates.get(PsiTreeUtil.getParentOfType(arg, PsiCall.class)); if (state != null) { return state.getInferenceSubstitutor(); } return super.findNestedSubstitutor(arg, defaultSession); } }; final Map<PsiElement, InferenceSession> nestedSessions = topLevelSession.getInferenceSessionContainer().myNestedSessions; for (Map.Entry<PsiElement, InferenceSession> entry : nestedSessions.entrySet()) { nestedStates.put( entry.getKey(), entry .getValue() .createInitialState( copy, topLevelSession.getInferenceVariables(), topInferenceSubstitutor)); } PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; for (InferenceVariable variable : topLevelSession.getInferenceVariables()) { final PsiType instantiation = variable.getInstantiation(); if (instantiation != PsiType.NULL) { final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(topInferenceSubstitutor.substitute(variable)); if (psiClass instanceof InferenceVariable) { substitutor = substitutor.put((PsiTypeParameter) psiClass, instantiation); } } } return new CompoundInitialState(substitutor, nestedStates); }
protected void collectAllVariablesOnBothSides( Set<InferenceVariable> dependencies, Pair<PsiTypeParameter[], PsiClassType> capture) { mySession.collectDependencies(capture.second, dependencies); for (PsiTypeParameter psiTypeParameter : capture.first) { final InferenceVariable var = mySession.getInferenceVariable(psiTypeParameter); if (var != null) { dependencies.add(var); } } }
void registerNestedSession( InferenceSession session, PsiType returnType, PsiExpression returnExpression) { final PsiSubstitutor callSession = findNestedSubstitutor(((PsiCallExpression) returnExpression).getArgumentList(), null); if (callSession == null) { final InferenceSession inferenceSession = ExpressionCompatibilityConstraint.reduceExpressionCompatibilityConstraint( session, returnExpression, returnType); if (inferenceSession != null && inferenceSession != session) { registerNestedSession(inferenceSession); session.propagateVariables( inferenceSession.getInferenceVariables(), inferenceSession.getRestoreNameSubstitution()); } } }
private static Boolean isInferenceVariableOrFreshTypeParameter(PsiType eqBound) { final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(eqBound); if (psiClass instanceof InferenceVariable || psiClass instanceof PsiTypeParameter && InferenceSession.isFreshVariable((PsiTypeParameter) psiClass)) return true; return false; }
public boolean hasInstantiation(InferenceSession session) { List<PsiType> bounds = getBounds(InferenceBound.EQ); if (bounds != null) { for (PsiType bound : bounds) { if (session.isProperType(bound)) return true; } } return false; }
/** a = b imply every bound of a matches a bound of b and vice versa */ private boolean eqCrossVariables(InferenceVariable inferenceVariable, List<PsiType> eqBounds) { boolean needFurtherIncorporation = false; for (PsiType eqBound : eqBounds) { final InferenceVariable inferenceVar = mySession.getInferenceVariable(eqBound); if (inferenceVar != null) { for (InferenceBound inferenceBound : InferenceBound.values()) { for (PsiType bound : inferenceVariable.getBounds(inferenceBound)) { if (mySession.getInferenceVariable(bound) != inferenceVar) { needFurtherIncorporation |= inferenceVar.addBound(bound, inferenceBound); } } for (PsiType bound : inferenceVar.getBounds(inferenceBound)) { if (mySession.getInferenceVariable(bound) != inferenceVariable) { needFurtherIncorporation |= inferenceVariable.addBound(bound, inferenceBound); } } } } } return needFurtherIncorporation; }
boolean isFullyIncorporated() { boolean needFurtherIncorporation = false; for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) { if (inferenceVariable.getInstantiation() != PsiType.NULL) continue; final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER); needFurtherIncorporation |= crossVariables(inferenceVariable, upperBounds, lowerBounds, InferenceBound.LOWER); needFurtherIncorporation |= crossVariables(inferenceVariable, lowerBounds, upperBounds, InferenceBound.UPPER); needFurtherIncorporation |= eqCrossVariables(inferenceVariable, eqBounds); } return !needFurtherIncorporation; }
/** * If two bounds have the form α <: S and α <: T, and if for some generic class or interface, G, * there exists a supertype (4.10) of S of the form G<S1, ..., Sn> and a supertype of T of the * form G<T1, ..., Tn>, then for all i, 1 ≤ i ≤ n, if Si and Ti are types (not wildcards), the * constraint ⟨Si = Ti⟩ is implied. */ private boolean upUp(List<PsiType> upperBounds) { return InferenceSession.findParameterizationOfTheSameGenericClass( upperBounds, new Processor<Pair<PsiType, PsiType>>() { @Override public boolean process(Pair<PsiType, PsiType> pair) { final PsiType sType = pair.first; final PsiType tType = pair.second; if (!(sType instanceof PsiWildcardType) && !(tType instanceof PsiWildcardType) && sType != null && tType != null) { addConstraint(new TypeEqualityConstraint(sType, tType)); } return true; } }) != null; }
/** a < b & S <: a & b <: T imply S <: b & a <: T */ private boolean crossVariables( InferenceVariable inferenceVariable, List<PsiType> upperBounds, List<PsiType> lowerBounds, InferenceBound inferenceBound) { final InferenceBound oppositeBound = inferenceBound == InferenceBound.LOWER ? InferenceBound.UPPER : InferenceBound.LOWER; boolean result = false; for (PsiType upperBound : upperBounds) { final InferenceVariable inferenceVar = mySession.getInferenceVariable(upperBound); if (inferenceVar != null && inferenceVariable != inferenceVar) { for (PsiType lowerBound : lowerBounds) { result |= inferenceVar.addBound(lowerBound, inferenceBound); } for (PsiType varUpperBound : inferenceVar.getBounds(oppositeBound)) { result |= inferenceVariable.addBound(varUpperBound, oppositeBound); } } } return result; }
static PsiSubstitutor infer( @NotNull PsiTypeParameter[] typeParameters, @NotNull PsiParameter[] parameters, @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, @NotNull final PsiElement parent, @NotNull final ParameterTypeInferencePolicy policy) { if (parent instanceof PsiCall) { final PsiExpressionList argumentList = ((PsiCall) parent).getArgumentList(); final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList); // overload resolution can't depend on outer call => should not traverse to top if (properties != null && !properties.isApplicabilityCheck() && // in order to to avoid caching of candidates's errors on parent (!) , so check for // overload resolution is left here // But overload resolution can depend on type of lambda parameter. As it can't depend on // lambda body, // traversing down would stop at lambda level and won't take into account overloaded // method !MethodCandidateInfo.ourOverloadGuard.currentStack().contains(argumentList)) { final PsiCall topLevelCall = PsiResolveHelper.ourGraphGuard.doPreventingRecursion( parent, false, new Computable<PsiCall>() { @Override public PsiCall compute() { if (parent instanceof PsiExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression) parent)) { return null; } return LambdaUtil.treeWalkUp(parent); } }); if (topLevelCall != null) { InferenceSession session; if (MethodCandidateInfo.isOverloadCheck() || !PsiDiamondType.ourDiamondGuard.currentStack().isEmpty() || LambdaUtil.isLambdaParameterCheck()) { session = startTopLevelInference(topLevelCall, policy); } else { session = CachedValuesManager.getCachedValue( topLevelCall, new CachedValueProvider<InferenceSession>() { @Nullable @Override public Result<InferenceSession> compute() { return new Result<InferenceSession>( startTopLevelInference(topLevelCall, policy), PsiModificationTracker.MODIFICATION_COUNT); } }); if (session != null) { // reject cached top level session if it was based on wrong candidate: check nested // session if candidate (it's type parameters) are the same // such situations are avoided when overload resolution is performed // (MethodCandidateInfo.isOverloadCheck above) // but situations when client code iterates through // PsiResolveHelper.getReferencedMethodCandidates or similar are impossible to guess final Map<PsiElement, InferenceSession> sessions = session.getInferenceSessionContainer().myNestedSessions; final InferenceSession childSession = sessions.get(parent); if (childSession != null) { for (PsiTypeParameter parameter : typeParameters) { if (!childSession .getInferenceSubstitution() .getSubstitutionMap() .containsKey(parameter)) { session = startTopLevelInference(topLevelCall, policy); break; } } } } } if (session != null) { final PsiSubstitutor childSubstitutor = inferNested( typeParameters, parameters, arguments, partialSubstitutor, (PsiCall) parent, policy, properties, session); if (childSubstitutor != null) return childSubstitutor; } else if (topLevelCall instanceof PsiMethodCallExpression) { return new InferenceSession( typeParameters, partialSubstitutor, parent.getManager(), parent, policy) .prepareSubstitution(); } } } } final InferenceSession inferenceSession = new InferenceSession( typeParameters, partialSubstitutor, parent.getManager(), parent, policy); inferenceSession.initExpressionConstraints(parameters, arguments, parent); return inferenceSession.infer(parameters, arguments, parent); }
public void registerNestedSession(InferenceSession session) { myNestedSessions.put(session.getContext(), session); myNestedSessions.putAll(session.getInferenceSessionContainer().myNestedSessions); }
private static PsiSubstitutor inferNested( final PsiTypeParameter[] typeParameters, @NotNull final PsiParameter[] parameters, @NotNull final PsiExpression[] arguments, final PsiSubstitutor partialSubstitutor, @NotNull final PsiCall parent, @NotNull final ParameterTypeInferencePolicy policy, final MethodCandidateInfo.CurrentCandidateProperties properties, final InferenceSession parentSession) { final CompoundInitialState compoundInitialState = createState(parentSession); InitialInferenceState initialInferenceState = compoundInitialState.getInitialState(parent); if (initialInferenceState != null) { final InferenceSession childSession = new InferenceSession(initialInferenceState); final List<String> errorMessages = parentSession.getIncompatibleErrorMessages(); if (errorMessages != null) { return childSession.prepareSubstitution(); } return childSession.collectAdditionalAndInfer( parameters, arguments, properties, compoundInitialState.getInitialSubstitutor()); } // we do not investigate lambda return expressions when lambda's return type is already inferred // (proper) // this way all calls from lambda's return expressions won't appear in nested sessions else { PsiElement gParent = PsiUtil.skipParenthesizedExprUp(parent.getParent()); // find the nearest parent which appears in the map and start inference with a provided target // type for a nested lambda while (true) { if (gParent instanceof PsiReturnStatement) { // process code block lambda final PsiElement returnContainer = gParent.getParent(); if (returnContainer instanceof PsiCodeBlock) { gParent = returnContainer.getParent(); } } if (gParent instanceof PsiLambdaExpression) { final PsiCall call = PsiTreeUtil.getParentOfType(gParent, PsiCall.class); if (call != null) { initialInferenceState = compoundInitialState.getInitialState(call); if (initialInferenceState != null) { final int idx = LambdaUtil.getLambdaIdx(call.getArgumentList(), gParent); final PsiMethod method = call.resolveMethod(); if (method != null && idx > -1) { final PsiType parameterType = PsiTypesUtil.getParameterType( method.getParameterList().getParameters(), idx, true); final PsiType parameterTypeInTermsOfSession = initialInferenceState.getInferenceSubstitutor().substitute(parameterType); final PsiType lambdaTargetType = compoundInitialState .getInitialSubstitutor() .substitute(parameterTypeInTermsOfSession); return LambdaUtil.performWithLambdaTargetType( (PsiLambdaExpression) gParent, lambdaTargetType, new Producer<PsiSubstitutor>() { @Nullable @Override public PsiSubstitutor produce() { if (call.equals(PsiTreeUtil.getParentOfType(parent, PsiCall.class, true))) { // parent was mentioned in the top inference session // just proceed with the target type final InferenceSession inferenceSession = new InferenceSession( typeParameters, partialSubstitutor, parent.getManager(), parent, policy); inferenceSession.initExpressionConstraints(parameters, arguments, parent); return inferenceSession.infer(parameters, arguments, parent); } // one of the grand parents were found in the top inference session // start from it as it is the top level call final InferenceSession sessionInsideLambda = startTopLevelInference(call, policy); return inferNested( typeParameters, parameters, arguments, partialSubstitutor, parent, policy, properties, sessionInsideLambda); } }); } } else { gParent = PsiUtil.skipParenthesizedExprUp(call.getParent()); continue; } } } break; } } return null; }
public boolean incorporate() { final Collection<InferenceVariable> inferenceVariables = mySession.getInferenceVariables(); final PsiSubstitutor substitutor = mySession.retrieveNonPrimitiveEqualsBounds(inferenceVariables); for (InferenceVariable inferenceVariable : inferenceVariables) { if (inferenceVariable.getInstantiation() != PsiType.NULL) continue; final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER); eqEq(eqBounds); upDown(lowerBounds, upperBounds, substitutor); upDown(eqBounds, upperBounds, substitutor); upDown(lowerBounds, eqBounds, substitutor); upUp(upperBounds); } for (Pair<PsiTypeParameter[], PsiClassType> capture : myCaptures) { final PsiClassType right = capture.second; final PsiClass gClass = right.resolve(); LOG.assertTrue(gClass != null); final PsiTypeParameter[] parameters = capture.first; PsiType[] typeArgs = right.getParameters(); if (parameters.length != typeArgs.length) continue; for (int i = 0; i < typeArgs.length; i++) { PsiType aType = typeArgs[i]; if (aType instanceof PsiCapturedWildcardType) { aType = ((PsiCapturedWildcardType) aType).getWildcard(); } final InferenceVariable inferenceVariable = mySession.getInferenceVariable(parameters[i]); LOG.assertTrue(inferenceVariable != null); final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER); if (aType instanceof PsiWildcardType) { for (PsiType eqBound : eqBounds) { if (!isInferenceVariableOrFreshTypeParameter(eqBound)) return false; } final PsiClassType[] paramBounds = inferenceVariable.getParameter().getExtendsListTypes(); PsiType glb = null; for (PsiClassType paramBound : paramBounds) { if (glb == null) { glb = paramBound; } else { glb = GenericsUtil.getGreatestLowerBound(glb, paramBound); } } if (!((PsiWildcardType) aType).isBounded()) { for (PsiType upperBound : upperBounds) { if (glb != null && mySession.getInferenceVariable(upperBound) == null) { addConstraint( new StrictSubtypingConstraint( upperBound, mySession.substituteWithInferenceVariables(glb))); } } for (PsiType lowerBound : lowerBounds) { if (isInferenceVariableOrFreshTypeParameter(lowerBound)) return false; } } else if (((PsiWildcardType) aType).isExtends()) { final PsiType extendsBound = ((PsiWildcardType) aType).getExtendsBound(); for (PsiType upperBound : upperBounds) { if (mySession.getInferenceVariable(upperBound) == null) { if (paramBounds.length == 1 && paramBounds[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || paramBounds.length == 0) { addConstraint(new StrictSubtypingConstraint(upperBound, extendsBound)); } else if (extendsBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && glb != null) { addConstraint( new StrictSubtypingConstraint( upperBound, mySession.substituteWithInferenceVariables(glb))); } } } for (PsiType lowerBound : lowerBounds) { if (isInferenceVariableOrFreshTypeParameter(lowerBound)) return false; } } else { LOG.assertTrue(((PsiWildcardType) aType).isSuper()); final PsiType superBound = ((PsiWildcardType) aType).getSuperBound(); for (PsiType upperBound : upperBounds) { if (glb != null && mySession.getInferenceVariable(upperBound) == null) { addConstraint( new StrictSubtypingConstraint( mySession.substituteWithInferenceVariables(glb), upperBound)); } } for (PsiType lowerBound : lowerBounds) { if (mySession.getInferenceVariable(lowerBound) == null) { addConstraint(new StrictSubtypingConstraint(lowerBound, superBound)); } } } } else { inferenceVariable.addBound(aType, InferenceBound.EQ); } } } return true; }
private void addConstraint(ConstraintFormula constraint) { mySession.addConstraint(constraint); }