001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.types.checker;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
022 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
023 import org.jetbrains.kotlin.resolve.DescriptorUtils;
024 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
025 import org.jetbrains.kotlin.types.*;
026
027 import java.util.Collection;
028 import java.util.Collections;
029 import java.util.List;
030
031 import static org.jetbrains.kotlin.types.Variance.*;
032
033 public class TypeCheckingProcedure {
034
035 // This method returns the supertype of the first parameter that has the same constructor
036 // as the second parameter, applying the substitution of type arguments to it
037 @Nullable
038 public static KotlinType findCorrespondingSupertype(@NotNull KotlinType subtype, @NotNull KotlinType supertype) {
039 return findCorrespondingSupertype(subtype, supertype, new TypeCheckerProcedureCallbacksImpl());
040 }
041
042 // This method returns the supertype of the first parameter that has the same constructor
043 // as the second parameter, applying the substitution of type arguments to it
044 @Nullable
045 public static KotlinType findCorrespondingSupertype(@NotNull KotlinType subtype, @NotNull KotlinType supertype, @NotNull TypeCheckingProcedureCallbacks typeCheckingProcedureCallbacks) {
046 return UtilsKt.findCorrespondingSupertype(subtype, supertype, typeCheckingProcedureCallbacks);
047 }
048
049 @NotNull
050 private static KotlinType getOutType(@NotNull TypeParameterDescriptor parameter, @NotNull TypeProjection argument) {
051 boolean isInProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
052 return isInProjected ? DescriptorUtilsKt.getBuiltIns(parameter).getNullableAnyType() : argument.getType();
053 }
054
055 @NotNull
056 private static KotlinType getInType(@NotNull TypeParameterDescriptor parameter, @NotNull TypeProjection argument) {
057 boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
058 return isOutProjected ? DescriptorUtilsKt.getBuiltIns(parameter).getNothingType() : argument.getType();
059 }
060
061 private final TypeCheckingProcedureCallbacks constraints;
062
063 public TypeCheckingProcedure(TypeCheckingProcedureCallbacks constraints) {
064 this.constraints = constraints;
065 }
066
067 public boolean equalTypes(@NotNull KotlinType type1, @NotNull KotlinType type2) {
068 if (type1 == type2) return true;
069 if (FlexibleTypesKt.isFlexible(type1)) {
070 if (FlexibleTypesKt.isFlexible(type2)) {
071 return !type1.isError() && !type2.isError() && isSubtypeOf(type1, type2) && isSubtypeOf(type2, type1);
072 }
073 return heterogeneousEquivalence(type2, type1);
074 }
075 else if (FlexibleTypesKt.isFlexible(type2)) {
076 return heterogeneousEquivalence(type1, type2);
077 }
078
079 if (type1.isMarkedNullable() != type2.isMarkedNullable()) {
080 return false;
081 }
082
083 if (type1.isMarkedNullable()) {
084 // Then type2 is nullable, too (see the previous condition
085 return constraints.assertEqualTypes(TypeUtils.makeNotNullable(type1), TypeUtils.makeNotNullable(type2), this);
086 }
087
088 TypeConstructor constructor1 = type1.getConstructor();
089 TypeConstructor constructor2 = type2.getConstructor();
090
091 if (!constraints.assertEqualTypeConstructors(constructor1, constructor2)) {
092 return false;
093 }
094
095 List<TypeProjection> type1Arguments = type1.getArguments();
096 List<TypeProjection> type2Arguments = type2.getArguments();
097 if (type1Arguments.size() != type2Arguments.size()) {
098 return false;
099 }
100
101 for (int i = 0; i < type1Arguments.size(); i++) {
102 TypeProjection typeProjection1 = type1Arguments.get(i);
103 TypeProjection typeProjection2 = type2Arguments.get(i);
104 if (typeProjection1.isStarProjection() && typeProjection2.isStarProjection()) {
105 continue;
106 }
107 TypeParameterDescriptor typeParameter1 = constructor1.getParameters().get(i);
108 TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i);
109
110 if (capture(typeProjection1, typeProjection2, typeParameter1)) {
111 continue;
112 }
113 if (getEffectiveProjectionKind(typeParameter1, typeProjection1) != getEffectiveProjectionKind(typeParameter2, typeProjection2)) {
114 return false;
115 }
116
117 if (!constraints.assertEqualTypes(typeProjection1.getType(), typeProjection2.getType(), this)) {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 protected boolean heterogeneousEquivalence(KotlinType inflexibleType, KotlinType flexibleType) {
125 // This is to account for the case when we have Collection<X> vs (Mutable)Collection<X>! or K(java.util.Collection<? extends X>)
126 assert !FlexibleTypesKt.isFlexible(inflexibleType) : "Only inflexible types are allowed here: " + inflexibleType;
127 return isSubtypeOf(FlexibleTypesKt.flexibility(flexibleType).getLowerBound(), inflexibleType)
128 && isSubtypeOf(inflexibleType, FlexibleTypesKt.flexibility(flexibleType).getUpperBound());
129 }
130
131 public enum EnrichedProjectionKind {
132 IN, OUT, INV, STAR;
133
134 @NotNull
135 public static EnrichedProjectionKind fromVariance(@NotNull Variance variance) {
136 switch (variance) {
137 case INVARIANT:
138 return INV;
139 case IN_VARIANCE:
140 return IN;
141 case OUT_VARIANCE:
142 return OUT;
143 }
144 throw new IllegalStateException("Unknown variance");
145 }
146 }
147
148 // If class C<out T> then C<T> and C<out T> mean the same
149 // out * out = out
150 // out * in = *
151 // out * inv = out
152 //
153 // in * out = *
154 // in * in = in
155 // in * inv = in
156 //
157 // inv * out = out
158 // inv * in = out
159 // inv * inv = inv
160 public static EnrichedProjectionKind getEffectiveProjectionKind(
161 @NotNull TypeParameterDescriptor typeParameter,
162 @NotNull TypeProjection typeArgument
163 ) {
164 Variance a = typeParameter.getVariance();
165 Variance b = typeArgument.getProjectionKind();
166
167 // If they are not both invariant, let's make b not invariant for sure
168 if (b == INVARIANT) {
169 Variance t = a;
170 a = b;
171 b = t;
172 }
173
174 // Opposites yield STAR
175 if (a == IN_VARIANCE && b == OUT_VARIANCE) {
176 return EnrichedProjectionKind.STAR;
177 }
178 if (a == OUT_VARIANCE && b == IN_VARIANCE) {
179 return EnrichedProjectionKind.STAR;
180 }
181
182 // If they are not opposite, return b, because b is either equal to a or b is in/out and a is inv
183 return EnrichedProjectionKind.fromVariance(b);
184 }
185
186 public boolean isSubtypeOf(@NotNull KotlinType subtype, @NotNull KotlinType supertype) {
187 if (TypeCapabilitiesKt.sameTypeConstructors(subtype, supertype)) {
188 return !subtype.isMarkedNullable() || supertype.isMarkedNullable();
189 }
190 KotlinType subtypeRepresentative = TypeCapabilitiesKt.getSubtypeRepresentative(subtype);
191 KotlinType supertypeRepresentative = TypeCapabilitiesKt.getSupertypeRepresentative(supertype);
192 if (subtypeRepresentative != subtype || supertypeRepresentative != supertype) {
193 // recursive invocation for possible chain of representatives
194 return isSubtypeOf(subtypeRepresentative, supertypeRepresentative);
195 }
196 return isSubtypeOfForRepresentatives(subtype, supertype);
197 }
198
199 private boolean isSubtypeOfForRepresentatives(KotlinType subtype, KotlinType supertype) {
200 if (subtype.isError() || supertype.isError()) {
201 return true;
202 }
203
204 if (!supertype.isMarkedNullable() && subtype.isMarkedNullable()) {
205 return false;
206 }
207
208 if (KotlinBuiltIns.isNothingOrNullableNothing(subtype)) {
209 return true;
210 }
211
212 @Nullable KotlinType closestSupertype = findCorrespondingSupertype(subtype, supertype, constraints);
213 if (closestSupertype == null) {
214 return constraints.noCorrespondingSupertype(subtype, supertype); // if this returns true, there still isn't any supertype to continue with
215 }
216
217 if (!supertype.isMarkedNullable() && closestSupertype.isMarkedNullable()) {
218 return false;
219 }
220
221 return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
222 }
223
224 private boolean checkSubtypeForTheSameConstructor(@NotNull KotlinType subtype, @NotNull KotlinType supertype) {
225 TypeConstructor constructor = subtype.getConstructor();
226 assert constraints.assertEqualTypeConstructors(constructor, supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
227
228 List<TypeProjection> subArguments = subtype.getArguments();
229 List<TypeProjection> superArguments = supertype.getArguments();
230 if (subArguments.size() != superArguments.size()) return false;
231
232 List<TypeParameterDescriptor> parameters = constructor.getParameters();
233 for (int i = 0; i < parameters.size(); i++) {
234 TypeParameterDescriptor parameter = parameters.get(i);
235
236 TypeProjection superArgument = superArguments.get(i);
237 TypeProjection subArgument = subArguments.get(i);
238
239 if (superArgument.isStarProjection()) continue;
240
241 if (capture(subArgument, superArgument, parameter)) continue;
242
243 boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError();
244 if (!argumentIsErrorType && parameter.getVariance() == INVARIANT &&
245 subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
246 if (!constraints.assertEqualTypes(subArgument.getType(), superArgument.getType(), this)) return false;
247 continue;
248 }
249
250 KotlinType superOut = getOutType(parameter, superArgument);
251 KotlinType subOut = getOutType(parameter, subArgument);
252 if (!constraints.assertSubtype(subOut, superOut, this)) return false;
253
254 KotlinType superIn = getInType(parameter, superArgument);
255 KotlinType subIn = getInType(parameter, subArgument);
256
257 if (superArgument.getProjectionKind() != Variance.OUT_VARIANCE) {
258 if (!constraints.assertSubtype(superIn, subIn, this)) return false;
259 }
260 else {
261 assert KotlinBuiltIns.isNothing(superIn) : "In component must be Nothing for out-projection";
262 }
263 }
264 return true;
265 }
266
267 private boolean capture(
268 @NotNull TypeProjection firstProjection,
269 @NotNull TypeProjection secondProjection,
270 @NotNull TypeParameterDescriptor parameter
271 ) {
272 // Capturing makes sense only for invariant classes
273 if (parameter.getVariance() != INVARIANT) return false;
274
275 // Now, both subtype and supertype relations transform to equality constraints on type arguments:
276 // Array<T> is a subtype, supertype or equal to Array<out Int> then T captures a type that extends Int: 'Captured(out Int)'
277 // Array<T> is a subtype, supertype or equal to Array<in Int> then T captures a type that extends Int: 'Captured(in Int)'
278
279 if (firstProjection.getProjectionKind() == INVARIANT && secondProjection.getProjectionKind() != INVARIANT) {
280 return constraints.capture(firstProjection.getType(), secondProjection);
281 }
282 if (firstProjection.getProjectionKind() != INVARIANT && secondProjection.getProjectionKind() == INVARIANT) {
283 return constraints.capture(secondProjection.getType(), firstProjection);
284 }
285 return false;
286 }
287 }