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;
018
019 import com.google.common.collect.Maps;
020 import com.google.common.collect.Multimap;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
024 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
025 import org.jetbrains.jet.util.CommonSuppliers;
026
027 import java.util.HashMap;
028 import java.util.Iterator;
029 import java.util.List;
030 import java.util.Map;
031
032 public class SubstitutionUtils {
033 @NotNull
034 public static Map<TypeConstructor, TypeProjection> buildSubstitutionContext(@NotNull JetType context) {
035 return buildSubstitutionContext(context.getConstructor().getParameters(), context.getArguments());
036 }
037
038 /**
039 * Builds a context with all the supertypes' parameters substituted
040 */
041 @NotNull
042 public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) {
043 Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap();
044 TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution);
045 // we use the mutability of the map here
046 fillInDeepSubstitutor(type, typeSubstitutor, substitution, null);
047 return typeSubstitutor;
048 }
049
050 /*
051 For each supertype of a given type, we map type parameters to type arguments.
052
053 For instance, we have the following class hierarchy:
054 trait Hashable
055 trait Iterable<out T>
056 trait Collection<out E>: Iterable<E>, Hashable
057 trait MyFooCollection<F>: Collection<Foo<F>>
058
059 For MyFunCollection<out CharSequence>, the following multimap will be returned:
060 T declared in Iterable -> Foo<out CharSequence>
061 E declared in Collection -> Foo<out CharSequence>
062 F declared in MyFooCollection -> out CharSequence
063 */
064 @NotNull
065 public static Multimap<TypeConstructor, TypeProjection> buildDeepSubstitutionMultimap(@NotNull JetType type) {
066 Multimap<TypeConstructor, TypeProjection> fullSubstitution = CommonSuppliers.newLinkedHashSetHashSetMultimap();
067 Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap();
068 TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution);
069 // we use the mutability of the map here
070 fillInDeepSubstitutor(type, typeSubstitutor, substitution, fullSubstitution);
071 return fullSubstitution;
072 }
073
074 // we use the mutability of the substitution map here
075 private static void fillInDeepSubstitutor(@NotNull JetType context, @NotNull TypeSubstitutor substitutor, @NotNull Map<TypeConstructor, TypeProjection> substitution, @Nullable Multimap<TypeConstructor, TypeProjection> fullSubstitution) {
076 List<TypeParameterDescriptor> parameters = context.getConstructor().getParameters();
077 List<TypeProjection> arguments = context.getArguments();
078
079 if (parameters.size() != arguments.size()) {
080 throw new IllegalStateException();
081 }
082
083 for (int i = 0; i < arguments.size(); i++) {
084 TypeProjection argument = arguments.get(i);
085 TypeParameterDescriptor parameter = parameters.get(i);
086
087 TypeProjection substitute = substitutor.substitute(argument);
088 assert substitute != null;
089 substitution.put(parameter.getTypeConstructor(), substitute);
090 if (fullSubstitution != null) {
091 fullSubstitution.put(parameter.getTypeConstructor(), substitute);
092 }
093 }
094 if (KotlinBuiltIns.getInstance().isNothingOrNullableNothing(context)) return;
095 for (JetType supertype : context.getConstructor().getSupertypes()) {
096 fillInDeepSubstitutor(supertype, substitutor, substitution, fullSubstitution);
097 }
098 }
099
100 @NotNull
101 public static Map<TypeConstructor, TypeProjection> buildSubstitutionContext(@NotNull List<TypeParameterDescriptor> parameters, @NotNull List<TypeProjection> contextArguments) {
102 Map<TypeConstructor, TypeProjection> parameterValues = new HashMap<TypeConstructor, TypeProjection>();
103 fillInSubstitutionContext(parameters, contextArguments, parameterValues);
104 return parameterValues;
105 }
106
107 private static void fillInSubstitutionContext(List<TypeParameterDescriptor> parameters, List<TypeProjection> contextArguments, Map<TypeConstructor, TypeProjection> parameterValues) {
108 if (parameters.size() != contextArguments.size()) {
109 throw new IllegalArgumentException("type parameter count != context arguments");
110 }
111 for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
112 TypeParameterDescriptor parameter = parameters.get(i);
113 TypeProjection value = contextArguments.get(i);
114 parameterValues.put(parameter.getTypeConstructor(), value);
115 }
116 }
117
118 @NotNull
119 public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) {
120 return new TypeProjection(parameterDescriptor.getVariance() == Variance.OUT_VARIANCE
121 ? Variance.INVARIANT
122 : Variance.OUT_VARIANCE, parameterDescriptor.getUpperBoundsAsType());
123 }
124
125 public static boolean hasUnsubstitutedTypeParameters(JetType type) {
126 if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
127 return true;
128 }
129
130 for(TypeProjection proj : type.getArguments()) {
131 if (hasUnsubstitutedTypeParameters(proj.getType())) {
132 return true;
133 }
134 }
135
136 return false;
137 }
138
139 public static Map<TypeConstructor, TypeProjection> removeTrivialSubstitutions(Map<TypeConstructor, TypeProjection> context) {
140 Map<TypeConstructor, TypeProjection> clean = Maps.newHashMap(context);
141 boolean changed = false;
142 for (Iterator<Map.Entry<TypeConstructor, TypeProjection>> iterator = clean.entrySet().iterator(); iterator.hasNext(); ) {
143 Map.Entry<TypeConstructor, TypeProjection> entry = iterator.next();
144 TypeConstructor key = entry.getKey();
145 TypeProjection value = entry.getValue();
146 if (key == value.getType().getConstructor() && value.getProjectionKind() == Variance.INVARIANT) {
147 iterator.remove();
148 changed = true;
149 }
150 }
151 return changed ? clean : context;
152 }
153
154 public static void assertNotImmediatelyRecursive(Map<TypeConstructor, TypeProjection> context) {
155 // Make sure we never replace a T with "Foo<T>" or something similar,
156 // because the substitution will not terminate in this case
157 // This check is not complete. It does not find cases like
158 // T -> Foo<T1>
159 // T -> Bar<T>
160
161 for (Map.Entry<TypeConstructor, TypeProjection> entry : context.entrySet()) {
162 TypeConstructor key = entry.getKey();
163 TypeProjection value = entry.getValue();
164 if (TypeUtils.typeConstructorUsedInType(key, value.getType())) {
165 throw new IllegalStateException("Immediately recursive substitution: " + context + "\nProblematic parameter: " + key + " -> " + value);
166 }
167 }
168 }
169 }