From 7c4079fd326cf6ca108e0695b154a47978d2bfa5 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 7 Oct 2013 16:23:23 +0400 Subject: [PATCH] Tear down SubstitutionUtils and move to frontend --- .../jet/lang/resolve/TypeResolver.java | 2 +- .../jet/lang/types/CastDiagnosticsUtil.java | 2 +- .../jet/lang/types/SubstitutionUtils.java | 99 ++++++++++ .../impl/AbstractClassDescriptor.java | 2 +- .../jet/lang/types/SubstitutionUtils.java | 170 ------------------ .../jet/lang/types/TypeSubstitutor.java | 50 +++++- .../jetbrains/jet/lang/types/TypeUtils.java | 8 +- 7 files changed, 151 insertions(+), 182 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java delete mode 100644 core/descriptors/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java index ca93485104f..9dca39af02b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java @@ -264,7 +264,7 @@ public class TypeResolver { List parameters = constructor.getParameters(); if (parameters.size() > i) { TypeParameterDescriptor parameterDescriptor = parameters.get(i); - arguments.add(SubstitutionUtils.makeStarProjection(parameterDescriptor)); + arguments.add(TypeUtils.makeStarProjection(parameterDescriptor)); } else { arguments.add(new TypeProjectionImpl(OUT_VARIANCE, ErrorUtils.createErrorType("*"))); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java index 150b6a18c51..0afb50033b5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java @@ -182,7 +182,7 @@ public class CastDiagnosticsUtil { if (value == null) { substitution.put( variable.getTypeConstructor(), - SubstitutionUtils.makeStarProjection(variable) + TypeUtils.makeStarProjection(variable) ); allArgumentsInferred = false; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java new file mode 100644 index 00000000000..02daaeda178 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.types; + +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import org.jetbrains.jet.utils.CommonSuppliers; + +import java.util.List; +import java.util.Map; + +public class SubstitutionUtils { + private SubstitutionUtils() { + } + + /** + * Builds a context with all the supertypes' parameters substituted + */ + @NotNull + public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) { + Map substitution = Maps.newHashMap(); + TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution); + // we use the mutability of the map here + fillInDeepSubstitutor(type, typeSubstitutor, substitution, null); + return typeSubstitutor; + } + + /* + For each supertype of a given type, we map type parameters to type arguments. + + For instance, we have the following class hierarchy: + trait Iterable + trait Collection: Iterable + trait MyFooCollection: Collection> + + For MyFooCollection, the following multimap will be returned: + T declared in Iterable -> Foo + E declared in Collection -> Foo + F declared in MyFooCollection -> out CharSequence + */ + @NotNull + public static Multimap buildDeepSubstitutionMultimap(@NotNull JetType type) { + Multimap fullSubstitution = CommonSuppliers.newLinkedHashSetHashSetMultimap(); + Map substitution = Maps.newHashMap(); + TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution); + // we use the mutability of the map here + fillInDeepSubstitutor(type, typeSubstitutor, substitution, fullSubstitution); + return fullSubstitution; + } + + // we use the mutability of the substitution map here + private static void fillInDeepSubstitutor( + @NotNull JetType context, + @NotNull TypeSubstitutor substitutor, + @NotNull Map substitution, + @Nullable Multimap fullSubstitution + ) { + List parameters = context.getConstructor().getParameters(); + List arguments = context.getArguments(); + + if (parameters.size() != arguments.size()) { + throw new IllegalStateException(); + } + + for (int i = 0; i < arguments.size(); i++) { + TypeProjection argument = arguments.get(i); + TypeParameterDescriptor parameter = parameters.get(i); + + TypeProjection substitute = substitutor.substitute(argument); + assert substitute != null; + substitution.put(parameter.getTypeConstructor(), substitute); + if (fullSubstitution != null) { + fullSubstitution.put(parameter.getTypeConstructor(), substitute); + } + } + if (KotlinBuiltIns.getInstance().isNothingOrNullableNothing(context)) return; + for (JetType supertype : context.getConstructor().getSupertypes()) { + fillInDeepSubstitutor(supertype, substitutor, substitution, fullSubstitution); + } + } +} diff --git a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/AbstractClassDescriptor.java b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/AbstractClassDescriptor.java index b0b4492e166..91dbb39dee3 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/AbstractClassDescriptor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/descriptors/impl/AbstractClassDescriptor.java @@ -114,7 +114,7 @@ public abstract class AbstractClassDescriptor implements ClassDescriptor { if (typeArguments.isEmpty()) return getScopeForMemberLookup(); List typeParameters = getTypeConstructor().getParameters(); - Map substitutionContext = SubstitutionUtils.buildSubstitutionContext(typeParameters, typeArguments); + Map substitutionContext = TypeSubstitutor.buildSubstitutionContext(typeParameters, typeArguments); // Unsafe substitutor is OK, because no recursion can hurt us upon a trivial substitution: // all the types are written explicitly in the code already, they can not get infinite. diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java deleted file mode 100644 index e78311178f5..00000000000 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2010-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.jet.lang.types; - -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; -import org.jetbrains.jet.utils.CommonSuppliers; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -public class SubstitutionUtils { - @NotNull - public static Map buildSubstitutionContext(@NotNull JetType context) { - return buildSubstitutionContext(context.getConstructor().getParameters(), context.getArguments()); - } - - /** - * Builds a context with all the supertypes' parameters substituted - */ - @NotNull - public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) { - Map substitution = Maps.newHashMap(); - TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution); - // we use the mutability of the map here - fillInDeepSubstitutor(type, typeSubstitutor, substitution, null); - return typeSubstitutor; - } - - /* - For each supertype of a given type, we map type parameters to type arguments. - - For instance, we have the following class hierarchy: - trait Iterable - trait Collection: Iterable - trait MyFooCollection: Collection> - - For MyFooCollection, the following multimap will be returned: - T declared in Iterable -> Foo - E declared in Collection -> Foo - F declared in MyFooCollection -> out CharSequence - */ - @NotNull - public static Multimap buildDeepSubstitutionMultimap(@NotNull JetType type) { - Multimap fullSubstitution = CommonSuppliers.newLinkedHashSetHashSetMultimap(); - Map substitution = Maps.newHashMap(); - TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution); - // we use the mutability of the map here - fillInDeepSubstitutor(type, typeSubstitutor, substitution, fullSubstitution); - return fullSubstitution; - } - - // we use the mutability of the substitution map here - private static void fillInDeepSubstitutor(@NotNull JetType context, @NotNull TypeSubstitutor substitutor, @NotNull Map substitution, @Nullable Multimap fullSubstitution) { - List parameters = context.getConstructor().getParameters(); - List arguments = context.getArguments(); - - if (parameters.size() != arguments.size()) { - throw new IllegalStateException(); - } - - for (int i = 0; i < arguments.size(); i++) { - TypeProjection argument = arguments.get(i); - TypeParameterDescriptor parameter = parameters.get(i); - - TypeProjection substitute = substitutor.substitute(argument); - assert substitute != null; - substitution.put(parameter.getTypeConstructor(), substitute); - if (fullSubstitution != null) { - fullSubstitution.put(parameter.getTypeConstructor(), substitute); - } - } - if (KotlinBuiltIns.getInstance().isNothingOrNullableNothing(context)) return; - for (JetType supertype : context.getConstructor().getSupertypes()) { - fillInDeepSubstitutor(supertype, substitutor, substitution, fullSubstitution); - } - } - - @NotNull - public static Map buildSubstitutionContext(@NotNull List parameters, @NotNull List contextArguments) { - Map parameterValues = new HashMap(); - fillInSubstitutionContext(parameters, contextArguments, parameterValues); - return parameterValues; - } - - private static void fillInSubstitutionContext(List parameters, List contextArguments, Map parameterValues) { - if (parameters.size() != contextArguments.size()) { - throw new IllegalArgumentException("type parameter count != context arguments: \n" + - "parameters=" + parameters + "\n" + - "contextArgs=" + contextArguments); - } - for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { - TypeParameterDescriptor parameter = parameters.get(i); - TypeProjection value = contextArguments.get(i); - parameterValues.put(parameter.getTypeConstructor(), value); - } - } - - @NotNull - public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) { - return new TypeProjectionImpl(parameterDescriptor.getVariance() == Variance.OUT_VARIANCE - ? Variance.INVARIANT - : Variance.OUT_VARIANCE, parameterDescriptor.getUpperBoundsAsType()); - } - - public static boolean hasUnsubstitutedTypeParameters(JetType type) { - if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) { - return true; - } - - for(TypeProjection proj : type.getArguments()) { - if (hasUnsubstitutedTypeParameters(proj.getType())) { - return true; - } - } - - return false; - } - - public static Map removeTrivialSubstitutions(Map context) { - Map clean = Maps.newHashMap(context); - boolean changed = false; - for (Iterator> iterator = clean.entrySet().iterator(); iterator.hasNext(); ) { - Map.Entry entry = iterator.next(); - TypeConstructor key = entry.getKey(); - TypeProjection value = entry.getValue(); - if (key == value.getType().getConstructor() && value.getProjectionKind() == Variance.INVARIANT) { - iterator.remove(); - changed = true; - } - } - return changed ? clean : context; - } - - public static void assertNotImmediatelyRecursive(Map context) { - // Make sure we never replace a T with "Foo" or something similar, - // because the substitution will not terminate in this case - // This check is not complete. It does not find cases like - // T -> Foo - // T -> Bar - - for (Map.Entry entry : context.entrySet()) { - TypeConstructor key = entry.getKey(); - TypeProjection value = entry.getValue(); - if (TypeUtils.typeConstructorUsedInType(key, value.getType())) { - throw new IllegalStateException("Immediately recursive substitution: " + context + "\nProblematic parameter: " + key + " -> " + value); - } - } - } -} diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index ee02af7780c..156cf800196 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -24,6 +24,8 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.resolve.scopes.SubstitutingScope; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -72,18 +74,33 @@ public class TypeSubstitutor { /** No assertion for immediate recursion */ public static TypeSubstitutor createUnsafe(@NotNull Map substitutionContext) { - Map cleanContext = SubstitutionUtils.removeTrivialSubstitutions(substitutionContext); + Map cleanContext = removeTrivialSubstitutions(substitutionContext); return create(new MapToTypeSubstitutionAdapter(cleanContext)); } public static TypeSubstitutor create(@NotNull Map substitutionContext) { - Map cleanContext = SubstitutionUtils.removeTrivialSubstitutions(substitutionContext); - //SubstitutionUtils.assertNotImmediatelyRecursive(cleanContext); - return createUnsafe(cleanContext); + return createUnsafe(removeTrivialSubstitutions(substitutionContext)); } public static TypeSubstitutor create(@NotNull JetType context) { - return create(SubstitutionUtils.buildSubstitutionContext(context)); + return create(buildSubstitutionContext(context.getConstructor().getParameters(), context.getArguments())); + } + + @NotNull + public static Map buildSubstitutionContext( + @NotNull List parameters, + @NotNull List contextArguments + ) { + Map parameterValues = new HashMap(); + if (parameters.size() != contextArguments.size()) { + throw new IllegalArgumentException("type parameter count != context arguments: \n" + + "parameters=" + parameters + "\n" + + "contextArgs=" + contextArguments); + } + for (int i = 0, size = parameters.size(); i < size; i++) { + parameterValues.put(parameters.get(i).getTypeConstructor(), contextArguments.get(i)); + } + return parameterValues; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -156,7 +173,8 @@ public class TypeSubstitutor { case OUT_IN_IN_POSITION: throw new SubstitutionException("Out-projection in in-position"); case IN_IN_OUT_POSITION: - return SubstitutionUtils.makeStarProjection(typeParameter); + //noinspection ConstantConditions + return TypeUtils.makeStarProjection(typeParameter); case NO_CONFLICT: boolean resultingIsNullable = type.isNullable() || replacement.getType().isNullable(); JetType substitutedType = TypeUtils.makeNullableAsSpecified(replacement.getType(), resultingIsNullable); @@ -199,7 +217,7 @@ public class TypeSubstitutor { break; case OUT_IN_IN_POSITION: case IN_IN_OUT_POSITION: - substitutedTypeArgument = SubstitutionUtils.makeStarProjection(typeParameter); + substitutedTypeArgument = TypeUtils.makeStarProjection(typeParameter); break; } @@ -208,6 +226,22 @@ public class TypeSubstitutor { return substitutedArguments; } + @NotNull + private static Map removeTrivialSubstitutions(@NotNull Map context) { + Map clean = new HashMap(context); + boolean changed = false; + for (Iterator> iterator = clean.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry entry = iterator.next(); + TypeConstructor key = entry.getKey(); + TypeProjection value = entry.getValue(); + if (key == value.getType().getConstructor() && value.getProjectionKind() == Variance.INVARIANT) { + iterator.remove(); + changed = true; + } + } + return changed ? clean : context; + } + private static Variance combine(Variance typeParameterVariance, Variance projectionKind) { if (typeParameterVariance == Variance.INVARIANT) return projectionKind; if (projectionKind == Variance.INVARIANT) return typeParameterVariance; @@ -218,7 +252,7 @@ public class TypeSubstitutor { private enum VarianceConflictType { NO_CONFLICT, IN_IN_OUT_POSITION, - OUT_IN_IN_POSITION; + OUT_IN_IN_POSITION } private static VarianceConflictType conflictType(Variance position, Variance argument) { diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java index da20557d229..3a13bef729e 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -547,10 +547,16 @@ public class TypeUtils { return builder.toString(); } + @NotNull + public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) { + return new TypeProjectionImpl(parameterDescriptor.getVariance() == Variance.OUT_VARIANCE + ? Variance.INVARIANT + : Variance.OUT_VARIANCE, parameterDescriptor.getUpperBoundsAsType()); + } + @Nullable public static JetType commonSupertypeForNumberTypes(@NotNull Collection numberLowerBounds) { if (numberLowerBounds.isEmpty()) return null; - assert !numberLowerBounds.isEmpty(); Set intersectionOfSupertypes = getIntersectionOfSupertypes(numberLowerBounds); JetType primitiveNumberType = getDefaultPrimitiveNumberType(intersectionOfSupertypes); if (primitiveNumberType != null) {