From 51c5a54e31db6e5b1a715d793d1a86460a4aa0af Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Wed, 30 Jun 2021 16:21:58 +0300 Subject: [PATCH] Support type inference for self type materialization calls --- .../kotlin/fir/types/ConeInferenceContext.kt | 5 +++ .../kotlin/fir/types/ConeTypeContext.kt | 13 +++++- .../jetbrains/kotlin/fir/types/TypeUtils.kt | 3 ++ .../kotlin/ir/types/IrTypeSystemContext.kt | 6 ++- .../resolve/calls/inference/InferenceUtils.kt | 10 +++++ .../components/ResultTypeResolver.kt | 8 ++-- .../components/VariableFixationFinder.kt | 8 ++++ .../kotlin/types/AbstractTypeApproximator.kt | 9 ++++ .../types/TypeApproximatorConfiguration.kt | 7 ++++ .../ClassicTypeSystemContextForCS.kt | 4 +- .../KotlinConstraintSystemCompleter.kt | 32 --------------- .../basicInferenceForImplicitSelfType.fir.kt | 8 ---- .../writerAppenderExampleRecursive.fir.kt | 19 ++++++--- .../writerAppenderExampleRecursive.kt | 17 ++++++-- .../writerAppenderExampleRecursive.txt | 13 ++++-- .../kotlin/types/AbstractTypeChecker.kt | 4 +- .../types/TypeSystemCommonBackendContext.kt | 1 - .../kotlin/types/model/TypeSystemContext.kt | 21 +++++++++- .../kotlin/types/StarProjectionImpl.kt | 41 ++++++++++++++----- .../org/jetbrains/kotlin/types/TypeUtils.kt | 22 ++++++---- .../types/checker/ClassicTypeSystemContext.kt | 21 ++++++---- 21 files changed, 183 insertions(+), 89 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index be58aabee4a..939c94edaad 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -270,6 +270,11 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo return this.withArguments(newArguments.cast>().toTypedArray(), this@ConeInferenceContext) } + override fun SimpleTypeMarker.replaceArguments(replacement: (TypeArgumentMarker) -> TypeArgumentMarker): SimpleTypeMarker { + require(this is ConeKotlinType) + return this.withArguments({ replacement(it).cast() }, this@ConeInferenceContext) + } + override fun KotlinTypeMarker.hasExactAnnotation(): Boolean { require(this is ConeKotlinType) return attributes.exact != null diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt index 9b91f292d39..fc40e0507f2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt @@ -239,6 +239,15 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty } } + override fun TypeConstructorMarker.getParameters(): List { + return when (val symbol = toClassLikeSymbol()) { + is FirAnonymousObjectSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() } + is FirRegularClassSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() } + is FirTypeAliasSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() } + else -> error("Unexpected FirClassLikeSymbol $symbol for ${this::class}, with classId ${(this as? ConeClassLikeLookupTag)?.classId}") + } + } + private fun TypeConstructorMarker.toClassLikeSymbol(): FirClassLikeSymbol<*>? = (this as? ConeClassLikeLookupTag)?.toSymbol(session) override fun TypeConstructorMarker.supertypes(): Collection { @@ -297,11 +306,11 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty return this } - override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker): Boolean { + override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker?): Boolean { require(this is ConeTypeParameterLookupTag) return this.typeParameterSymbol.fir.bounds.any { typeRef -> typeRef.coneType.contains { it.typeConstructor() == this.getTypeConstructor() } - && typeRef.coneType.typeConstructor() == selfConstructor + && (selfConstructor == null || typeRef.coneType.typeConstructor() == selfConstructor) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 37e269ab2b0..099e88ca421 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -89,6 +89,9 @@ fun T.withArguments(arguments: Array T.withArguments(replacement: (ConeTypeProjection) -> ConeTypeProjection, typeSystemContext: ConeTypeContext) = + withArguments(typeArguments.map(replacement).toTypedArray(), typeSystemContext) + fun T.withAttributes(attributes: ConeAttributes, typeSystemContext: ConeTypeContext): T { if (this.attributes == attributes) { return this diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt index a12e0ea91ab..a7ac20c3a2a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeSystemContext.kt @@ -139,6 +139,8 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon override fun TypeConstructorMarker.getParameter(index: Int) = getTypeParameters(this)[index].symbol + override fun TypeConstructorMarker.getParameters() = getTypeParameters(this).map { it.symbol } + override fun TypeConstructorMarker.supertypes(): Collection { return when (this) { is IrCapturedType.Constructor -> superTypes @@ -179,10 +181,10 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon return false } - override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker): Boolean { + override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker?): Boolean { for (i in 0 until this.upperBoundCount()) { val upperBound = this.getUpperBound(i) - if (upperBound.containsTypeConstructor(selfConstructor) && upperBound.typeConstructor() == selfConstructor) { + if (upperBound.containsTypeConstructor(this.getTypeConstructor()) && (selfConstructor == null || upperBound.typeConstructor() == selfConstructor)) { return true } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt index 03b695ccc01..f0dd7c117e5 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/InferenceUtils.kt @@ -5,7 +5,9 @@ package org.jetbrains.kotlin.resolve.calls.inference +import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage +import org.jetbrains.kotlin.resolve.calls.inference.model.DeclaredUpperBoundConstraintPosition import org.jetbrains.kotlin.types.model.* fun ConstraintStorage.buildCurrentSubstitutor( @@ -46,3 +48,11 @@ fun ConstraintStorage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor( notFixedTypeVariables.mapValues { context.createStubTypeForTypeVariablesInSubtyping(it.value.typeVariable) } ) } + +fun TypeSystemInferenceExtensionContext.hasDeclaredUpperBoundSelfTypes(constraint: Constraint): Boolean { + val typeConstructor = constraint.type.typeConstructor() + + return constraint.position.from is DeclaredUpperBoundConstraintPosition<*> + && (typeConstructor.getParameters().any { it.hasRecursiveBounds(typeConstructor) } + || typeConstructor.getTypeParameterClassifier()?.hasRecursiveBounds() == true) +} diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index d4c4cd752cc..daab292fde1 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator.ResolveDirection +import org.jetbrains.kotlin.resolve.calls.inference.hasDeclaredUpperBoundSelfTypes import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.types.AbstractTypeApproximator import org.jetbrains.kotlin.types.AbstractTypeChecker @@ -210,8 +211,9 @@ class ResultTypeResolver( } private fun Context.findSuperType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? { - val upperConstraints = - variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperTypeForFixation(it.type) } + val upperConstraints = variableWithConstraints.constraints.filter { + it.kind == ConstraintKind.UPPER && (hasDeclaredUpperBoundSelfTypes(it) || isProperTypeForFixation(it.type)) + } if (upperConstraints.isNotEmpty()) { val intersectionUpperType = intersectTypes(upperConstraints.map { it.type }) val resultIsActuallyIntersection = intersectionUpperType.typeConstructor().isIntersection() @@ -238,7 +240,7 @@ class ResultTypeResolver( return typeApproximator.approximateToSubType( upperType, - TypeApproximatorConfiguration.InternalTypesApproximation + TypeApproximatorConfiguration.InternalAndSelfTypesApproximation ) ?: upperType } return null diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt index a81b764a5a1..bd20eb163c4 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/VariableFixationFinder.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode.PARTIAL +import org.jetbrains.kotlin.resolve.calls.inference.hasDeclaredUpperBoundSelfTypes import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint import org.jetbrains.kotlin.resolve.calls.inference.model.DeclaredUpperBoundConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints @@ -48,6 +49,7 @@ class VariableFixationFinder( FROM_INCORPORATION_OF_DECLARED_UPPER_BOUND, READY_FOR_FIXATION_UPPER, READY_FOR_FIXATION_LOWER, + READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES, READY_FOR_FIXATION, READY_FOR_FIXATION_REIFIED, } @@ -61,6 +63,7 @@ class VariableFixationFinder( ): TypeVariableFixationReadiness = when { !notFixedTypeVariables.contains(variable) || dependencyProvider.isVariableRelatedToTopLevelType(variable) -> TypeVariableFixationReadiness.FORBIDDEN + hasDeclaredUpperBoundSelfTypes(variable) -> TypeVariableFixationReadiness.READY_FOR_FIXATION_DECLARED_UPPER_BOUND_WITH_SELF_TYPES !variableHasProperArgumentConstraints(variable) -> TypeVariableFixationReadiness.WITHOUT_PROPER_ARGUMENT_CONSTRAINT hasDependencyToOtherTypeVariables(variable) -> TypeVariableFixationReadiness.WITH_COMPLEX_DEPENDENCY variableHasTrivialOrNonProperConstraints(variable) -> TypeVariableFixationReadiness.WITH_TRIVIAL_OR_NON_PROPER_CONSTRAINTS @@ -166,6 +169,11 @@ class VariableFixationFinder( it.kind.isLower() && isProperArgumentConstraint(it) && !it.type.typeConstructor().isNothingConstructor() } } + + private fun Context.hasDeclaredUpperBoundSelfTypes(variable: TypeConstructorMarker): Boolean { + val constraints = notFixedTypeVariables[variable]?.constraints ?: return false + return constraints.isNotEmpty() && constraints.all(::hasDeclaredUpperBoundSelfTypes) + } } inline fun TypeSystemInferenceExtensionContext.isProperTypeForFixation(type: KotlinTypeMarker, isProper: (KotlinTypeMarker) -> Boolean) = diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt index 156fe9beb1d..2ee1aba3d39 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt @@ -404,6 +404,15 @@ abstract class AbstractTypeApproximator( if (argument.isStarProjection()) continue + val argumentTypeConstructor = argument.getType().typeConstructor() + + if (argumentTypeConstructor is TypeVariableTypeConstructorMarker && conf.selfTypesWithTypeVariablesToCapturedStarProjection) { + // If we have Self where T is bounded by Self, we approximate it to CapturedType(*) to satisfy constraints + if (argumentTypeConstructor.typeParameter?.hasRecursiveBounds(type.typeConstructor()) == true) { + return createCapturedStarProjectionForSelfType(argumentTypeConstructor, type) + } + } + val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance()) val argumentType = newArguments[index]?.getType() ?: argument.getType() diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt index 914637e8238..5c0ecf6cb04 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt @@ -24,6 +24,7 @@ open class TypeApproximatorConfiguration { open val intersection: IntersectionStrategy = IntersectionStrategy.TO_COMMON_SUPERTYPE open val intersectionTypesInContravariantPositions = false open val localTypes = false + open val selfTypesWithTypeVariablesToCapturedStarProjection = false open val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean = { false } open fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean = @@ -73,6 +74,12 @@ open class TypeApproximatorConfiguration { override val intersectionTypesInContravariantPositions: Boolean get() = true } + object InternalAndSelfTypesApproximation : AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) { + override val integerLiteralType: Boolean get() = true + override val intersectionTypesInContravariantPositions: Boolean get() = true + override val selfTypesWithTypeVariablesToCapturedStarProjection = true + } + object FinalApproximationAfterResolutionAndInference : AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) { override val integerLiteralType: Boolean get() = true diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ClassicTypeSystemContextForCS.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ClassicTypeSystemContextForCS.kt index d1aeac8d9c7..bd338fe6e38 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ClassicTypeSystemContextForCS.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ClassicTypeSystemContextForCS.kt @@ -41,7 +41,7 @@ class ClassicTypeSystemContextForCS(override val builtIns: KotlinBuiltIns) : Typ captureStatus: CaptureStatus ): CapturedTypeMarker { require(lowerType is UnwrappedType?, lowerType::errorMessage) - require(constructorProjection is TypeProjectionImpl, constructorProjection::errorMessage) + require(constructorProjection is TypeProjectionBase, constructorProjection::errorMessage) @Suppress("UNCHECKED_CAST") val newCapturedTypeConstructor = NewCapturedTypeConstructor( @@ -49,7 +49,7 @@ class ClassicTypeSystemContextForCS(override val builtIns: KotlinBuiltIns) : Typ constructorSupertypes as List ) return NewCapturedType( - CaptureStatus.FOR_INCORPORATION, + captureStatus, newCapturedTypeConstructor, lowerType = lowerType ) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index e642300118b..cfe45b8097f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -299,11 +299,6 @@ class KotlinConstraintSystemCompleter( return true } - hasDeclaredUpperBoundSelfTypes(variableWithConstraints) -> { - fixVariable(this, variableWithConstraints, topLevelAtoms) - return true - } - else -> processVariableWhenNotEnoughInformation(this, variableWithConstraints, topLevelAtoms, diagnosticsHolder) } } @@ -311,33 +306,6 @@ class KotlinConstraintSystemCompleter( return false } - private fun ConstraintSystemCompletionContext.hasDeclaredUpperBoundSelfTypes(variable: VariableWithConstraints): Boolean { - val constraints = variable.constraints - return constraints.isNotEmpty() && constraints.all { - it.position.from is DeclaredUpperBoundConstraintPosition<*> && isSelfType(it.type.typeConstructor()) - } - } - - private fun ConstraintSystemCompletionContext.isSelfType(typeConstructor: TypeConstructorMarker): Boolean { - if (typeConstructor.isTypeParameterTypeConstructor()) { - val supertype = typeConstructor.supertypes().firstOrNull() ?: return false - return isSelfType(supertype.typeConstructor()) - } - - val parametersCount = typeConstructor.parametersCount() - if (parametersCount == 0) return false - - for (parameterId in 0 until parametersCount) { - val parameter = typeConstructor.getParameter(parameterId) - if (parameter.upperBoundCount() != 1) return false - - val upperBound = parameter.getUpperBound(0) - if (upperBound.typeConstructor() == typeConstructor) return true - } - - return false - } - private fun processVariableWhenNotEnoughInformation( c: ConstraintSystemCompletionContext, variableWithConstraints: VariableWithConstraints, diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt index 905f12fc3ab..2ca7ce11f42 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/basicInferenceForImplicitSelfType.fir.kt @@ -18,12 +18,4 @@ fun > testTypeParam(builder: Builder) { builder .test() .foo() -} - -interface BodySpec> { - fun isEqualTo(expected: B): T -} - -fun test(b: BodySpec) { - b.isEqualTo("") } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt index a12bdc16f6a..6f90e01727d 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.fir.kt @@ -1,15 +1,24 @@ fun test() { - WriterAppender.newBuilder() + ")!>WriterAppender.newBuilder() + >")!>WriterAppender.Builder1() + + & Builder2<*>}")!>WriterAppender.intersectTwoSelfTypes() } object WriterAppender { - class Builder> { + interface Builder2> + + class Builder1> { fun asBuilder(): B { - return this as B + return this as B } } - fun > newBuilder(): B { - return Builder().asBuilder() + fun > newBuilder(): B { + return Builder1().asBuilder() + } + + fun intersectTwoSelfTypes(): B where B : Builder1, B: Builder2 { + return Builder1().asBuilder() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt index 84256060a91..6f90e01727d 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.kt @@ -1,15 +1,24 @@ fun test() { - WriterAppender.newBuilder() + ")!>WriterAppender.newBuilder() + >")!>WriterAppender.Builder1() + + & Builder2<*>}")!>WriterAppender.intersectTwoSelfTypes() } object WriterAppender { - class Builder> { + interface Builder2> + + class Builder1> { fun asBuilder(): B { return this as B } } - fun > newBuilder(): B { - return Builder().asBuilder() + fun > newBuilder(): B { + return Builder1().asBuilder() + } + + fun intersectTwoSelfTypes(): B where B : Builder1, B: Builder2 { + return Builder1().asBuilder() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt index 2272f255742..fe013b5adc8 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/writerAppenderExampleRecursive.txt @@ -6,14 +6,21 @@ public object WriterAppender { private constructor WriterAppender() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public final fun > newBuilder(): B + public final fun > intersectTwoSelfTypes(): B where B : WriterAppender.Builder2 + public final fun > newBuilder(): B public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String - public final class Builder> { - public constructor Builder>() + public final class Builder1> { + public constructor Builder1>() public final fun asBuilder(): B public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + + public interface Builder2> { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index 0b24b602cba..f8a362f3e5d 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -528,7 +528,9 @@ object AbstractTypeChecker { * so if CapturedType(out Bar) is the same as a type of Foo's argument and Foo is a self type, then subtyping should return true. * If we don't handle this case separately, subtyping may not converge due to the nature of the capturing. */ - if (subType is CapturedTypeMarker) { + val subTypeConstructor = subType.typeConstructor() + if (subType is CapturedTypeMarker + || (subTypeConstructor.isIntersection() && subTypeConstructor.supertypes().all { it is CapturedTypeMarker })) { val typeParameter = context.typeSystemContext.getTypeParameterForArgumentInBaseIfItEqualToTarget(baseType = superType, targetType = subType) if (typeParameter != null && typeParameter.hasRecursiveBounds(superType.typeConstructor())) { diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt index 75715abd8b3..fa89644aef7 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/TypeSystemCommonBackendContext.kt @@ -30,7 +30,6 @@ interface TypeSystemCommonBackendContext : TypeSystemContext { */ fun KotlinTypeMarker.getAnnotationFirstArgumentValue(fqName: FqName): Any? - fun TypeConstructorMarker.getTypeParameterClassifier(): TypeParameterMarker? fun TypeConstructorMarker.isInlineClass(): Boolean fun TypeConstructorMarker.isInnerClass(): Boolean fun TypeParameterMarker.getRepresentativeUpperBound(): KotlinTypeMarker diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index cffae338c38..8c3d12d72b9 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -175,6 +175,7 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker fun SimpleTypeMarker.replaceArguments(newArguments: List): SimpleTypeMarker + fun SimpleTypeMarker.replaceArguments(replacement: (TypeArgumentMarker) -> TypeArgumentMarker): SimpleTypeMarker fun KotlinTypeMarker.hasExactAnnotation(): Boolean fun KotlinTypeMarker.hasNoInferAnnotation(): Boolean @@ -248,6 +249,22 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui * In future once we have only FIR (or FE 1.0 behavior is fixed) this method should be inlined to the use-site */ fun SimpleTypeMarker.createConstraintPartForLowerBoundAndFlexibleTypeVariable(): KotlinTypeMarker + + fun createCapturedStarProjectionForSelfType( + typeVariable: TypeVariableTypeConstructorMarker, + selfType: SimpleTypeMarker, + ): SimpleTypeMarker? { + val typeParameter = typeVariable.typeParameter ?: return null + val starProjection = createStarProjection(typeParameter) + val superType = selfType.replaceArguments { + val constructor = it.getType().typeConstructor() + if (constructor is TypeVariableTypeConstructorMarker && constructor == typeVariable) { + starProjection + } else it + } + + return createCapturedType(starProjection, listOf(superType), lowerType = null, CaptureStatus.FROM_EXPRESSION) + } } @@ -313,12 +330,14 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun TypeConstructorMarker.parametersCount(): Int fun TypeConstructorMarker.getParameter(index: Int): TypeParameterMarker + fun TypeConstructorMarker.getParameters(): List fun TypeConstructorMarker.supertypes(): Collection fun TypeConstructorMarker.isIntersection(): Boolean fun TypeConstructorMarker.isClassTypeConstructor(): Boolean fun TypeConstructorMarker.isInterface(): Boolean fun TypeConstructorMarker.isIntegerLiteralTypeConstructor(): Boolean fun TypeConstructorMarker.isLocalType(): Boolean + fun TypeConstructorMarker.getTypeParameterClassifier(): TypeParameterMarker? val TypeVariableTypeConstructorMarker.typeParameter: TypeParameterMarker? @@ -327,7 +346,7 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun TypeParameterMarker.getUpperBound(index: Int): KotlinTypeMarker fun TypeParameterMarker.getUpperBounds(): List fun TypeParameterMarker.getTypeConstructor(): TypeConstructorMarker - fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker): Boolean + fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker? = null): Boolean fun areEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker): Boolean diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/StarProjectionImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/types/StarProjectionImpl.kt index 79cc27f7f13..b0b75d84a04 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/StarProjectionImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/StarProjectionImpl.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.types import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner @@ -45,18 +46,38 @@ class StarProjectionImpl( } } -fun TypeParameterDescriptor.starProjectionType(): KotlinType { - val classDescriptor = this.containingDeclaration as ClassifierDescriptorWithTypeParameters - val typeParameters = classDescriptor.typeConstructor.parameters.map { it.typeConstructor } - return TypeSubstitutor.create( - object : TypeConstructorSubstitution() { - override fun get(key: TypeConstructor) = - if (key in typeParameters) - TypeUtils.makeStarProjection(key.declarationDescriptor as TypeParameterDescriptor) - else null +private fun buildStarProjectionTypeByTypeParameters( + typeParameters: List, + upperBounds: List, + builtIns: KotlinBuiltIns +) = TypeSubstitutor.create( + object : TypeConstructorSubstitution() { + override fun get(key: TypeConstructor) = + if (key in typeParameters) + TypeUtils.makeStarProjection(key.declarationDescriptor as TypeParameterDescriptor) + else null + } +).substitute(upperBounds.first(), Variance.OUT_VARIANCE) ?: builtIns.defaultBound + +fun TypeParameterDescriptor.starProjectionType(): KotlinType { + return when (val descriptor = this.containingDeclaration) { + is ClassifierDescriptorWithTypeParameters -> { + buildStarProjectionTypeByTypeParameters( + typeParameters = descriptor.typeConstructor.parameters.map { it.typeConstructor }, + upperBounds, + builtIns + ) } - ).substitute(this.upperBounds.first(), Variance.OUT_VARIANCE) ?: builtIns.defaultBound + is FunctionDescriptor -> { + buildStarProjectionTypeByTypeParameters( + typeParameters = descriptor.typeParameters.map { it.typeConstructor }, + upperBounds, + builtIns + ) + } + else -> throw IllegalArgumentException("Unsupported descriptor type to build star projection type based on type parameters of it") + } } // It should only be used in rare cases when type parameter for the relevant argument is not available diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index b591c3af882..e173bf075a0 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.isCaptured import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.* +import org.jetbrains.kotlin.types.model.TypeArgumentMarker import org.jetbrains.kotlin.types.model.TypeVariableTypeConstructorMarker import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.util.* @@ -182,8 +183,8 @@ fun KotlinType.getImmediateSuperclassNotAny(): KotlinType? { fun KotlinType.asTypeProjection(): TypeProjection = TypeProjectionImpl(this) fun KotlinType.contains(predicate: (UnwrappedType) -> Boolean) = TypeUtils.contains(this, predicate) -fun KotlinType.replaceArgumentsWithStarProjections() = replaceArgumentsWith(::StarProjectionImpl) -fun KotlinType.replaceArgumentsWithNothing() = replaceArgumentsWith { it.builtIns.nothingType.asTypeProjection() } +fun KotlinType.replaceArgumentsWithStarProjections() = replaceArgumentsByParametersWith(::StarProjectionImpl) +fun KotlinType.replaceArgumentsWithNothing() = replaceArgumentsByParametersWith { it.builtIns.nothingType.asTypeProjection() } fun KotlinType.extractTypeParametersFromUpperBounds(visitedTypeParameters: Set?): Set = mutableSetOf().also { extractTypeParametersFromUpperBounds(this, it, visitedTypeParameters) } @@ -247,7 +248,7 @@ fun KotlinType.replaceArgumentsWithStarProjectionOrMapped( variance: Variance, visitedTypeParameters: Set? ) = - replaceArgumentsWith { typeParameterDescriptor -> + replaceArgumentsByParametersWith { typeParameterDescriptor -> val argument = arguments.getOrNull(typeParameterDescriptor.index) val isTypeParameterVisited = visitedTypeParameters != null && typeParameterDescriptor in visitedTypeParameters if (!isTypeParameterVisited && argument != null && argument.type.constructor in substitutionMap) { @@ -256,18 +257,18 @@ fun KotlinType.replaceArgumentsWithStarProjectionOrMapped( }.let { substitutor.safeSubstitute(it, variance) } -inline fun KotlinType.replaceArgumentsWith(replacement: (TypeParameterDescriptor) -> TypeProjection): KotlinType { +inline fun KotlinType.replaceArgumentsByParametersWith(replacement: (TypeParameterDescriptor) -> TypeProjection): KotlinType { val unwrapped = unwrap() return when (unwrapped) { is FlexibleType -> KotlinTypeFactory.flexibleType( - unwrapped.lowerBound.replaceArgumentsWith(replacement), - unwrapped.upperBound.replaceArgumentsWith(replacement) + unwrapped.lowerBound.replaceArgumentsByParametersWith(replacement), + unwrapped.upperBound.replaceArgumentsByParametersWith(replacement) ) - is SimpleType -> unwrapped.replaceArgumentsWith(replacement) + is SimpleType -> unwrapped.replaceArgumentsByParametersWith(replacement) }.inheritEnhancement(unwrapped) } -inline fun SimpleType.replaceArgumentsWith(replacement: (TypeParameterDescriptor) -> TypeProjection): SimpleType { +inline fun SimpleType.replaceArgumentsByParametersWith(replacement: (TypeParameterDescriptor) -> TypeProjection): SimpleType { if (constructor.parameters.isEmpty() || constructor.declarationDescriptor == null) return this val newArguments = constructor.parameters.map(replacement) @@ -275,6 +276,11 @@ inline fun SimpleType.replaceArgumentsWith(replacement: (TypeParameterDescriptor return replace(newArguments) } +inline fun SimpleType.replaceArgumentsByExistingArgumentsWith(replacement: (TypeArgumentMarker) -> TypeArgumentMarker): SimpleType { + if (arguments.isEmpty()) return this + return replace(newArguments = arguments.map { replacement(it) as TypeProjection }) +} + fun KotlinType.containsTypeAliasParameters(): Boolean = contains { it.constructor.declarationDescriptor?.isTypeAliasParameter() ?: false diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt index f9518e9353f..d1a6c7fbc87 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.inference.CapturedType -import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructor import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.isInlineClass @@ -25,10 +24,7 @@ import org.jetbrains.kotlin.resolve.substitutedUnderlyingType import org.jetbrains.kotlin.resolve.unsubstitutedUnderlyingType import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.model.* -import org.jetbrains.kotlin.types.typeUtil.asTypeProjection -import org.jetbrains.kotlin.types.typeUtil.contains -import org.jetbrains.kotlin.types.typeUtil.hasTypeParameterRecursiveBounds -import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound +import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType as classicIsSignedOrUnsignedNumberType @@ -210,6 +206,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy return this.parameters[index] } + override fun TypeConstructorMarker.getParameters(): List { + require(this is TypeConstructor, this::errorMessage) + return this.parameters + } + override fun TypeConstructorMarker.supertypes(): Collection { require(this is TypeConstructor, this::errorMessage) return this.supertypes @@ -240,9 +241,9 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy return this.typeConstructor } - override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker): Boolean { + override fun TypeParameterMarker.hasRecursiveBounds(selfConstructor: TypeConstructorMarker?): Boolean { require(this is TypeParameterDescriptor, this::errorMessage) - require(selfConstructor is TypeConstructor, this::errorMessage) + require(selfConstructor is TypeConstructor?, this::errorMessage) return hasTypeParameterRecursiveBounds(this, selfConstructor) } @@ -528,6 +529,12 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy return this.replace(newArguments as List) } + override fun SimpleTypeMarker.replaceArguments(replacement: (TypeArgumentMarker) -> TypeArgumentMarker): SimpleTypeMarker { + require(this is SimpleType, this::errorMessage) + @Suppress("UNCHECKED_CAST") + return this.replaceArgumentsByExistingArgumentsWith(replacement) + } + override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker { require(this is DefinitelyNotNullType, this::errorMessage) return this.original