From 53caa84db9c1624c9107b86ff5161ff51e72a96c Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Fri, 7 Apr 2017 13:24:34 +0300 Subject: [PATCH] [NI] Add constrains even we try add constraint like TypeVariable <: CapturedType from subtyping. If such captured type has lower type, then from TypeVariable <: lowerType => TypeVariable <: CapturedType. --- .../components/ConstraintInjector.kt | 48 +++++++++++++------ .../kotlin/types/TypeApproximator.kt | 26 ++++------ .../capturedTypes/captureFromSubtyping.kt | 9 ++++ .../capturedTypes/captureFromSubtyping.txt | 10 ++++ .../checkers/DiagnosticsTestGenerated.java | 6 +++ .../kotlin/types/checker/NewCapturedType.kt | 2 +- 6 files changed, 67 insertions(+), 34 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.kt create mode 100644 compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.txt diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt index 3f10a0b572b..0da3d6ab6d8 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -16,19 +16,17 @@ package org.jetbrains.kotlin.resolve.calls.inference.components +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic -import org.jetbrains.kotlin.types.FlexibleType -import org.jetbrains.kotlin.types.SimpleType -import org.jetbrains.kotlin.types.TypeConstructor -import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.CaptureStatus import org.jetbrains.kotlin.types.checker.NewCapturedType import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.contains import java.util.* -class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator) { +class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val typeApproximator: TypeApproximator) { private val ALLOWED_DEPTH_DELTA_FOR_INCORPORATION = 3 interface Context { @@ -119,24 +117,44 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator) { override fun addLowerConstraint(typeVariable: TypeConstructor, subType: UnwrappedType) = addConstraint(typeVariable, subType, ConstraintKind.LOWER) + private fun isCapturedTypeFromSubtyping(type: UnwrappedType) = + when ((type as? NewCapturedType)?.captureStatus) { + null, CaptureStatus.FROM_EXPRESSION -> false + CaptureStatus.FOR_SUBTYPING -> true + CaptureStatus.FOR_INCORPORATION -> + error("Captured type for incorporation shouldn't escape from incorporation: $type\n" + renderBaseConstraint()) + } + private fun addConstraint(typeVariableConstructor: TypeConstructor, type: UnwrappedType, kind: ConstraintKind) { val typeVariable = c.allTypeVariables[typeVariableConstructor] ?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}") - if (type.contains { - val captureStatus = (it as? NewCapturedType)?.captureStatus - assert(captureStatus != CaptureStatus.FOR_INCORPORATION) { - "Captured type for incorporation shouldn't escape from incorporation: $type\n" + renderBaseConstraint() + var targetType = type + if (type.contains(this::isCapturedTypeFromSubtyping)) { + // TypeVariable <: type -> if TypeVariable <: subType => TypeVariable <: type + if (kind == ConstraintKind.UPPER) { + val subType = typeApproximator.approximateToSubType(type, TypeApproximatorConfiguration.SubtypeCapturedTypesApproximation) + if (subType != null && !KotlinBuiltIns.isNothingOrNullableNothing(subType)) { + targetType = subType + } + } + + if (kind == ConstraintKind.LOWER) { + val superType = typeApproximator.approximateToSuperType(type, TypeApproximatorConfiguration.SubtypeCapturedTypesApproximation) + if (superType != null && !KotlinBuiltIns.isAnyOrNullableAny(superType)) { // todo rethink error reporting for Any cases + targetType = superType + } + } + + if (targetType === type) { + c.addError(CapturedTypeFromSubtyping(typeVariable, type, position)) + return } - captureStatus != null && captureStatus != CaptureStatus.FROM_EXPRESSION - }) { - c.addError(CapturedTypeFromSubtyping(typeVariable, type, position)) - return } - if (!c.isAllowedType(type)) return + if (!c.isAllowedType(targetType)) return - val newConstraint = Constraint(kind, type, position) + val newConstraint = Constraint(kind, targetType, position) possibleNewConstraints.add(typeVariable to newConstraint) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt index 8b7efef8de8..6ff74ad76ff 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/types/TypeApproximator.kt @@ -19,12 +19,8 @@ package org.jetbrains.kotlin.types import org.jetbrains.kotlin.resolve.calls.components.CommonSupertypeCalculator import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor import org.jetbrains.kotlin.types.TypeApproximatorConfiguration.IntersectionStrategy.* -import org.jetbrains.kotlin.types.checker.CaptureStatus.FOR_INCORPORATION -import org.jetbrains.kotlin.types.checker.CaptureStatus.FROM_EXPRESSION -import org.jetbrains.kotlin.types.checker.NewCapturedType -import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor -import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker -import org.jetbrains.kotlin.types.checker.intersectTypes +import org.jetbrains.kotlin.types.checker.* +import org.jetbrains.kotlin.types.checker.CaptureStatus.* import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.types.typeUtil.isNothing @@ -64,24 +60,18 @@ open class TypeApproximatorConfiguration { override val allFlexible get() = true } - object IncorporationConfiguration : TypeApproximatorConfiguration.AllFlexibleSameValue() { + abstract class AbstractCapturedTypesApproximation(val approximatedCapturedStatus: CaptureStatus): TypeApproximatorConfiguration.AllFlexibleSameValue() { override val allFlexible get() = true - // i.e. will be approximated only FOR_INCORPORATION captured types - override val capturedType get() = { it: NewCapturedType -> it.captureStatus != FOR_INCORPORATION } - override val intersection get() = IntersectionStrategy.ALLOWED - override val typeVariable: (TypeVariableTypeConstructor) -> Boolean get() = { true } - } - - object CapturedTypesApproximation : TypeApproximatorConfiguration.AllFlexibleSameValue() { - override val allFlexible get() = true - - // i.e. will be approximated only FROM_EXPRESSION captured types - override val capturedType get() = { it: NewCapturedType -> it.captureStatus != FROM_EXPRESSION } + // i.e. will be approximated only approximatedCapturedStatus captured types + override val capturedType get() = { it: NewCapturedType -> it.captureStatus != approximatedCapturedStatus } override val intersection get() = IntersectionStrategy.ALLOWED override val typeVariable: (TypeVariableTypeConstructor) -> Boolean get() = { true } } + object IncorporationConfiguration : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_INCORPORATION) + object SubtypeCapturedTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_SUBTYPING) + object CapturedTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FROM_EXPRESSION) } class TypeApproximator(private val commonSupertypeCalculator: CommonSupertypeCalculator) { diff --git a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.kt b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.kt new file mode 100644 index 00000000000..f5cb4510ff3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.kt @@ -0,0 +1,9 @@ +fun > mapKeysTo(destination: M): Inv3 { + val foo = associateByTo(destination) + + return foo +} + +fun < Y, Z, T : MutableMap> associateByTo(destination: T): Inv3 = TODO() + +interface Inv3 diff --git a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.txt b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.txt new file mode 100644 index 00000000000..bac64883f84 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.txt @@ -0,0 +1,10 @@ +package + +public fun > associateByTo(/*0*/ destination: T): Inv3 +public fun > mapKeysTo(/*0*/ destination: M): Inv3 + +public interface Inv3 { + 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/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index fbe606d428c..ff9db612d00 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10242,6 +10242,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("captureFromSubtyping.kt") + public void testCaptureFromSubtyping() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromSubtyping.kt"); + doTest(fileName); + } + @TestMetadata("captureFromTypeParameterUpperBound.kt") public void testCaptureFromTypeParameterUpperBound() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt index 23f9f1a51d1..c650d889843 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewCapturedType.kt @@ -122,7 +122,7 @@ enum class CaptureStatus { class NewCapturedType( val captureStatus: CaptureStatus, override val constructor: NewCapturedTypeConstructor, - val lowerType: UnwrappedType?, + val lowerType: UnwrappedType?, // todo check lower type for nullable captured types override val annotations: Annotations = Annotations.EMPTY, override val isMarkedNullable: Boolean = false ): SimpleType() {