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.utils.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<? extends 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<? extends TypeProjection> contextArguments, Map<TypeConstructor, TypeProjection> parameterValues) {
108 if (parameters.size() != contextArguments.size()) {
109 throw new IllegalArgumentException("type parameter count != context arguments: \n" +
110 "parameters=" + parameters + "\n" +
111 "contextArgs=" + contextArguments);
112 }
113 for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
114 TypeParameterDescriptor parameter = parameters.get(i);
115 TypeProjection value = contextArguments.get(i);
116 parameterValues.put(parameter.getTypeConstructor(), value);
117 }
118 }
119
120 @NotNull
121 public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) {
122 return new TypeProjectionImpl(parameterDescriptor.getVariance() == Variance.OUT_VARIANCE
123 ? Variance.INVARIANT
124 : Variance.OUT_VARIANCE, parameterDescriptor.getUpperBoundsAsType());
125 }
126
127 public static boolean hasUnsubstitutedTypeParameters(JetType type) {
128 if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
129 return true;
130 }
131
132 for(TypeProjection proj : type.getArguments()) {
133 if (hasUnsubstitutedTypeParameters(proj.getType())) {
134 return true;
135 }
136 }
137
138 return false;
139 }
140
141 public static Map<TypeConstructor, TypeProjection> removeTrivialSubstitutions(Map<TypeConstructor, TypeProjection> context) {
142 Map<TypeConstructor, TypeProjection> clean = Maps.newHashMap(context);
143 boolean changed = false;
144 for (Iterator<Map.Entry<TypeConstructor, TypeProjection>> iterator = clean.entrySet().iterator(); iterator.hasNext(); ) {
145 Map.Entry<TypeConstructor, TypeProjection> entry = iterator.next();
146 TypeConstructor key = entry.getKey();
147 TypeProjection value = entry.getValue();
148 if (key == value.getType().getConstructor() && value.getProjectionKind() == Variance.INVARIANT) {
149 iterator.remove();
150 changed = true;
151 }
152 }
153 return changed ? clean : context;
154 }
155
156 public static void assertNotImmediatelyRecursive(Map<TypeConstructor, TypeProjection> context) {
157 // Make sure we never replace a T with "Foo<T>" or something similar,
158 // because the substitution will not terminate in this case
159 // This check is not complete. It does not find cases like
160 // T -> Foo<T1>
161 // T -> Bar<T>
162
163 for (Map.Entry<TypeConstructor, TypeProjection> entry : context.entrySet()) {
164 TypeConstructor key = entry.getKey();
165 TypeProjection value = entry.getValue();
166 if (TypeUtils.typeConstructorUsedInType(key, value.getType())) {
167 throw new IllegalStateException("Immediately recursive substitution: " + context + "\nProblematic parameter: " + key + " -> " + value);
168 }
169 }
170 }
171 }