/** * Remember that we are trying to cast something of type {@code supertype} to {@code subtype}. * * <p>Since at runtime we can only check the class (type constructor), the rest of the subtype * should be known statically, from supertype. This method reconstructs all static information * that can be obtained from supertype. * * <p>Example 1: supertype = Collection<String> subtype = List<...> result = List<String>, all * arguments are inferred * * <p>Example 2: supertype = Any subtype = List<...> result = List<*>, some arguments were not * inferred, replaced with '*' */ public static TypeReconstructionResult findStaticallyKnownSubtype( @NotNull KotlinType supertype, @NotNull TypeConstructor subtypeConstructor) { assert !supertype.isMarkedNullable() : "This method only makes sense for non-nullable types"; // Assume we are casting an expression of type Collection<Foo> to List<Bar> // First, let's make List<T>, where T is a type variable ClassifierDescriptor descriptor = subtypeConstructor.getDeclarationDescriptor(); assert descriptor != null : "Can't create default type for " + subtypeConstructor; KotlinType subtypeWithVariables = descriptor.getDefaultType(); // Now, let's find a supertype of List<T> that is a Collection of something, // in this case it will be Collection<T> KotlinType supertypeWithVariables = TypeCheckingProcedure.findCorrespondingSupertype(subtypeWithVariables, supertype); final List<TypeParameterDescriptor> variables = subtypeWithVariables.getConstructor().getParameters(); Map<TypeConstructor, TypeProjection> substitution; if (supertypeWithVariables != null) { // Now, let's try to unify Collection<T> and Collection<Foo> solution is a map from T to Foo TypeUnifier.UnificationResult solution = TypeUnifier.unify( new TypeProjectionImpl(supertype), new TypeProjectionImpl(supertypeWithVariables), new Predicate<TypeConstructor>() { @Override public boolean apply(TypeConstructor typeConstructor) { ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor(); return descriptor instanceof TypeParameterDescriptor && variables.contains(descriptor); } }); substitution = Maps.newHashMap(solution.getSubstitution()); } else { // If there's no corresponding supertype, no variables are determined // This may be OK, e.g. in case 'Any as List<*>' substitution = Maps.newHashMapWithExpectedSize(variables.size()); } // If some of the parameters are not determined by unification, it means that these parameters // are lost, // let's put stars instead, so that we can only cast to something like List<*>, e.g. (a: Any) as // List<*> boolean allArgumentsInferred = true; for (TypeParameterDescriptor variable : variables) { TypeProjection value = substitution.get(variable.getTypeConstructor()); if (value == null) { substitution.put(variable.getTypeConstructor(), TypeUtils.makeStarProjection(variable)); allArgumentsInferred = false; } } // At this point we have values for all type parameters of List // Let's make a type by substituting them: List<T> -> List<Foo> KotlinType substituted = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT); return new TypeReconstructionResult(substituted, allArgumentsInferred); }
@Nullable public static KotlinType createSubstitutedSupertype( @NotNull KotlinType subType, @NotNull KotlinType superType, @NotNull TypeSubstitutor substitutor) { KotlinType substitutedType = substitutor.substitute(superType, Variance.INVARIANT); if (substitutedType != null) { return makeNullableIfNeeded(substitutedType, subType.isMarkedNullable()); } return null; }
@NotNull public static List<KotlinType> getImmediateSupertypes(@NotNull KotlinType type) { TypeSubstitutor substitutor = TypeSubstitutor.create(type); Collection<KotlinType> originalSupertypes = type.getConstructor().getSupertypes(); List<KotlinType> result = new ArrayList<KotlinType>(originalSupertypes.size()); for (KotlinType supertype : originalSupertypes) { KotlinType substitutedType = createSubstitutedSupertype(type, supertype, substitutor); if (substitutedType != null) { result.add(substitutedType); } } return result; }
@NotNull public static TypeSubstitutor createSubstitutorForTypeParameters( @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters) { Map<TypeConstructor, TypeProjection> typeSubstitutionContext = new HashMap<TypeConstructor, TypeProjection>(); for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameter : originalToAltTypeParameters.entrySet()) { typeSubstitutionContext.put( originalToAltTypeParameter.getKey().getTypeConstructor(), new TypeProjectionImpl(originalToAltTypeParameter.getValue().getDefaultType())); } // TODO: Use IndexedParametersSubstitution here instead of map creation return TypeSubstitutor.create(typeSubstitutionContext); }
@NotNull public static KotlinType substituteProjectionsForParameters( @NotNull ClassDescriptor clazz, @NotNull List<TypeProjection> projections) { List<TypeParameterDescriptor> clazzTypeParameters = clazz.getTypeConstructor().getParameters(); if (clazzTypeParameters.size() != projections.size()) { throw new IllegalArgumentException( "type parameter counts do not match: " + clazz + ", " + projections); } Map<TypeConstructor, TypeProjection> substitutions = org.jetbrains.kotlin.utils.CollectionsKt.newHashMapWithExpectedSize( clazzTypeParameters.size()); for (int i = 0; i < clazzTypeParameters.size(); ++i) { TypeConstructor typeConstructor = clazzTypeParameters.get(i).getTypeConstructor(); substitutions.put(typeConstructor, projections.get(i)); } return TypeSubstitutor.create(substitutions) .substitute(clazz.getDefaultType(), Variance.INVARIANT); }