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    }