001 /*
002 * Copyright 2010-2013 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.jet.lang.types.checker;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
022 import org.jetbrains.jet.lang.types.*;
023 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
024
025 import java.util.List;
026
027 import static org.jetbrains.jet.lang.types.Variance.*;
028
029 public class TypeCheckingProcedure {
030
031 // This method returns the supertype of the first parameter that has the same constructor
032 // as the second parameter, applying the substitution of type arguments to it
033 @Nullable
034 public static JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
035 return findCorrespondingSupertype(subtype, supertype, new TypeCheckerTypingConstraints());
036 }
037
038 // This method returns the supertype of the first parameter that has the same constructor
039 // as the second parameter, applying the substitution of type arguments to it
040 @Nullable
041 public static JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypingConstraints typingConstraints) {
042 TypeConstructor constructor = subtype.getConstructor();
043 if (typingConstraints.assertEqualTypeConstructors(constructor, supertype.getConstructor())) {
044 return subtype;
045 }
046 for (JetType immediateSupertype : constructor.getSupertypes()) {
047 JetType correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype, typingConstraints);
048 if (correspondingSupertype != null) {
049 return TypeSubstitutor.create(subtype).safeSubstitute(correspondingSupertype, Variance.INVARIANT);
050 }
051 }
052 return null;
053 }
054
055 public static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) {
056 boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
057 return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType();
058 }
059
060 public static JetType getInType(TypeParameterDescriptor parameter, TypeProjection argument) {
061 boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
062 return isOutProjected ? KotlinBuiltIns.getInstance().getNothingType() : argument.getType();
063 }
064
065 private final TypingConstraints constraints;
066
067 public TypeCheckingProcedure(TypingConstraints constraints) {
068 this.constraints = constraints;
069 }
070
071 public boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2) {
072 if (TypesPackage.isFlexible(type1)) {
073 if (TypesPackage.isFlexible(type2)) {
074 return equalTypes(TypesPackage.flexibility(type1).getLowerBound(), TypesPackage.flexibility(type2).getLowerBound())
075 && equalTypes(TypesPackage.flexibility(type1).getUpperBound(), TypesPackage.flexibility(type2).getUpperBound());
076 }
077 return heterogeneousEquivalence(type2, type1);
078 }
079 else if (TypesPackage.isFlexible(type2)) {
080 return heterogeneousEquivalence(type1, type2);
081 }
082
083 if (type1.isNullable() != type2.isNullable()) {
084 return false;
085 }
086
087 if (type1.isNullable()) {
088 // Then type2 is nullable, too (see the previous condition
089 return constraints.assertEqualTypes(TypeUtils.makeNotNullable(type1), TypeUtils.makeNotNullable(type2), this);
090 }
091
092 TypeConstructor constructor1 = type1.getConstructor();
093 TypeConstructor constructor2 = type2.getConstructor();
094
095 if (!constraints.assertEqualTypeConstructors(constructor1, constructor2)) {
096 return false;
097 }
098
099 List<TypeProjection> type1Arguments = type1.getArguments();
100 List<TypeProjection> type2Arguments = type2.getArguments();
101 if (type1Arguments.size() != type2Arguments.size()) {
102 return false;
103 }
104
105 for (int i = 0; i < type1Arguments.size(); i++) {
106 TypeParameterDescriptor typeParameter1 = constructor1.getParameters().get(i);
107 TypeProjection typeProjection1 = type1Arguments.get(i);
108 TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i);
109 TypeProjection typeProjection2 = type2Arguments.get(i);
110 if (getEffectiveProjectionKind(typeParameter1, typeProjection1) != getEffectiveProjectionKind(typeParameter2, typeProjection2)) {
111 return false;
112 }
113
114 if (!constraints.assertEqualTypes(typeProjection1.getType(), typeProjection2.getType(), this)) {
115 return false;
116 }
117 }
118 return true;
119 }
120
121 private boolean heterogeneousEquivalence(JetType inflexibleType, JetType flexibleType) {
122 // This is to account for the case when we have Collection<X> vs (Mutable)Collection<X>! or K(java.util.Collection<? extends X>)
123 assert !TypesPackage.isFlexible(inflexibleType) : "Only inflexible types are allowed here: " + inflexibleType;
124 return isSubtypeOf(TypesPackage.flexibility(flexibleType).getLowerBound(), inflexibleType)
125 && isSubtypeOf(inflexibleType, TypesPackage.flexibility(flexibleType).getUpperBound());
126 }
127
128 public enum EnrichedProjectionKind {
129 IN, OUT, INV, STAR;
130
131 @NotNull
132 public static EnrichedProjectionKind fromVariance(@NotNull Variance variance) {
133 switch (variance) {
134 case INVARIANT:
135 return INV;
136 case IN_VARIANCE:
137 return IN;
138 case OUT_VARIANCE:
139 return OUT;
140 }
141 throw new IllegalStateException("Unknown variance");
142 }
143 }
144
145 // If class C<out T> then C<T> and C<out T> mean the same
146 // out * out = out
147 // out * in = *
148 // out * inv = out
149 //
150 // in * out = *
151 // in * in = in
152 // in * inv = in
153 //
154 // inv * out = out
155 // inv * in = out
156 // inv * inv = inv
157 public static EnrichedProjectionKind getEffectiveProjectionKind(
158 @NotNull TypeParameterDescriptor typeParameter,
159 @NotNull TypeProjection typeArgument
160 ) {
161 Variance a = typeParameter.getVariance();
162 Variance b = typeArgument.getProjectionKind();
163
164 // If they are not both invariant, let's make b not invariant for sure
165 if (b == INVARIANT) {
166 Variance t = a;
167 a = b;
168 b = t;
169 }
170
171 // Opposites yield STAR
172 if (a == IN_VARIANCE && b == OUT_VARIANCE) {
173 return EnrichedProjectionKind.STAR;
174 }
175 if (a == OUT_VARIANCE && b == IN_VARIANCE) {
176 return EnrichedProjectionKind.STAR;
177 }
178
179 // If they are not opposite, return b, because b is either equal to a or b is in/out and a is inv
180 return EnrichedProjectionKind.fromVariance(b);
181 }
182
183 public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
184 if (TypesPackage.isFlexible(subtype)) {
185 return isSubtypeOf(TypesPackage.flexibility(subtype).getLowerBound(), supertype);
186 }
187 if (TypesPackage.isFlexible(supertype)) {
188 return isSubtypeOf(subtype, TypesPackage.flexibility(supertype).getUpperBound());
189 }
190 if (subtype.isError() || supertype.isError()) {
191 return true;
192 }
193 if (!supertype.isNullable() && subtype.isNullable()) {
194 return false;
195 }
196 subtype = TypeUtils.makeNotNullable(subtype);
197 supertype = TypeUtils.makeNotNullable(supertype);
198 if (KotlinBuiltIns.getInstance().isNothingOrNullableNothing(subtype)) {
199 return true;
200 }
201 @Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype, constraints);
202 if (closestSupertype == null) {
203 return constraints.noCorrespondingSupertype(subtype, supertype); // if this returns true, there still isn't any supertype to continue with
204 }
205
206 return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
207 }
208
209 private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) {
210 TypeConstructor constructor = subtype.getConstructor();
211 assert constraints.assertEqualTypeConstructors(constructor, supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
212
213 List<TypeProjection> subArguments = subtype.getArguments();
214 List<TypeProjection> superArguments = supertype.getArguments();
215 if (subArguments.size() != superArguments.size()) return false;
216
217 List<TypeParameterDescriptor> parameters = constructor.getParameters();
218 for (int i = 0; i < parameters.size(); i++) {
219 TypeParameterDescriptor parameter = parameters.get(i);
220
221 TypeProjection subArgument = subArguments.get(i);
222 JetType subIn = getInType(parameter, subArgument);
223 JetType subOut = getOutType(parameter, subArgument);
224
225 TypeProjection superArgument = superArguments.get(i);
226 JetType superIn = getInType(parameter, superArgument);
227 JetType superOut = getOutType(parameter, superArgument);
228
229 boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError();
230 if (!argumentIsErrorType && parameter.getVariance() == INVARIANT
231 && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
232 if (!constraints.assertEqualTypes(subArgument.getType(), superArgument.getType(), this)) return false;
233 }
234 else {
235 if (!constraints.assertSubtype(subOut, superOut, this)) return false;
236 if (!constraints.assertSubtype(superIn, subIn, this)) return false;
237 }
238 }
239 return true;
240 }
241 }