@NotNull private static PsiSubstitutor replaceVariables(Collection<InferenceVariable> inferenceVariables) { final List<InferenceVariable> targetVars = new ArrayList<InferenceVariable>(); PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; final InferenceVariable[] oldVars = inferenceVariables.toArray(new InferenceVariable[inferenceVariables.size()]); for (InferenceVariable variable : oldVars) { final InferenceVariable newVariable = new InferenceVariable( variable.getCallContext(), variable.getParameter(), variable.getName()); substitutor = substitutor.put( variable, JavaPsiFacade.getElementFactory(variable.getProject()).createType(newVariable)); targetVars.add(newVariable); if (variable.isThrownBound()) { newVariable.setThrownBound(); } } 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, null); } } } return substitutor; }
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); }
public boolean hasCaptureConstraints(Iterable<InferenceVariable> variables) { for (InferenceVariable variable : variables) { final PsiTypeParameter parameter = variable.getParameter(); for (Pair<PsiTypeParameter[], PsiClassType> capture : myCaptures) { for (PsiTypeParameter typeParameter : capture.first) { if (parameter == typeParameter) { return true; } } } } return false; }
public void forgetCaptures(List<InferenceVariable> variables) { for (InferenceVariable variable : variables) { final PsiTypeParameter parameter = variable.getParameter(); for (Iterator<Pair<PsiTypeParameter[], PsiClassType>> iterator = myCaptures.iterator(); iterator.hasNext(); ) { Pair<PsiTypeParameter[], PsiClassType> capture = iterator.next(); for (PsiTypeParameter typeParameter : capture.first) { if (parameter == typeParameter) { iterator.remove(); break; } } } } }
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; }
public void collectCaptureDependencies( InferenceVariable variable, Set<InferenceVariable> dependencies) { final PsiTypeParameter parameter = variable.getParameter(); for (Pair<PsiTypeParameter[], PsiClassType> capture : myCaptures) { for (PsiTypeParameter typeParameter : capture.first) { if (typeParameter == parameter) { collectAllVariablesOnBothSides(dependencies, capture); break; } } } }
/** 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; }
/** 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; }
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; }