001 /*
002 * Copyright 2010-2016 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;
018
019 import kotlin.jvm.functions.Function1;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
024 import org.jetbrains.kotlin.descriptors.annotations.Annotations;
025 import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations;
026 import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations;
027 import org.jetbrains.kotlin.name.FqName;
028 import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructorKt;
029 import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
030 import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
031
032 import java.util.ArrayList;
033 import java.util.List;
034 import java.util.Map;
035
036 public class TypeSubstitutor {
037
038 private static final int MAX_RECURSION_DEPTH = 100;
039
040 public static final TypeSubstitutor EMPTY = create(TypeSubstitution.EMPTY);
041
042 private static final class SubstitutionException extends Exception {
043 public SubstitutionException(String message) {
044 super(message);
045 }
046 }
047
048 @NotNull
049 public static TypeSubstitutor create(@NotNull TypeSubstitution substitution) {
050 return new TypeSubstitutor(substitution);
051 }
052
053 @NotNull
054 public static TypeSubstitutor createChainedSubstitutor(@NotNull TypeSubstitution first, @NotNull TypeSubstitution second) {
055 return create(DisjointKeysUnionTypeSubstitution.create(first, second));
056 }
057
058 @NotNull
059 public static TypeSubstitutor create(@NotNull Map<TypeConstructor, TypeProjection> substitutionContext) {
060 return create(TypeConstructorSubstitution.createByConstructorsMap(substitutionContext));
061 }
062
063 @NotNull
064 public static TypeSubstitutor create(@NotNull KotlinType context) {
065 return create(TypeConstructorSubstitution.create(context.getConstructor(), context.getArguments()));
066 }
067
068 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
069
070 private final @NotNull TypeSubstitution substitution;
071
072 protected TypeSubstitutor(@NotNull TypeSubstitution substitution) {
073 this.substitution = substitution;
074 }
075
076 public boolean isEmpty() {
077 return substitution.isEmpty();
078 }
079
080 @NotNull
081 public TypeSubstitution getSubstitution() {
082 return substitution;
083 }
084
085 @NotNull
086 public KotlinType safeSubstitute(@NotNull KotlinType type, @NotNull Variance howThisTypeIsUsed) {
087 if (isEmpty()) {
088 return type;
089 }
090
091 try {
092 return unsafeSubstitute(new TypeProjectionImpl(howThisTypeIsUsed, type), 0).getType();
093 } catch (SubstitutionException e) {
094 return ErrorUtils.createErrorType(e.getMessage());
095 }
096 }
097
098 @Nullable
099 public KotlinType substitute(@NotNull KotlinType type, @NotNull Variance howThisTypeIsUsed) {
100 TypeProjection projection =
101 substitute(new TypeProjectionImpl(howThisTypeIsUsed, getSubstitution().prepareTopLevelType(type, howThisTypeIsUsed)));
102 return projection == null ? null : projection.getType();
103 }
104
105 @Nullable
106 public TypeProjection substitute(@NotNull TypeProjection typeProjection) {
107 TypeProjection substitutedTypeProjection = substituteWithoutApproximation(typeProjection);
108 if (!substitution.approximateCapturedTypes() && !substitution.approximateContravariantCapturedTypes()) {
109 return substitutedTypeProjection;
110 }
111 return CapturedTypeApproximationKt.approximateCapturedTypesIfNecessary(
112 substitutedTypeProjection, substitution.approximateContravariantCapturedTypes());
113 }
114
115 @Nullable
116 public TypeProjection substituteWithoutApproximation(@NotNull TypeProjection typeProjection) {
117 if (isEmpty()) {
118 return typeProjection;
119 }
120
121 try {
122 return unsafeSubstitute(typeProjection, 0);
123 } catch (SubstitutionException e) {
124 return null;
125 }
126 }
127
128 @NotNull
129 private TypeProjection unsafeSubstitute(@NotNull TypeProjection originalProjection, int recursionDepth) throws SubstitutionException {
130 assertRecursionDepth(recursionDepth, originalProjection, substitution);
131
132 if (originalProjection.isStarProjection()) return originalProjection;
133
134 // The type is within the substitution range, i.e. T or T?
135 KotlinType type = originalProjection.getType();
136 if (DynamicTypesKt.isDynamic(type) || type.unwrap() instanceof RawType) {
137 return originalProjection; // todo investigate
138 }
139
140 TypeProjection replacement = substitution.get(type);
141 Variance originalProjectionKind = originalProjection.getProjectionKind();
142 if (replacement == null && FlexibleTypesKt.isFlexible(type) && !TypeCapabilitiesKt.isCustomTypeVariable(type)) {
143 FlexibleType flexibleType = FlexibleTypesKt.asFlexibleType(type);
144 TypeProjection substitutedLower =
145 unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibleType.getLowerBound()), recursionDepth + 1);
146 TypeProjection substitutedUpper =
147 unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibleType.getUpperBound()), recursionDepth + 1);
148
149 Variance substitutedProjectionKind = substitutedLower.getProjectionKind();
150 assert (substitutedProjectionKind == substitutedUpper.getProjectionKind()) &&
151 originalProjectionKind == Variance.INVARIANT || originalProjectionKind == substitutedProjectionKind :
152 "Unexpected substituted projection kind: " + substitutedProjectionKind + "; original: " + originalProjectionKind;
153
154 KotlinType substitutedFlexibleType = KotlinTypeFactory.flexibleType(
155 TypeSubstitutionKt.asSimpleType(substitutedLower.getType()), TypeSubstitutionKt.asSimpleType(substitutedUpper.getType()));
156 return new TypeProjectionImpl(substitutedProjectionKind, substitutedFlexibleType);
157 }
158
159 if (KotlinBuiltIns.isNothing(type) || type.isError()) return originalProjection;
160
161 if (replacement != null) {
162 VarianceConflictType varianceConflict = conflictType(originalProjectionKind, replacement.getProjectionKind());
163
164 // Captured type might be substituted in an opposite projection:
165 // out 'Captured (in Int)' = out Int
166 // in 'Captured (out Int)' = in Int
167 boolean allowVarianceConflict = CapturedTypeConstructorKt.isCaptured(type);
168 if (!allowVarianceConflict) {
169 //noinspection EnumSwitchStatementWhichMissesCases
170 switch (varianceConflict) {
171 case OUT_IN_IN_POSITION:
172 throw new SubstitutionException("Out-projection in in-position");
173 case IN_IN_OUT_POSITION:
174 // todo use the right type parameter variance and upper bound
175 return new TypeProjectionImpl(Variance.OUT_VARIANCE, type.getConstructor().getBuiltIns().getNullableAnyType());
176 }
177 }
178 KotlinType substitutedType;
179 CustomTypeVariable typeVariable = TypeCapabilitiesKt.getCustomTypeVariable(type);
180 if (replacement.isStarProjection()) {
181 return replacement;
182 }
183 else if (typeVariable != null) {
184 substitutedType = typeVariable.substitutionResult(replacement.getType());
185 }
186 else {
187 // this is a simple type T or T?: if it's T, we should just take replacement, if T? - we make replacement nullable
188 substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
189 }
190
191 // substitutionType.annotations = replacement.annotations ++ type.annotations
192 if (!type.getAnnotations().isEmpty()) {
193 Annotations typeAnnotations = filterOutUnsafeVariance(substitution.filterAnnotations(type.getAnnotations()));
194 substitutedType = TypeUtilsKt.replaceAnnotations(
195 substitutedType,
196 new CompositeAnnotations(substitutedType.getAnnotations(), typeAnnotations)
197 );
198 }
199
200 Variance resultingProjectionKind = varianceConflict == VarianceConflictType.NO_CONFLICT
201 ? combine(originalProjectionKind, replacement.getProjectionKind())
202 : originalProjectionKind;
203 return new TypeProjectionImpl(resultingProjectionKind, substitutedType);
204 }
205 // The type is not within the substitution range, i.e. Foo, Bar<T> etc.
206 return substituteCompoundType(originalProjection, recursionDepth);
207 }
208
209 @NotNull
210 private static Annotations filterOutUnsafeVariance(@NotNull Annotations annotations) {
211 if (!annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) return annotations;
212 return new FilteredAnnotations(annotations, new Function1<FqName, Boolean>() {
213 @Override
214 public Boolean invoke(@NotNull FqName name) {
215 return !name.equals(KotlinBuiltIns.FQ_NAMES.unsafeVariance);
216 }
217 });
218 }
219
220 private TypeProjection substituteCompoundType(
221 TypeProjection originalProjection,
222 int recursionDepth
223 ) throws SubstitutionException {
224 KotlinType type = originalProjection.getType();
225 Variance projectionKind = originalProjection.getProjectionKind();
226 if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
227 // substitution can't change type parameter
228 // todo substitute bounds
229 return originalProjection;
230 }
231
232 KotlinType substitutedAbbreviation = null;
233 SimpleType abbreviation = SpecialTypesKt.getAbbreviation(type);
234 if (abbreviation != null) {
235 substitutedAbbreviation = substitute(abbreviation, Variance.INVARIANT);
236 }
237
238 List<TypeProjection> substitutedArguments = substituteTypeArguments(
239 type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
240
241 KotlinType substitutedType =
242 TypeSubstitutionKt.replace(type, substitutedArguments, substitution.filterAnnotations(type.getAnnotations()));
243 if (substitutedType instanceof SimpleType && substitutedAbbreviation instanceof SimpleType) {
244 substitutedType = SpecialTypesKt.withAbbreviation((SimpleType) substitutedType, (SimpleType) substitutedAbbreviation);
245 }
246
247 return new TypeProjectionImpl(projectionKind, substitutedType);
248 }
249
250 private List<TypeProjection> substituteTypeArguments(
251 List<TypeParameterDescriptor> typeParameters, List<TypeProjection> typeArguments, int recursionDepth
252 ) throws SubstitutionException {
253 List<TypeProjection> substitutedArguments = new ArrayList<TypeProjection>(typeParameters.size());
254 for (int i = 0; i < typeParameters.size(); i++) {
255 TypeParameterDescriptor typeParameter = typeParameters.get(i);
256 TypeProjection typeArgument = typeArguments.get(i);
257
258 TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, recursionDepth + 1);
259
260 switch (conflictType(typeParameter.getVariance(), substitutedTypeArgument.getProjectionKind())) {
261 case NO_CONFLICT:
262 // if the corresponding type parameter is already co/contra-variant, there's not need for an explicit projection
263 if (typeParameter.getVariance() != Variance.INVARIANT && !substitutedTypeArgument.isStarProjection()) {
264 substitutedTypeArgument = new TypeProjectionImpl(Variance.INVARIANT, substitutedTypeArgument.getType());
265 }
266 break;
267 case OUT_IN_IN_POSITION:
268 case IN_IN_OUT_POSITION:
269 substitutedTypeArgument = TypeUtils.makeStarProjection(typeParameter);
270 break;
271 }
272
273 substitutedArguments.add(substitutedTypeArgument);
274 }
275 return substitutedArguments;
276 }
277
278 @NotNull
279 public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull TypeProjection typeProjection) {
280 if (typeProjection.isStarProjection()) return Variance.OUT_VARIANCE;
281
282 return combine(typeParameterVariance, typeProjection.getProjectionKind());
283 }
284
285 @NotNull
286 public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull Variance projectionKind) {
287 if (typeParameterVariance == Variance.INVARIANT) return projectionKind;
288 if (projectionKind == Variance.INVARIANT) return typeParameterVariance;
289 if (typeParameterVariance == projectionKind) return projectionKind;
290 throw new AssertionError("Variance conflict: type parameter variance '" + typeParameterVariance + "' and " +
291 "projection kind '" + projectionKind + "' cannot be combined");
292 }
293
294 private enum VarianceConflictType {
295 NO_CONFLICT,
296 IN_IN_OUT_POSITION,
297 OUT_IN_IN_POSITION
298 }
299
300 private static VarianceConflictType conflictType(Variance position, Variance argument) {
301 if (position == Variance.IN_VARIANCE && argument == Variance.OUT_VARIANCE) {
302 return VarianceConflictType.OUT_IN_IN_POSITION;
303 }
304 if (position == Variance.OUT_VARIANCE && argument == Variance.IN_VARIANCE) {
305 return VarianceConflictType.IN_IN_OUT_POSITION;
306 }
307 return VarianceConflictType.NO_CONFLICT;
308 }
309
310 private static void assertRecursionDepth(int recursionDepth, TypeProjection projection, TypeSubstitution substitution) {
311 if (recursionDepth > MAX_RECURSION_DEPTH) {
312 throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(projection) + "; substitution: " + safeToString(substitution));
313 }
314 }
315
316 private static String safeToString(Object o) {
317 try {
318 return o.toString();
319 }
320 catch (Throwable e) {
321 if (e.getClass().getName().equals("com.intellij.openapi.progress.ProcessCanceledException")) {
322 //noinspection ConstantConditions
323 throw (RuntimeException) e;
324 }
325 return "[Exception while computing toString(): " + e + "]";
326 }
327 }
328 }