private List<TypeProjection> substituteTypeArguments( List<TypeParameterDescriptor> typeParameters, List<TypeProjection> typeArguments, int recursionDepth) throws SubstitutionException { List<TypeProjection> substitutedArguments = new ArrayList<TypeProjection>(typeParameters.size()); for (int i = 0; i < typeParameters.size(); i++) { TypeParameterDescriptor typeParameter = typeParameters.get(i); TypeProjection typeArgument = typeArguments.get(i); TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, recursionDepth + 1); switch (conflictType( typeParameter.getVariance(), substitutedTypeArgument.getProjectionKind())) { case NO_CONFLICT: // if the corresponding type parameter is already co/contra-variant, there's not need for // an explicit projection if (typeParameter.getVariance() != Variance.INVARIANT && !substitutedTypeArgument.isStarProjection()) { substitutedTypeArgument = new TypeProjectionImpl(Variance.INVARIANT, substitutedTypeArgument.getType()); } break; case OUT_IN_IN_POSITION: case IN_IN_OUT_POSITION: substitutedTypeArgument = TypeUtils.makeStarProjection(typeParameter); break; } substitutedArguments.add(substitutedTypeArgument); } return substitutedArguments; }
@NotNull public static List<ValueParameterDescriptor> getValueParameters( @NotNull FunctionDescriptor functionDescriptor, @NotNull KotlinType type) { assert isFunctionOrExtensionFunctionType(type); List<TypeProjection> parameterTypes = getParameterTypeProjectionsFromFunctionType(type); List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(parameterTypes.size()); for (int i = 0; i < parameterTypes.size(); i++) { TypeProjection parameterType = parameterTypes.get(i); ValueParameterDescriptorImpl valueParameterDescriptor = new ValueParameterDescriptorImpl( functionDescriptor, null, i, Annotations.Companion.getEMPTY(), Name.identifier("p" + (i + 1)), parameterType.getType(), /* declaresDefaultValue = */ false, /* isCrossinline = */ false, /* isNoinline = */ false, null, SourceElement.NO_SOURCE); valueParameters.add(valueParameterDescriptor); } return valueParameters; }
// Returns list with type arguments info from supertypes // Example: // - Foo<A, B> is a subtype of Bar<A, List<B>>, Baz<Boolean, A> // - input: klass = Foo, typesFromSuper = [Bar<String, List<Int>>, Baz<Boolean, CharSequence>] // - output[0] = [String, CharSequence], output[1] = [] private static List<List<TypeProjectionAndVariance>> calculateTypeArgumentsFromSuper( @NotNull ClassDescriptor klass, @NotNull Collection<TypeAndVariance> typesFromSuper) { // For each superclass of klass and its parameters, hold their mapping to klass' parameters // #0 of Bar -> A // #1 of Bar -> List<B> // #0 of Baz -> Boolean // #1 of Baz -> A // #0 of Foo -> A (mapped to itself) // #1 of Foo -> B (mapped to itself) Multimap<TypeConstructor, TypeProjection> substitution = SubstitutionUtils.buildDeepSubstitutionMultimap( TypeUtils.makeUnsubstitutedType(klass, JetScope.EMPTY)); // for each parameter of klass, hold arguments in corresponding supertypes List<List<TypeProjectionAndVariance>> parameterToArgumentsFromSuper = Lists.newArrayList(); for (TypeParameterDescriptor ignored : klass.getTypeConstructor().getParameters()) { parameterToArgumentsFromSuper.add(new ArrayList<TypeProjectionAndVariance>()); } // Enumerate all types from super and all its parameters for (TypeAndVariance typeFromSuper : typesFromSuper) { for (TypeParameterDescriptor parameter : typeFromSuper.type.getConstructor().getParameters()) { TypeProjection argument = typeFromSuper.type.getArguments().get(parameter.getIndex()); // for given example, this block is executed four times: // 1. typeFromSuper = Bar<String, List<Int>>, parameter = "#0 of Bar", argument = // String // 2. typeFromSuper = Bar<String, List<Int>>, parameter = "#1 of Bar", argument = // List<Int> // 3. typeFromSuper = Baz<Boolean, CharSequence>, parameter = "#0 of Baz", argument = // Boolean // 4. typeFromSuper = Baz<Boolean, CharSequence>, parameter = "#1 of Baz", argument = // CharSequence // if it is mapped to klass' parameter, then store it into map for (TypeProjection projection : substitution.get(parameter.getTypeConstructor())) { // 1. projection = A // 2. projection = List<B> // 3. projection = Boolean // 4. projection = A ClassifierDescriptor classifier = projection.getType().getConstructor().getDeclarationDescriptor(); // this condition is true for 1 and 4, false for 2 and 3 if (classifier instanceof TypeParameterDescriptor && classifier.getContainingDeclaration() == klass) { int parameterIndex = ((TypeParameterDescriptor) classifier).getIndex(); Variance effectiveVariance = parameter.getVariance().superpose(typeFromSuper.varianceOfPosition); parameterToArgumentsFromSuper .get(parameterIndex) .add(new TypeProjectionAndVariance(argument, effectiveVariance)); } } } } return parameterToArgumentsFromSuper; }
public static boolean containsErrorType(@Nullable JetType type) { if (type == null) return false; if (type.isError()) return true; for (TypeProjection projection : type.getArguments()) { if (containsErrorType(projection.getType())) return true; } return false; }
public static boolean dependsOnTypeConstructors( @NotNull KotlinType type, @NotNull Collection<TypeConstructor> typeParameterConstructors) { if (typeParameterConstructors.contains(type.getConstructor())) return true; for (TypeProjection typeProjection : type.getArguments()) { if (!typeProjection.isStarProjection() && dependsOnTypeConstructors(typeProjection.getType(), typeParameterConstructors)) { return true; } } return false; }
private void appendTypeProjections( @NotNull List<TypeProjection> typeProjections, @NotNull StringBuilder builder) { for (Iterator<TypeProjection> iterator = typeProjections.iterator(); iterator.hasNext(); ) { TypeProjection typeProjection = iterator.next(); if (typeProjection.getProjectionKind() != Variance.INVARIANT) { builder.append(typeProjection.getProjectionKind()).append(" "); } builder.append(renderType(typeProjection.getType())); if (iterator.hasNext()) { builder.append(", "); } } }
private TypeProjection substituteCompoundType( TypeProjection originalProjection, int recursionDepth) throws SubstitutionException { final JetType type = originalProjection.getType(); Variance projectionKind = originalProjection.getProjectionKind(); if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) { // substitution can't change type parameter // todo substitute bounds return originalProjection; } List<TypeProjection> substitutedArguments = substituteTypeArguments( type.getConstructor().getParameters(), type.getArguments(), recursionDepth); // Only type parameters of the corresponding class (or captured type parameters of outer // declaration) are substituted // e.g. for return type Foo of 'add(..)' in 'class Foo { fun <R> add(bar: Bar<R>): Foo }' R // shouldn't be substituted in the scope TypeSubstitution substitutionFilteringTypeParameters = new TypeSubstitution() { private final Collection<TypeConstructor> containedOrCapturedTypeParameters = TypeUtilsKt.getContainedAndCapturedTypeParameterConstructors(type); @Nullable @Override public TypeProjection get(@NotNull JetType key) { return containedOrCapturedTypeParameters.contains(key.getConstructor()) ? substitution.get(key) : null; } @Override public boolean isEmpty() { return substitution.isEmpty(); } }; JetType substitutedType = JetTypeImpl.create( type.getAnnotations(), // Old annotations. This is questionable type.getConstructor(), // The same constructor type.isMarkedNullable(), // Same nullability substitutedArguments, substitutionFilteringTypeParameters, new SubstitutingScope( type.getMemberScope(), create(substitutionFilteringTypeParameters)), type.getCapabilities()); return new TypeProjectionImpl(projectionKind, substitutedType); }
public static boolean contains( @Nullable KotlinType type, @NotNull Function1<KotlinType, Boolean> isSpecialType) { if (type == null) return false; if (isSpecialType.invoke(type)) return true; Flexibility flexibility = type.getCapability(Flexibility.class); if (flexibility != null && (contains(flexibility.getLowerBound(), isSpecialType) || contains(flexibility.getUpperBound(), isSpecialType))) { return true; } for (TypeProjection projection : type.getArguments()) { if (!projection.isStarProjection() && contains(projection.getType(), isSpecialType)) return true; } return false; }
@NotNull public static JetType createCorrespondingExtensionFunctionType( @NotNull JetType functionType, @NotNull JetType receiverType) { assert KotlinBuiltIns.getInstance().isFunctionType(functionType); List<TypeProjection> typeArguments = functionType.getArguments(); assert !typeArguments.isEmpty(); List<JetType> arguments = Lists.newArrayList(); // excluding the last type argument of the function type, which is the return type int index = 0; int lastIndex = typeArguments.size() - 1; for (TypeProjection typeArgument : typeArguments) { if (index < lastIndex) { arguments.add(typeArgument.getType()); } index++; } JetType returnType = typeArguments.get(lastIndex).getType(); return KotlinBuiltIns.getInstance() .getFunctionType(functionType.getAnnotations(), receiverType, arguments, returnType); }
@NotNull private List<TypeProjection> getTypeArgsOfType( @NotNull JetType autoType, @NotNull ClassifierDescriptor classifier, @NotNull List<TypeAndVariance> typesFromSuper) { List<TypeProjection> autoArguments = autoType.getArguments(); if (!(classifier instanceof ClassDescriptor)) { assert autoArguments.isEmpty() : "Unexpected type arguments when type constructor is not ClassDescriptor, type = " + autoType; return autoArguments; } List<List<TypeProjectionAndVariance>> typeArgumentsFromSuper = calculateTypeArgumentsFromSuper((ClassDescriptor) classifier, typesFromSuper); // Modify type arguments using info from typesFromSuper List<TypeProjection> resultArguments = Lists.newArrayList(); for (TypeParameterDescriptor parameter : classifier.getTypeConstructor().getParameters()) { TypeProjection argument = autoArguments.get(parameter.getIndex()); JetType argumentType = argument.getType(); List<TypeProjectionAndVariance> projectionsFromSuper = typeArgumentsFromSuper.get(parameter.getIndex()); List<TypeAndVariance> argTypesFromSuper = getTypes(projectionsFromSuper); JetType type = modifyTypeAccordingToSuperMethods(argumentType, argTypesFromSuper, TYPE_ARGUMENT); Variance projectionKind = calculateArgumentProjectionKindFromSuper(argument, projectionsFromSuper); resultArguments.add(new TypeProjection(projectionKind, type)); } return resultArguments; }
private static void doUnify( TypeProjection knownProjection, TypeProjection projectWithVariables, Predicate<TypeConstructor> isVariable, UnificationResultImpl result) { JetType known = knownProjection.getType(); JetType withVariables = projectWithVariables.getType(); // in Foo ~ in X => Foo ~ X Variance knownProjectionKind = knownProjection.getProjectionKind(); Variance withVariablesProjectionKind = projectWithVariables.getProjectionKind(); if (knownProjectionKind == withVariablesProjectionKind && knownProjectionKind != Variance.INVARIANT) { doUnify(new TypeProjection(known), new TypeProjection(withVariables), isVariable, result); return; } // Foo? ~ X? => Foo ~ X if (known.isNullable() && withVariables.isNullable()) { doUnify( new TypeProjection(knownProjectionKind, TypeUtils.makeNotNullable(known)), new TypeProjection(withVariablesProjectionKind, TypeUtils.makeNotNullable(withVariables)), isVariable, result); return; } // in Foo ~ out X => fail // in Foo ~ X => may be OK if (knownProjectionKind != withVariablesProjectionKind && withVariablesProjectionKind != Variance.INVARIANT) { result.fail(); return; } // Foo ~ X? => fail if (!known.isNullable() && withVariables.isNullable()) { result.fail(); return; } // Foo ~ X => x |-> Foo TypeConstructor maybeVariable = withVariables.getConstructor(); if (isVariable.apply(maybeVariable)) { result.put(maybeVariable, new TypeProjection(knownProjectionKind, known)); return; } // Foo? ~ Foo || in Foo ~ Foo || Foo ~ Bar boolean structuralMismatch = known.isNullable() != withVariables.isNullable() || knownProjectionKind != withVariablesProjectionKind || !known.getConstructor().equals(withVariables.getConstructor()); if (structuralMismatch) { result.fail(); return; } // Foo<A> ~ Foo<B, C> if (known.getArguments().size() != withVariables.getArguments().size()) { result.fail(); return; } // Foo ~ Foo if (known.getArguments().isEmpty()) { return; } // Foo<...> ~ Foo<...> List<TypeProjection> knownArguments = known.getArguments(); List<TypeProjection> withVariablesArguments = withVariables.getArguments(); for (int i = 0; i < knownArguments.size(); i++) { TypeProjection knownArg = knownArguments.get(i); TypeProjection withVariablesArg = withVariablesArguments.get(i); doUnify(knownArg, withVariablesArg, isVariable, result); } }
@Nullable public JetType substitute(@NotNull JetType type, @NotNull Variance howThisTypeIsUsed) { TypeProjection projection = substitute(new TypeProjectionImpl(howThisTypeIsUsed, type)); return projection == null ? null : projection.getType(); }
@NotNull private TypeProjection unsafeSubstitute( @NotNull TypeProjection originalProjection, int recursionDepth) throws SubstitutionException { assertRecursionDepth(recursionDepth, originalProjection, substitution); if (originalProjection.isStarProjection()) return originalProjection; // The type is within the substitution range, i.e. T or T? JetType type = originalProjection.getType(); TypeProjection replacement = substitution.get(type); Variance originalProjectionKind = originalProjection.getProjectionKind(); if (replacement == null && FlexibleTypesKt.isFlexible(type) && !TypeCapabilitiesKt.isCustomTypeVariable(type)) { Flexibility flexibility = FlexibleTypesKt.flexibility(type); TypeProjection substitutedLower = unsafeSubstitute( new TypeProjectionImpl(originalProjectionKind, flexibility.getLowerBound()), recursionDepth + 1); TypeProjection substitutedUpper = unsafeSubstitute( new TypeProjectionImpl(originalProjectionKind, flexibility.getUpperBound()), recursionDepth + 1); Variance substitutedProjectionKind = substitutedLower.getProjectionKind(); assert (substitutedProjectionKind == substitutedUpper.getProjectionKind()) && originalProjectionKind == Variance.INVARIANT || originalProjectionKind == substitutedProjectionKind : "Unexpected substituted projection kind: " + substitutedProjectionKind + "; original: " + originalProjectionKind; JetType substitutedFlexibleType = DelegatingFlexibleType.create( substitutedLower.getType(), substitutedUpper.getType(), flexibility.getExtraCapabilities()); return new TypeProjectionImpl(substitutedProjectionKind, substitutedFlexibleType); } if (KotlinBuiltIns.isNothing(type) || type.isError()) return originalProjection; if (replacement != null) { VarianceConflictType varianceConflict = conflictType(originalProjectionKind, replacement.getProjectionKind()); // Captured type might be substituted in an opposite projection: // out 'Captured (in Int)' = out Int // in 'Captured (out Int)' = in Int boolean allowVarianceConflict = CapturedTypeConstructorKt.isCaptured(type); if (!allowVarianceConflict) { //noinspection EnumSwitchStatementWhichMissesCases switch (varianceConflict) { case OUT_IN_IN_POSITION: throw new SubstitutionException("Out-projection in in-position"); case IN_IN_OUT_POSITION: // todo use the right type parameter variance and upper bound return new TypeProjectionImpl( Variance.OUT_VARIANCE, type.getConstructor().getBuiltIns().getNullableAnyType()); } } JetType substitutedType; CustomTypeVariable typeVariable = TypeCapabilitiesKt.getCustomTypeVariable(type); if (replacement.isStarProjection()) { return replacement; } else if (typeVariable != null) { substitutedType = typeVariable.substitutionResult(replacement.getType()); } else { // this is a simple type T or T?: if it's T, we should just take replacement, if T? - we // make replacement nullable substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable()); } // substitutionType.annotations = replacement.annotations ++ type.annotations if (!type.getAnnotations().isEmpty()) { Annotations typeAnnotations = filterOutUnsafeVariance(type.getAnnotations()); substitutedType = TypeUtilsKt.replaceAnnotations( substitutedType, new CompositeAnnotations(substitutedType.getAnnotations(), typeAnnotations)); } Variance resultingProjectionKind = varianceConflict == VarianceConflictType.NO_CONFLICT ? combine(originalProjectionKind, replacement.getProjectionKind()) : originalProjectionKind; return new TypeProjectionImpl(resultingProjectionKind, substitutedType); } // The type is not within the substitution range, i.e. Foo, Bar<T> etc. return substituteCompoundType(originalProjection, recursionDepth); }
public static JetType createWrongVarianceErrorType(TypeProjection value) { return createErrorType(value + " is not allowed here", value.getType().getMemberScope()); }
public static boolean canHaveSubtypes(KotlinTypeChecker typeChecker, @NotNull KotlinType type) { if (type.isMarkedNullable()) { return true; } if (!type.getConstructor().isFinal()) { return true; } List<TypeParameterDescriptor> parameters = type.getConstructor().getParameters(); List<TypeProjection> arguments = type.getArguments(); for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { TypeParameterDescriptor parameterDescriptor = parameters.get(i); TypeProjection typeProjection = arguments.get(i); if (typeProjection.isStarProjection()) return true; Variance projectionKind = typeProjection.getProjectionKind(); KotlinType argument = typeProjection.getType(); switch (parameterDescriptor.getVariance()) { case INVARIANT: switch (projectionKind) { case INVARIANT: if (lowerThanBound(typeChecker, argument, parameterDescriptor) || canHaveSubtypes(typeChecker, argument)) { return true; } break; case IN_VARIANCE: if (lowerThanBound(typeChecker, argument, parameterDescriptor)) { return true; } break; case OUT_VARIANCE: if (canHaveSubtypes(typeChecker, argument)) { return true; } break; } break; case IN_VARIANCE: if (projectionKind != Variance.OUT_VARIANCE) { if (lowerThanBound(typeChecker, argument, parameterDescriptor)) { return true; } } else { if (canHaveSubtypes(typeChecker, argument)) { return true; } } break; case OUT_VARIANCE: if (projectionKind != Variance.IN_VARIANCE) { if (canHaveSubtypes(typeChecker, argument)) { return true; } } else { if (lowerThanBound(typeChecker, argument, parameterDescriptor)) { return true; } } break; } } return false; }
private void addConstraint( @NotNull ConstraintKind constraintKind, @NotNull JetType subjectType, @Nullable JetType constrainingType, @NotNull ConstraintPosition constraintPosition) { if (constrainingType == TypeUtils.NO_EXPECTED_TYPE || constrainingType == DONT_CARE || constrainingType == CANT_INFER) { return; } if (constrainingType == null || (ErrorUtils.isErrorType(constrainingType) && constrainingType != PLACEHOLDER_FUNCTION_TYPE)) { hasErrorInConstrainingTypes = true; return; } assert subjectType != TypeUtils.NO_EXPECTED_TYPE : "Subject type shouldn't be NO_EXPECTED_TYPE (in position " + constraintPosition + " )"; if (ErrorUtils.isErrorType(subjectType)) return; DeclarationDescriptor subjectTypeDescriptor = subjectType.getConstructor().getDeclarationDescriptor(); KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance(); if (constrainingType == PLACEHOLDER_FUNCTION_TYPE) { if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(subjectType)) { if (subjectTypeDescriptor instanceof TypeParameterDescriptor && typeParameterConstraints.get(subjectTypeDescriptor) != null) { // a constraint binds type parameter and any function type, so there is no new info and no // error return; } errorConstraintPositions.add(constraintPosition); } return; } // todo temporary hack // function literal without declaring receiver type { x -> ... } // can be considered as extension function if one is expected // (special type constructor for function/ extension function should be introduced like // PLACEHOLDER_FUNCTION_TYPE) if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(constrainingType) && kotlinBuiltIns.isExtensionFunctionType(subjectType)) { constrainingType = createCorrespondingExtensionFunctionType(constrainingType, DONT_CARE); } DeclarationDescriptor constrainingTypeDescriptor = constrainingType.getConstructor().getDeclarationDescriptor(); if (subjectTypeDescriptor instanceof TypeParameterDescriptor) { TypeParameterDescriptor typeParameter = (TypeParameterDescriptor) subjectTypeDescriptor; TypeConstraintsImpl typeConstraints = typeParameterConstraints.get(typeParameter); if (typeConstraints != null) { if (TypeUtils.dependsOnTypeParameterConstructors( constrainingType, Collections.singleton(DONT_CARE.getConstructor()))) { return; } if (subjectType.isNullable() && constrainingType.isNullable()) { constrainingType = TypeUtils.makeNotNullable(constrainingType); } typeConstraints.addBound(constraintKind, constrainingType); return; } } if (constrainingTypeDescriptor instanceof TypeParameterDescriptor) { assert typeParameterConstraints.get(constrainingTypeDescriptor) == null : "Constraining type contains type variable " + constrainingTypeDescriptor.getName(); } if (constraintKind == SUB_TYPE && kotlinBuiltIns.isNothingOrNullableNothing(constrainingType)) { // following constraints are always true: // 'Nothing' is a subtype of any type if (!constrainingType.isNullable()) return; // 'Nothing?' is a subtype of nullable type if (subjectType.isNullable()) return; } if (!(constrainingTypeDescriptor instanceof ClassDescriptor) || !(subjectTypeDescriptor instanceof ClassDescriptor)) { errorConstraintPositions.add(constraintPosition); return; } switch (constraintKind) { case SUB_TYPE: { if (kotlinBuiltIns.isNothingOrNullableNothing(constrainingType)) break; JetType correspondingSupertype = TypeCheckingProcedure.findCorrespondingSupertype(constrainingType, subjectType); if (correspondingSupertype != null) { constrainingType = correspondingSupertype; } break; } case SUPER_TYPE: { if (kotlinBuiltIns.isNothingOrNullableNothing(subjectType)) break; JetType correspondingSupertype = TypeCheckingProcedure.findCorrespondingSupertype(subjectType, constrainingType); if (correspondingSupertype != null) { subjectType = correspondingSupertype; } } case EQUAL: // nothing } if (constrainingType.getConstructor() != subjectType.getConstructor()) { errorConstraintPositions.add(constraintPosition); return; } TypeConstructor typeConstructor = subjectType.getConstructor(); List<TypeProjection> subjectArguments = subjectType.getArguments(); List<TypeProjection> constrainingArguments = constrainingType.getArguments(); List<TypeParameterDescriptor> parameters = typeConstructor.getParameters(); for (int i = 0; i < subjectArguments.size(); i++) { Variance typeParameterVariance = parameters.get(i).getVariance(); TypeProjection subjectArgument = subjectArguments.get(i); TypeProjection constrainingArgument = constrainingArguments.get(i); ConstraintKind typeParameterConstraintKind = getTypeParameterConstraintKind( typeParameterVariance, subjectArgument, constrainingArgument, constraintKind); addConstraint( typeParameterConstraintKind, subjectArgument.getType(), constrainingArgument.getType(), constraintPosition); } }