From 608cb01935925e08d04e059ca8a2c65e4834349f Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Wed, 22 Feb 2023 13:45:53 +0100 Subject: [PATCH] K2: Imitate K1 behavior for case of captured types with a raw supertype See the comments in the code, but mostly the motivation is that once it was decided to stick with such a legacy thing as raw types, we are ok with some corner-cases hacks for them (if there are not too many of them) and they don't break anything when there are no raw types in the code. ^KT-56616 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 12 ++++++ ...eeOldFrontendDiagnosticsTestGenerated.java | 12 ++++++ ...siOldFrontendDiagnosticsTestGenerated.java | 12 ++++++ .../kotlin/fir/types/ConeInferenceContext.kt | 5 +++ .../kotlin/types/AbstractTypeApproximator.kt | 12 ++++++ .../types/TypeApproximatorConfiguration.kt | 8 ++++ .../tests/j+k/rawTypesFromCaptured.fir.kt | 25 +++++++++++ .../tests/j+k/rawTypesFromCaptured.kt | 25 +++++++++++ .../tests/j+k/rawTypesFromCapturedOriginal.kt | 37 ++++++++++++++++ .../rawTypes/rawTypeInUpperBound.fir.kt | 42 ------------------- .../rawTypes/rawTypeInUpperBound.kt | 4 +- .../test/runners/DiagnosticTestGenerated.java | 12 ++++++ .../kotlin/types/model/TypeSystemContext.kt | 6 +++ .../types/checker/ClassicTypeSystemContext.kt | 4 ++ 14 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.fir.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt delete mode 100644 compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.fir.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 1c458873a1d..b19ea5d7916 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -19396,6 +19396,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/j+k/rawTypeScope.kt"); } + @Test + @TestMetadata("rawTypesFromCaptured.kt") + public void testRawTypesFromCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt"); + } + + @Test + @TestMetadata("rawTypesFromCapturedOriginal.kt") + public void testRawTypesFromCapturedOriginal() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt"); + } + @Test @TestMetadata("rawUpperBounds.kt") public void testRawUpperBounds() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 594e94e1d47..fbd133b61ca 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -19396,6 +19396,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/j+k/rawTypeScope.kt"); } + @Test + @TestMetadata("rawTypesFromCaptured.kt") + public void testRawTypesFromCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt"); + } + + @Test + @TestMetadata("rawTypesFromCapturedOriginal.kt") + public void testRawTypesFromCapturedOriginal() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt"); + } + @Test @TestMetadata("rawUpperBounds.kt") public void testRawUpperBounds() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 77cb54f9e4b..8f9a360601f 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -19402,6 +19402,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/j+k/rawTypeScope.kt"); } + @Test + @TestMetadata("rawTypesFromCaptured.kt") + public void testRawTypesFromCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt"); + } + + @Test + @TestMetadata("rawTypesFromCapturedOriginal.kt") + public void testRawTypesFromCapturedOriginal() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt"); + } + @Test @TestMetadata("rawUpperBounds.kt") public void testRawUpperBounds() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt index 634608ab951..6fe3c77723d 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeInferenceContext.kt @@ -558,6 +558,11 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo override fun useRefinedBoundsForTypeVariableInFlexiblePosition(): Boolean = true + override fun KotlinTypeMarker.convertToNonRaw(): KotlinTypeMarker { + require(this is ConeKotlinType) + return this.convertToNonRawVersion() + } + override fun createSubstitutorForSuperTypes(baseType: KotlinTypeMarker): TypeSubstitutorMarker? = if (baseType is ConeLookupTagBasedType) createSubstitutionForSupertype(baseType, session) else null 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 47db23dd54b..5e635a26241 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt @@ -297,6 +297,18 @@ abstract class AbstractTypeApproximator( type.isMarkedNullable() -> baseResult.withNullability(true) type.isProjectionNotNull() -> baseResult.withNullability(false) else -> baseResult + }.let { + when { + // This is just a hack that is necessary to preserve compatibility with K1 where return type of the calls + // if they contain a captured types with RAW supertype would be approximated to a regular non-raw flexible type + // See CapturedTypeApproximationKt.approximateCapturedTypes and especially the comment + // "// tod*: dynamic & raw type?" before it :) + // If we don't repeat that behavior, we would stumble upon KT-56616 with hardly having any workarounds. + isK2 && conf.convertToNonRawVersionAfterApproximationInK2 && it.isRawType() -> { + it.convertToNonRaw() + } + else -> it + } } } 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 f03d3d85879..e440b2d192d 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/types/TypeApproximatorConfiguration.kt @@ -26,6 +26,12 @@ open class TypeApproximatorConfiguration { open val intersectionTypesInContravariantPositions = false open val localTypes = false + /** + * Is only expected to be true for FinalApproximationAfterResolutionAndInference + * But it's only used for K2 to reproduce K1 behavior for the approximation of resolved calls + */ + open val convertToNonRawVersionAfterApproximationInK2 get() = false + /** * Whether to approximate anonymous type. This flag does not have any effect if `localTypes` is true because all anonymous types are * local. @@ -88,6 +94,8 @@ open class TypeApproximatorConfiguration { AbstractCapturedTypesApproximation(CaptureStatus.FROM_EXPRESSION) { override val integerLiteralConstantType: Boolean get() = true override val intersectionTypesInContravariantPositions: Boolean get() = true + + override val convertToNonRawVersionAfterApproximationInK2: Boolean get() = true } object TypeArgumentApproximation : AbstractCapturedTypesApproximation(null) { diff --git a/compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.fir.kt b/compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.fir.kt new file mode 100644 index 00000000000..bc02979a720 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.fir.kt @@ -0,0 +1,25 @@ +// ISSUE: KT-56616 + +// FILE: StubElement.java + +public interface StubElement { + E bar(E v); +} + +// FILE: StubBasedPsiElement.java + +public interface StubBasedPsiElement { + T foo1(); + + StubElement foo2(); +} + +// FILE: test.kt + +fun StubBasedPsiElement<*>.foo(): String? { + if ("".hashCode() == 0) { + return foo1().bar("") + } + + return foo2().bar("") +} diff --git a/compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt b/compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt new file mode 100644 index 00000000000..e88a6cced6f --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt @@ -0,0 +1,25 @@ +// ISSUE: KT-56616 + +// FILE: StubElement.java + +public interface StubElement { + E bar(E v); +} + +// FILE: StubBasedPsiElement.java + +public interface StubBasedPsiElement { + T foo1(); + + StubElement foo2(); +} + +// FILE: test.kt + +fun StubBasedPsiElement<*>.foo(): String? { + if ("".hashCode() == 0) { + return foo1().bar("") + } + + return foo2().bar("") +} diff --git a/compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt b/compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt new file mode 100644 index 00000000000..3692d0ca300 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt @@ -0,0 +1,37 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// ISSUE: KT-56616 + +// FILE: StubElement.java + +import org.jetbrains.annotations.NotNull; + +public interface StubElement { + E @NotNull [] getChildrenByType(@NotNull String filter, final E[] array); +} + +// FILE: PsiElement.java + +public interface PsiElement + +// FILE: StubBasedPsiElement.java + +public interface StubBasedPsiElement extends PsiElement { + Stub getStub(); +} + +// FILE: test.kt + +private val STRING_TEMPLATE_EMPTY_ARRAY = emptyArray() + +open class KtStringTemplateExpression : PsiElement + +fun StubBasedPsiElement<*>.foo(): KtStringTemplateExpression? { + stub?.let { + // K1: Array, was K2: Array + val expressions = it.getChildrenByType("", STRING_TEMPLATE_EMPTY_ARRAY) + // Ok in K1, Should not be error in K2 + return expressions.firstOrNull() + } + return null +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.fir.kt deleted file mode 100644 index 78ba2134bd4..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.fir.kt +++ /dev/null @@ -1,42 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE -// FILE: A.java - -import java.util.List; - -public class A { - List getChildrenStubs() { return null; } - void consume(T x) {} - - T produce() { return null; } -} - -// FILE: B.java - -public class B { - public E foo() { return null;} - E field; -} - -// FILE: Test.java - -public class Test { - static B rawB = null; -} - -// FILE: main.kt - -fun foo(x: B<*>) { - // TODO: In K1, x.foo() now is flexible type instead of raw, because of captured type approximation - // Works in K2 as expected: x.foo() returns raw `A`, thus it's `getChildrenStubs` has a type `MutableList..List<*>?` - val q: MutableList = x.foo().getChildrenStubs() - - // Raw(B).field erased to A..A? - Test.rawB.field = A() - val anyA: A = Test.rawB.field - - // FIR doesn't work here, because - // field has a type of just 'A' and it's not clear why should it accept 'String' at consume - // NB: some kind of BareTypeScope should be in use here - Test.rawB.field.consume("") - val y: Any = Test.rawB.field.produce() -} diff --git a/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.kt b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.kt index 26b400d8260..1f182636a99 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/rawTypes/rawTypeInUpperBound.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE // FILE: A.java @@ -26,8 +27,7 @@ public class Test { // FILE: main.kt fun foo(x: B<*>) { - // TODO: In K1, x.foo() now is flexible type instead of raw, because of captured type approximation - // Works in K2 as expected: x.foo() returns raw `A`, thus it's `getChildrenStubs` has a type `MutableList..List<*>?` + // x.foo() is flexible type instead of raw, because of captured type approximation val q: MutableList = x.foo().getChildrenStubs() // Raw(B).field erased to A..A? diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 7d62b8a1ad9..2521b62f629 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -19402,6 +19402,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/j+k/rawTypeScope.kt"); } + @Test + @TestMetadata("rawTypesFromCaptured.kt") + public void testRawTypesFromCaptured() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCaptured.kt"); + } + + @Test + @TestMetadata("rawTypesFromCapturedOriginal.kt") + public void testRawTypesFromCapturedOriginal() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/rawTypesFromCapturedOriginal.kt"); + } + @Test @TestMetadata("rawUpperBounds.kt") public void testRawUpperBounds() throws Exception { 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 5e83470185a..2c2ea1cac97 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 @@ -312,6 +312,11 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui */ fun useRefinedBoundsForTypeVariableInFlexiblePosition(): Boolean + /** + * It's only relevant for K2 (and is not expected to be implemented properly in other contexts) + */ + fun KotlinTypeMarker.convertToNonRaw(): KotlinTypeMarker + fun createCapturedStarProjectionForSelfType( typeVariable: TypeVariableTypeConstructorMarker, typesForRecursiveTypeParameters: List, @@ -357,6 +362,7 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun FlexibleTypeMarker.asDynamicType(): DynamicTypeMarker? fun KotlinTypeMarker.isRawType(): Boolean + fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker fun FlexibleTypeMarker.lowerBound(): SimpleTypeMarker 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 dfa044f13dc..6518f370637 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -158,6 +158,10 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy return this is RawType } + override fun KotlinTypeMarker.convertToNonRaw(): KotlinTypeMarker { + error("Is not expected to be called in K1") + } + override fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker { require(this is FlexibleType, this::errorMessage) return this.upperBound