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 = substitute(new TypeProjectionImpl(howThisTypeIsUsed, type));
101 return projection == null ? null : projection.getType();
102 }
103
104 @Nullable
105 public TypeProjection substitute(@NotNull TypeProjection typeProjection) {
106 TypeProjection substitutedTypeProjection = substituteWithoutApproximation(typeProjection);
107 if (!substitution.approximateCapturedTypes() && !substitution.approximateContravariantCapturedTypes()) {
108 return substitutedTypeProjection;
109 }
110 return CapturedTypeApproximationKt.approximateCapturedTypesIfNecessary(
111 substitutedTypeProjection, substitution.approximateContravariantCapturedTypes());
112 }
113
114 @Nullable
115 public TypeProjection substituteWithoutApproximation(@NotNull TypeProjection typeProjection) {
116 if (isEmpty()) {
117 return typeProjection;
118 }
119
120 try {
121 return unsafeSubstitute(typeProjection, 0);
122 } catch (SubstitutionException e) {
123 return null;
124 }
125 }
126
127 @NotNull
128 private TypeProjection unsafeSubstitute(@NotNull TypeProjection originalProjection, int recursionDepth) throws SubstitutionException {
129 assertRecursionDepth(recursionDepth, originalProjection, substitution);
130
131 if (originalProjection.isStarProjection()) return originalProjection;
132
133 // The type is within the substitution range, i.e. T or T?
134 KotlinType type = originalProjection.getType();
135 TypeProjection replacement = substitution.get(type);
136 Variance originalProjectionKind = originalProjection.getProjectionKind();
137 if (replacement == null && FlexibleTypesKt.isFlexible(type) && !TypeCapabilitiesKt.isCustomTypeVariable(type)) {
138 Flexibility flexibility = FlexibleTypesKt.flexibility(type);
139 TypeProjection substitutedLower =
140 unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibility.getLowerBound()), recursionDepth + 1);
141 TypeProjection substitutedUpper =
142 unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibility.getUpperBound()), recursionDepth + 1);
143
144 Variance substitutedProjectionKind = substitutedLower.getProjectionKind();
145 assert (substitutedProjectionKind == substitutedUpper.getProjectionKind()) &&
146 originalProjectionKind == Variance.INVARIANT || originalProjectionKind == substitutedProjectionKind :
147 "Unexpected substituted projection kind: " + substitutedProjectionKind + "; original: " + originalProjectionKind;
148
149 KotlinType substitutedFlexibleType = flexibility.getFactory().create(
150 substitutedLower.getType(), substitutedUpper.getType());
151 return new TypeProjectionImpl(substitutedProjectionKind, substitutedFlexibleType);
152 }
153
154 if (KotlinBuiltIns.isNothing(type) || type.isError()) return originalProjection;
155
156 if (replacement != null) {
157 VarianceConflictType varianceConflict = conflictType(originalProjectionKind, replacement.getProjectionKind());
158
159 // Captured type might be substituted in an opposite projection:
160 // out 'Captured (in Int)' = out Int
161 // in 'Captured (out Int)' = in Int
162 boolean allowVarianceConflict = CapturedTypeConstructorKt.isCaptured(type);
163 if (!allowVarianceConflict) {
164 //noinspection EnumSwitchStatementWhichMissesCases
165 switch (varianceConflict) {
166 case OUT_IN_IN_POSITION:
167 throw new SubstitutionException("Out-projection in in-position");
168 case IN_IN_OUT_POSITION:
169 // todo use the right type parameter variance and upper bound
170 return new TypeProjectionImpl(Variance.OUT_VARIANCE, type.getConstructor().getBuiltIns().getNullableAnyType());
171 }
172 }
173 KotlinType substitutedType;
174 CustomTypeVariable typeVariable = TypeCapabilitiesKt.getCustomTypeVariable(type);
175 if (replacement.isStarProjection()) {
176 return replacement;
177 }
178 else if (typeVariable != null) {
179 substitutedType = typeVariable.substitutionResult(replacement.getType());
180 }
181 else {
182 // this is a simple type T or T?: if it's T, we should just take replacement, if T? - we make replacement nullable
183 substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
184 }
185
186 // substitutionType.annotations = replacement.annotations ++ type.annotations
187 if (!type.getAnnotations().isEmpty()) {
188 Annotations typeAnnotations = filterOutUnsafeVariance(substitution.filterAnnotations(type.getAnnotations()));
189 substitutedType = TypeUtilsKt.replaceAnnotations(
190 substitutedType,
191 new CompositeAnnotations(substitutedType.getAnnotations(), typeAnnotations)
192 );
193 }
194
195 Variance resultingProjectionKind = varianceConflict == VarianceConflictType.NO_CONFLICT
196 ? combine(originalProjectionKind, replacement.getProjectionKind())
197 : originalProjectionKind;
198 return new TypeProjectionImpl(resultingProjectionKind, substitutedType);
199 }
200 // The type is not within the substitution range, i.e. Foo, Bar<T> etc.
201 return substituteCompoundType(originalProjection, recursionDepth);
202 }
203
204 @NotNull
205 private static Annotations filterOutUnsafeVariance(@NotNull Annotations annotations) {
206 if (!annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) return annotations;
207 return new FilteredAnnotations(annotations, new Function1<FqName, Boolean>() {
208 @Override
209 public Boolean invoke(@NotNull FqName name) {
210 return !name.equals(KotlinBuiltIns.FQ_NAMES.unsafeVariance);
211 }
212 });
213 }
214
215 private TypeProjection substituteCompoundType(
216 TypeProjection originalProjection,
217 int recursionDepth
218 ) throws SubstitutionException {
219 KotlinType type = originalProjection.getType();
220 Variance projectionKind = originalProjection.getProjectionKind();
221 if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
222 // substitution can't change type parameter
223 // todo substitute bounds
224 return originalProjection;
225 }
226
227 List<TypeProjection> substitutedArguments = substituteTypeArguments(
228 type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
229
230 KotlinType substitutedType =
231 TypeSubstitutionKt.replace(type, substitutedArguments, substitution.filterAnnotations(type.getAnnotations()));
232 return new TypeProjectionImpl(projectionKind, substitutedType);
233 }
234
235 private List<TypeProjection> substituteTypeArguments(
236 List<TypeParameterDescriptor> typeParameters, List<TypeProjection> typeArguments, int recursionDepth
237 ) throws SubstitutionException {
238 List<TypeProjection> substitutedArguments = new ArrayList<TypeProjection>(typeParameters.size());
239 for (int i = 0; i < typeParameters.size(); i++) {
240 TypeParameterDescriptor typeParameter = typeParameters.get(i);
241 TypeProjection typeArgument = typeArguments.get(i);
242
243 TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, recursionDepth + 1);
244
245 switch (conflictType(typeParameter.getVariance(), substitutedTypeArgument.getProjectionKind())) {
246 case NO_CONFLICT:
247 // if the corresponding type parameter is already co/contra-variant, there's not need for an explicit projection
248 if (typeParameter.getVariance() != Variance.INVARIANT && !substitutedTypeArgument.isStarProjection()) {
249 substitutedTypeArgument = new TypeProjectionImpl(Variance.INVARIANT, substitutedTypeArgument.getType());
250 }
251 break;
252 case OUT_IN_IN_POSITION:
253 case IN_IN_OUT_POSITION:
254 substitutedTypeArgument = TypeUtils.makeStarProjection(typeParameter);
255 break;
256 }
257
258 substitutedArguments.add(substitutedTypeArgument);
259 }
260 return substitutedArguments;
261 }
262
263 @NotNull
264 public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull TypeProjection typeProjection) {
265 if (typeProjection.isStarProjection()) return Variance.OUT_VARIANCE;
266
267 return combine(typeParameterVariance, typeProjection.getProjectionKind());
268 }
269
270 @NotNull
271 public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull Variance projectionKind) {
272 if (typeParameterVariance == Variance.INVARIANT) return projectionKind;
273 if (projectionKind == Variance.INVARIANT) return typeParameterVariance;
274 if (typeParameterVariance == projectionKind) return projectionKind;
275 throw new AssertionError("Variance conflict: type parameter variance '" + typeParameterVariance + "' and " +
276 "projection kind '" + projectionKind + "' cannot be combined");
277 }
278
279 private enum VarianceConflictType {
280 NO_CONFLICT,
281 IN_IN_OUT_POSITION,
282 OUT_IN_IN_POSITION
283 }
284
285 private static VarianceConflictType conflictType(Variance position, Variance argument) {
286 if (position == Variance.IN_VARIANCE && argument == Variance.OUT_VARIANCE) {
287 return VarianceConflictType.OUT_IN_IN_POSITION;
288 }
289 if (position == Variance.OUT_VARIANCE && argument == Variance.IN_VARIANCE) {
290 return VarianceConflictType.IN_IN_OUT_POSITION;
291 }
292 return VarianceConflictType.NO_CONFLICT;
293 }
294
295 private static void assertRecursionDepth(int recursionDepth, TypeProjection projection, TypeSubstitution substitution) {
296 if (recursionDepth > MAX_RECURSION_DEPTH) {
297 throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(projection) + "; substitution: " + safeToString(substitution));
298 }
299 }
300
301 private static String safeToString(Object o) {
302 try {
303 return o.toString();
304 }
305 catch (Throwable e) {
306 if (e.getClass().getName().equals("com.intellij.openapi.progress.ProcessCanceledException")) {
307 //noinspection ConstantConditions
308 throw (RuntimeException) e;
309 }
310 return "[Exception while computing toString(): " + e + "]";
311 }
312 }
313 }