ensure bounds are promoted on derived type otherwise bounds from super could appear...
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / resolve / graphInference / constraints / StrictSubtypingConstraint.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.psi.impl.source.resolve.graphInference.constraints;
17
18 import com.intellij.psi.*;
19 import com.intellij.psi.impl.source.resolve.graphInference.InferenceBound;
20 import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
21 import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
22 import com.intellij.psi.util.InheritanceUtil;
23 import com.intellij.psi.util.TypeConversionUtil;
24
25 import java.util.HashSet;
26 import java.util.List;
27
28 /**
29  * User: anna
30  */
31 public class StrictSubtypingConstraint implements ConstraintFormula {
32   private PsiType myS;
33   private PsiType myT;
34
35   //t < s
36   public StrictSubtypingConstraint(PsiType t, PsiType s) {
37     myT = t;
38     myS = s;
39   }
40
41   @Override
42   public void apply(PsiSubstitutor substitutor, boolean cache) {
43     myT = substitutor.substitute(myT);
44     myS = substitutor.substitute(myS);
45   }
46
47
48   @Override
49   public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) {
50     final HashSet<InferenceVariable> dependencies = new HashSet<InferenceVariable>();
51     final boolean reduceResult = doReduce(session, dependencies, constraints);
52     if (!reduceResult) {
53       session.registerIncompatibleErrorMessage(dependencies, session.getPresentableText(myS) + " conforms to " + session.getPresentableText(myT));
54     }
55     return reduceResult;
56   }
57
58   private boolean doReduce(InferenceSession session, HashSet<InferenceVariable> dependencies, List<ConstraintFormula> constraints) {
59     if (!session.collectDependencies(myS, dependencies) && !session.collectDependencies(myT, dependencies)) {
60       if (myT == null) return myS == null || myS.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
61       if (myS == null) return true;
62       return TypeConversionUtil.isAssignable(myT, myS);
63     }
64
65     if (PsiType.NULL.equals(myT) || myT == null) return false;
66     if (PsiType.NULL.equals(myS) || myS == null || myT.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true;
67
68     if (PsiType.VOID.equals(myS) ^ PsiType.VOID.equals(myT)) return false;
69
70     InferenceVariable inferenceVariable = session.getInferenceVariable(myS);
71     if (inferenceVariable != null) {
72       InferenceVariable.addBound(myS, myT, InferenceBound.UPPER, session);
73       return true;
74     }
75     inferenceVariable = session.getInferenceVariable(myT);
76     if (inferenceVariable != null) {
77       InferenceVariable.addBound(myT, myS, InferenceBound.LOWER, session);
78       return true;
79     }
80     if (myT instanceof PsiArrayType) {
81       PsiType sType = myS;
82       if (myS instanceof PsiCapturedWildcardType) {
83         final PsiType upperBound = ((PsiCapturedWildcardType)myS).getUpperBound();
84         if (upperBound instanceof PsiArrayType) {
85           sType = upperBound;
86         }
87       }
88       if (!(sType instanceof PsiArrayType)) return false; //todo most specific array supertype
89       final PsiType tComponentType = ((PsiArrayType)myT).getComponentType();
90       final PsiType sComponentType = ((PsiArrayType)sType).getComponentType();
91       if (!(tComponentType instanceof PsiPrimitiveType) && !(sComponentType instanceof PsiPrimitiveType)) {
92         constraints.add(new StrictSubtypingConstraint(tComponentType, sComponentType));
93         return true;
94       }
95       return sComponentType instanceof PsiPrimitiveType && sComponentType.equals(tComponentType);
96     }
97     if (myT instanceof PsiClassType) {
98       final PsiClassType.ClassResolveResult TResult = ((PsiClassType)myT).resolveGenerics();
99       final PsiClass CClass = TResult.getElement();
100       if (CClass != null) {
101         if (CClass instanceof PsiTypeParameter) {
102           if (myS instanceof PsiIntersectionType) {
103             for (PsiType conjunct : ((PsiIntersectionType)myS).getConjuncts()) {
104               if (myT.equals(conjunct)) return true;
105             }
106           }
107           final PsiType lowerBound = InferenceSession.getLowerBound(CClass);
108           if (lowerBound != null) {
109             constraints.add(new StrictSubtypingConstraint(lowerBound, myS));
110             return true;
111           }
112           return false;
113         }
114
115         PsiClassType sType = null;
116         if (myS instanceof PsiIntersectionType) {
117           for (PsiType conjunct : ((PsiIntersectionType)myS).getConjuncts()) {
118             if (conjunct instanceof PsiClassType) {
119               final PsiClassType.ClassResolveResult conjunctResult = ((PsiClassType)conjunct).resolveGenerics();
120               if (InheritanceUtil.isInheritorOrSelf(conjunctResult.getElement(), CClass, true)) {
121                 sType = (PsiClassType)conjunct;
122                 break;
123               }
124             }
125           }
126         }
127         else if (myS instanceof PsiClassType) {
128           sType = (PsiClassType)myS;
129         }
130         else if (myS instanceof PsiArrayType) {
131           return myT.isAssignableFrom(myS);
132         }
133         else if (myS instanceof PsiCapturedWildcardType) {
134           final PsiType upperBound = ((PsiCapturedWildcardType)myS).getUpperBound();
135           if (upperBound instanceof PsiClassType) {
136             sType = (PsiClassType)upperBound;
137           }
138         }
139
140         if (sType == null) return false;
141         final PsiClassType.ClassResolveResult SResult = sType.resolveGenerics();
142         PsiClass SClass = SResult.getElement();
143
144         if (SClass == null) return false;
145
146         PsiSubstitutor substitutor = SResult.getSubstitutor();
147         for (PsiTypeParameter typeParameter : SClass.getTypeParameters()) {
148           substitutor = substitutor.put(typeParameter, substitutor.substituteWithBoundsPromotion(typeParameter));
149         }
150
151         if (((PsiClassType)myT).isRaw()) {
152           return InheritanceUtil.isInheritorOrSelf(SClass, CClass, true);
153         }
154         final PsiSubstitutor tSubstitutor = TResult.getSubstitutor();
155         final PsiSubstitutor sSubstitutor = TypeConversionUtil.getClassSubstitutor(CClass, SClass, substitutor);
156         if (sSubstitutor != null) {
157           for (PsiTypeParameter parameter : CClass.getTypeParameters()) {
158             final PsiType tSubstituted = tSubstitutor.substitute(parameter);
159             final PsiType sSubstituted = sSubstitutor.substitute(parameter);
160             if (tSubstituted == null ^ sSubstituted == null) {
161               return false;
162             }
163             constraints.add(new SubtypingConstraint(tSubstituted, sSubstituted));
164           }
165           return true;
166         }
167       }
168       return false;
169     }
170
171     if (myT instanceof PsiIntersectionType) {
172       for (PsiType conjunct : ((PsiIntersectionType)myT).getConjuncts()) {
173         constraints.add(new StrictSubtypingConstraint(conjunct, myS));
174       }
175       return true;
176     }
177
178     return true;
179   }
180
181   @Override
182   public boolean equals(Object o) {
183     if (this == o) return true;
184     if (o == null || getClass() != o.getClass()) return false;
185
186     StrictSubtypingConstraint that = (StrictSubtypingConstraint)o;
187
188     if (myS != null ? !myS.equals(that.myS) : that.myS != null) return false;
189     if (myT != null ? !myT.equals(that.myT) : that.myT != null) return false;
190
191     return true;
192   }
193
194   @Override
195   public int hashCode() {
196     int result = myS != null ? myS.hashCode() : 0;
197     result = 31 * result + (myT != null ? myT.hashCode() : 0);
198     return result;
199   }
200
201   @Override
202   public String toString() {
203     return myT.getPresentableText() + " < " + myS.getPresentableText();
204   }
205 }