2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.psi.impl.source.resolve.graphInference;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.Pair;
20 import com.intellij.openapi.util.registry.Registry;
21 import com.intellij.psi.*;
22 import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula;
23 import com.intellij.psi.impl.source.resolve.graphInference.constraints.StrictSubtypingConstraint;
24 import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeCompatibilityConstraint;
25 import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeEqualityConstraint;
26 import com.intellij.psi.util.PsiUtil;
27 import com.intellij.psi.util.TypeConversionUtil;
28 import com.intellij.util.Processor;
29 import com.intellij.util.containers.ContainerUtil;
36 public class InferenceIncorporationPhase {
37 private static final Logger LOG = Logger.getInstance("#" + InferenceIncorporationPhase.class.getName());
38 private final InferenceSession mySession;
39 private final List<Pair<InferenceVariable[], PsiClassType>> myCaptures = new ArrayList<Pair<InferenceVariable[], PsiClassType>>();
40 private final Map<InferenceVariable, Map<InferenceBound, Set<PsiType>>> myCurrentBounds =
41 new HashMap<InferenceVariable, Map<InferenceBound, Set<PsiType>>>();
43 public InferenceIncorporationPhase(InferenceSession session) {
47 public void addCapture(InferenceVariable[] typeParameters, PsiClassType rightType) {
48 myCaptures.add(Pair.create(typeParameters, rightType));
51 public void forgetCaptures(List<InferenceVariable> variables) {
52 for (InferenceVariable variable : variables) {
53 for (Iterator<Pair<InferenceVariable[], PsiClassType>> iterator = myCaptures.iterator(); iterator.hasNext(); ) {
54 Pair<InferenceVariable[], PsiClassType> capture = iterator.next();
55 if (isCapturedVariable(variable, capture)) {
62 public boolean hasCaptureConstraints(Iterable<InferenceVariable> variables) {
63 for (InferenceVariable variable : variables) {
64 for (Pair<InferenceVariable[], PsiClassType> capture : myCaptures) {
65 if (isCapturedVariable(variable, capture)) {
73 private static boolean isCapturedVariable(InferenceVariable variable, Pair<InferenceVariable[], PsiClassType> capture) {
74 for (InferenceVariable capturedVariable : capture.first) {
75 if (variable == capturedVariable){
82 public void collectCaptureDependencies(InferenceVariable variable, Set<InferenceVariable> dependencies) {
83 for (Pair<InferenceVariable[], PsiClassType> capture : myCaptures) {
84 if (isCapturedVariable(variable, capture)) {
85 mySession.collectDependencies(capture.second, dependencies);
86 ContainerUtil.addAll(dependencies, capture.first);
91 public List<Pair<InferenceVariable[], PsiClassType>> getCaptures() {
95 public boolean incorporate() {
96 final Collection<InferenceVariable> inferenceVariables = mySession.getInferenceVariables();
97 for (InferenceVariable inferenceVariable : inferenceVariables) {
98 if (inferenceVariable.getInstantiation() != PsiType.NULL) continue;
99 final Map<InferenceBound, Set<PsiType>> boundsMap = myCurrentBounds.get(inferenceVariable);
100 if (boundsMap == null) continue;
101 final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
102 final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
103 final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);
105 final Collection<PsiType> changedEqBounds = boundsMap.get(InferenceBound.EQ);
106 final Collection<PsiType> changedUpperBounds = boundsMap.get(InferenceBound.UPPER);
107 final Collection<PsiType> changedLowerBounds = boundsMap.get(InferenceBound.LOWER);
109 //no new eq constraints were added -> no new constraints could be inferred
110 if (changedEqBounds != null) {
111 eqEq(eqBounds, changedEqBounds);
114 upDown(lowerBounds, changedLowerBounds, upperBounds, changedUpperBounds);
115 upDown(eqBounds, changedEqBounds, upperBounds, changedUpperBounds);
116 upDown(lowerBounds, changedLowerBounds, eqBounds, changedEqBounds);
118 if (changedUpperBounds != null) {
123 for (Pair<InferenceVariable[], PsiClassType> capture : myCaptures) {
124 final PsiClassType right = capture.second;
125 final PsiClass gClass = right.resolve();
126 LOG.assertTrue(gClass != null);
127 final InferenceVariable[] parameters = capture.first;
128 PsiType[] typeArgs = right.getParameters();
129 if (parameters.length != typeArgs.length) continue;
130 for (int i = 0; i < typeArgs.length; i++) {
131 final PsiType aType = typeArgs[i];
132 final InferenceVariable inferenceVariable = parameters[i];
134 final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
135 final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER);
136 final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER);
138 if (aType instanceof PsiWildcardType) {
140 for (PsiType eqBound : eqBounds) {
141 if (!isInferenceVariableOrFreshTypeParameter(eqBound)) {
146 final PsiClassType[] paramBounds = inferenceVariable.getParameter().getExtendsListTypes();
149 for (PsiClassType paramBound : paramBounds) {
154 glb = GenericsUtil.getGreatestLowerBound(glb, paramBound);
158 if (!((PsiWildcardType)aType).isBounded()) {
160 for (PsiType upperBound : upperBounds) {
161 if (glb != null && mySession.getInferenceVariable(upperBound) == null) {
162 addConstraint(new StrictSubtypingConstraint(upperBound, mySession.substituteWithInferenceVariables(glb)));
166 for (PsiType lowerBound : lowerBounds) {
167 if (isInferenceVariableOrFreshTypeParameter(lowerBound)) {
172 } else if (((PsiWildcardType)aType).isExtends()) {
174 final PsiType extendsBound = ((PsiWildcardType)aType).getExtendsBound();
176 for (PsiType upperBound : upperBounds) {
177 if (mySession.getInferenceVariable(upperBound) == null) {
178 if (paramBounds.length == 1 && paramBounds[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || paramBounds.length == 0) {
179 addConstraint(new StrictSubtypingConstraint(upperBound, extendsBound));
181 else if (extendsBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && glb != null) {
182 addConstraint(new StrictSubtypingConstraint(upperBound, mySession.substituteWithInferenceVariables(glb)));
187 for (PsiType lowerBound : lowerBounds) {
188 if (isInferenceVariableOrFreshTypeParameter(lowerBound)) {
194 LOG.assertTrue(((PsiWildcardType)aType).isSuper());
195 final PsiType superBound = ((PsiWildcardType)aType).getSuperBound();
197 for (PsiType upperBound : upperBounds) {
198 if (glb != null && mySession.getInferenceVariable(upperBound) == null) {
199 addConstraint(new StrictSubtypingConstraint(mySession.substituteWithInferenceVariables(glb), upperBound));
203 for (PsiType lowerBound : lowerBounds) {
204 if (mySession.getInferenceVariable(lowerBound) == null) {
205 addConstraint(new StrictSubtypingConstraint(superBound, lowerBound));
210 inferenceVariable.addBound(aType, InferenceBound.EQ, this);
217 protected void upDown(List<PsiType> lowerBounds,
218 Collection<PsiType> changedLowerBounds,
219 List<PsiType> upperBounds,
220 Collection<PsiType> changedUpperBounds) {
221 if (changedLowerBounds != null) {
222 upDown(changedLowerBounds, upperBounds);
224 if (changedUpperBounds != null) {
225 upDown(lowerBounds, changedUpperBounds);
229 private static Boolean isInferenceVariableOrFreshTypeParameter(PsiType eqBound) {
230 final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(eqBound);
231 if (psiClass instanceof InferenceVariable ||
232 psiClass instanceof PsiTypeParameter && TypeConversionUtil.isFreshVariable((PsiTypeParameter)psiClass)) return true;
236 boolean isFullyIncorporated() {
237 boolean needFurtherIncorporation = false;
238 for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) {
239 if (inferenceVariable.getInstantiation() != PsiType.NULL) continue;
240 Map<InferenceBound, Set<PsiType>> boundsMap = myCurrentBounds.remove(inferenceVariable);
241 if (boundsMap == null) continue;
242 final Set<PsiType> upperBounds = boundsMap.get(InferenceBound.UPPER);
243 final Set<PsiType> lowerBounds = boundsMap.get(InferenceBound.LOWER);
244 if (upperBounds != null) {
245 needFurtherIncorporation |= crossVariables(inferenceVariable, upperBounds, lowerBounds, InferenceBound.LOWER);
247 if (lowerBounds != null) {
248 needFurtherIncorporation |= crossVariables(inferenceVariable, lowerBounds, upperBounds, InferenceBound.UPPER);
251 return !needFurtherIncorporation;
255 * a < b & S <: a & b <: T imply S <: b & a <: T
257 private boolean crossVariables(InferenceVariable inferenceVariable,
258 Collection<PsiType> upperBounds,
259 Collection<PsiType> lowerBounds,
260 InferenceBound inferenceBound) {
262 final InferenceBound oppositeBound = inferenceBound == InferenceBound.LOWER
263 ? InferenceBound.UPPER
264 : InferenceBound.LOWER;
265 boolean result = false;
266 for (PsiType upperBound : upperBounds) {
267 final InferenceVariable inferenceVar = mySession.getInferenceVariable(upperBound);
268 if (inferenceVar != null && inferenceVariable != inferenceVar) {
270 if (lowerBounds != null) {
271 for (PsiType lowerBound : lowerBounds) {
272 result |= inferenceVar.addBound(lowerBound, inferenceBound, this);
276 for (PsiType varUpperBound : inferenceVar.getBounds(oppositeBound)) {
277 result |= inferenceVariable.addBound(varUpperBound, oppositeBound, this);
285 * a = S & a <: T imply S <: T
287 * a = S & T <: a imply T <: S
289 * S <: a & a <: T imply S <: T
291 private void upDown(Collection<PsiType> eqBounds, Collection<PsiType> upperBounds) {
292 for (PsiType upperBound : upperBounds) {
293 if (upperBound == null || PsiType.NULL.equals(upperBound) || upperBound instanceof PsiWildcardType) continue;
295 for (PsiType eqBound : eqBounds) {
296 if (eqBound == null || PsiType.NULL.equals(eqBound) || eqBound instanceof PsiWildcardType) continue;
297 if (Registry.is("javac.unchecked.subtyping.during.incorporation", true)) {
298 if (TypeCompatibilityConstraint.isUncheckedConversion(upperBound, eqBound)) {
299 if (PsiUtil.resolveClassInType(eqBound) instanceof PsiTypeParameter) {
300 mySession.setErased();
305 if (!mySession.isProperType(upperBound) &&
306 eqBound instanceof PsiCapturedWildcardType &&
307 TypeCompatibilityConstraint.isUncheckedConversion(upperBound, ((PsiCapturedWildcardType)eqBound).getUpperBound())) {
308 mySession.setErased();
313 addConstraint(new StrictSubtypingConstraint(upperBound, eqBound));
319 * a = S & a = T imply S = T
321 private void eqEq(List<PsiType> eqBounds, Collection<PsiType> changedEqBounds) {
322 for (int i = 0; i < eqBounds.size(); i++) {
323 PsiType sBound = eqBounds.get(i);
324 boolean changed = changedEqBounds.contains(sBound);
325 for (int j = i + 1; j < eqBounds.size(); j++) {
326 final PsiType tBound = eqBounds.get(j);
327 if (changed || changedEqBounds.contains(tBound)) {
328 addConstraint(new TypeEqualityConstraint(tBound, sBound));
336 * If two bounds have the form α <: S and α <: T, and if for some generic class or interface, G,
337 * 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>,
338 * then for all i, 1 ≤ i ≤ n, if Si and Ti are types (not wildcards), the constraint ⟨Si = Ti⟩ is implied.
340 private boolean upUp(List<PsiType> upperBounds) {
341 return InferenceSession.findParameterizationOfTheSameGenericClass(upperBounds, new Processor<Pair<PsiType, PsiType>>() {
343 public boolean process(Pair<PsiType, PsiType> pair) {
344 final PsiType sType = pair.first;
345 final PsiType tType = pair.second;
346 if (!(sType instanceof PsiWildcardType) && !(tType instanceof PsiWildcardType) && sType != null && tType != null) {
347 addConstraint(new TypeEqualityConstraint(sType, tType));
354 private void addConstraint(ConstraintFormula constraint) {
355 mySession.addConstraint(constraint);
358 public void addBound(InferenceVariable variable, PsiType type, InferenceBound bound) {
359 Map<InferenceBound, Set<PsiType>> bounds = myCurrentBounds.get(variable);
360 if (bounds == null) {
361 bounds = new HashMap<InferenceBound, Set<PsiType>>();
362 myCurrentBounds.put(variable, bounds);
364 Set<PsiType> types = bounds.get(bound);
366 types = new LinkedHashSet<PsiType>();
367 bounds.put(bound, types);