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