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