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 017package org.jetbrains.jet.lang.types; 018 019import com.google.common.collect.Maps; 020import com.google.common.collect.Multimap; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 024import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 025import org.jetbrains.jet.util.CommonSuppliers; 026 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Map; 031 032public 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}