From 867d7b5bca06a20692bfe056f159b6182cf99b61 Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Tue, 20 Apr 2021 14:41:13 +0300 Subject: [PATCH] Allow type variable fixation into intersection type if it isn't meaningless (i.e. has one or more final classes, or two or more open classes) ^KT-46186 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 + .../kotlin/fir/types/ConeTypeContext.kt | 4 + .../kotlin/ir/types/IrTypeSystemContext.kt | 4 + .../components/ResultTypeResolver.kt | 16 ++- .../tests/typeParameters/kt46186.fir.kt | 83 ++++++++++++++ .../tests/typeParameters/kt46186.kt | 83 ++++++++++++++ .../tests/typeParameters/kt46186.txt | 107 ++++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 + .../kotlin/types/model/TypeSystemContext.kt | 1 + .../types/checker/ClassicTypeSystemContext.kt | 6 + 10 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt46186.fir.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt46186.kt create mode 100644 compiler/testData/diagnostics/tests/typeParameters/kt46186.txt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 1f3e21a824a..03056df6aab 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -29029,6 +29029,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/typeParameters/kt42472.kt"); } + @Test + @TestMetadata("kt46186.kt") + public void testKt46186() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/kt46186.kt"); + } + @Test @TestMetadata("misplacedConstraints.kt") public void testMisplacedConstraints() throws Exception { 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 6e8c7f39903..9a357cf3dbc 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 @@ -261,6 +261,10 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty return this is ConeClassLikeLookupTag } + override fun TypeConstructorMarker.isInterface(): Boolean { + return ((this as? ConeClassLikeLookupTag)?.toClassLikeSymbol()?.fir as? FirClass)?.classKind == ClassKind.INTERFACE + } + override fun TypeParameterMarker.getVariance(): TypeVariance { require(this is ConeTypeParameterLookupTag) return this.symbol.fir.variance.convertVariance() 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 7e378bbfba6..6a699a8e8e5 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 @@ -158,6 +158,10 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon override fun TypeConstructorMarker.isClassTypeConstructor() = this is IrClassSymbol + override fun TypeConstructorMarker.isInterface(): Boolean { + return (this as? IrClassSymbol)?.owner?.isInterface == true + } + override fun TypeParameterMarker.getVariance() = (this as IrTypeParameterSymbol).owner.variance.convertVariance() private fun getSuperTypes(typeParameterMarker: TypeParameterMarker) = (typeParameterMarker as IrTypeParameterSymbol).owner.superTypes 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 691aa9db840..be9d87e9c87 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 @@ -216,10 +216,18 @@ class ResultTypeResolver( variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperTypeForFixation(it.type) } if (upperConstraints.isNotEmpty()) { val intersectionUpperType = intersectTypes(upperConstraints.map { it.type }) - val isThereExpectedTypeConstraint = upperConstraints.any { it.isExpectedTypePosition() } - val nonExpectedTypeConstraints = upperConstraints.filterNot { it.isExpectedTypePosition() } val resultIsActuallyIntersection = intersectionUpperType.typeConstructor().isIntersection() - val upperType = if (isThereExpectedTypeConstraint && nonExpectedTypeConstraints.isNotEmpty() && resultIsActuallyIntersection) { + + val isThereUnwantedIntersectedTypes = if (resultIsActuallyIntersection) { + val intersectionSupertypes = intersectionUpperType.typeConstructor().supertypes() + val intersectionClasses = intersectionSupertypes.count { + it.typeConstructor().isClassTypeConstructor() && !it.typeConstructor().isInterface() + } + val areThereIntersectionFinalClasses = intersectionSupertypes.any { it.typeConstructor().isCommonFinalClassConstructor() } + intersectionClasses > 1 || areThereIntersectionFinalClasses + } else false + + val upperType = if (isThereUnwantedIntersectedTypes) { /* * We shouldn't infer a type variable into the intersection type if there is an explicit expected type, * otherwise it can lead to something like this: @@ -227,7 +235,7 @@ class ResultTypeResolver( * fun materialize(): T = null as T * val bar: Int = materialize() // no errors, T is inferred into String & Int */ - intersectTypes(nonExpectedTypeConstraints.map { it.type }) + intersectTypes(upperConstraints.filterNot { it.isExpectedTypePosition() }.map { it.type }) } else intersectionUpperType return typeApproximator.approximateToSubType( diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt46186.fir.kt b/compiler/testData/diagnostics/tests/typeParameters/kt46186.fir.kt new file mode 100644 index 00000000000..a95edd7324b --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt46186.fir.kt @@ -0,0 +1,83 @@ +// !DIAGNOSTICS: -FINAL_UPPER_BOUND -CAST_NEVER_SUCCEEDS + +interface I + +class View1 +open class View2 +interface View3 +abstract class View4 +interface View5 + +fun findViewById1(): T = null as T +fun test1(): I = findViewById1() + +fun findViewById2(): T = null as T +fun test2(): I = findViewById2() + +inline fun findViewById3(): T = null as T +fun test3(): I = findViewById3() + +inline fun findViewById4(): T = null as T +fun test4(): I = findViewById4() + +fun findViewById5(): T = null as T +fun test5(): I = findViewById5() + +inline fun findViewById6(): T = null as T +fun test6(): I = findViewById6() + +fun findViewById7(): T = null as T +fun test7(): I = findViewById7() + +inline fun findViewById8(): T = null as T +fun test8(): I = findViewById8() + +fun findViewById9(): T where T: View3, T: View5 = null as T +fun test9(): I = findViewById9() + +inline fun findViewById10(): T where T: View3, T: View5 = null as T +fun test10(): I = findViewById10() + +fun findViewById11(): T = null as T +fun test11(): View4 = findViewById11() + +object Obj { + fun findViewById1(): T = null as T + fun test1(): View1 = findViewById1() + + fun findViewById2(): T = null as T + fun test2(): View2 = findViewById2() + + inline fun findViewById3(): T = null as T + fun test3(): View1 = findViewById3() + + inline fun findViewById4(): T = null as T + fun test4(): View2 = findViewById4() + + fun findViewById5(): T = null as T + fun test5(): View3 = findViewById5() + + inline fun findViewById6(): T = null as T + fun test6(): View3 = findViewById6() + + fun findViewById7(): T = null as T + fun test7(): View4 = findViewById7() + + inline fun findViewById8(): T = null as T + fun test8(): View4 = findViewById8() + + fun findViewById9(): T where T: View3, T: View5 = null as T + fun test9(): View1 = findViewById9() + + inline fun findViewById10(): T where T: View3, T: View5 = null as T + fun test10(): View1 = findViewById10() + + fun findViewById11(): T = null as T + fun test11(): View4 = findViewById11() +} + +interface A +open class B { + fun f(): T where T : A, T : B = null as T + fun g(): A = f() +} diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt46186.kt b/compiler/testData/diagnostics/tests/typeParameters/kt46186.kt new file mode 100644 index 00000000000..1f651e5677b --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt46186.kt @@ -0,0 +1,83 @@ +// !DIAGNOSTICS: -FINAL_UPPER_BOUND -CAST_NEVER_SUCCEEDS + +interface I + +class View1 +open class View2 +interface View3 +abstract class View4 +interface View5 + +fun findViewById1(): T = null as T +fun test1(): I = findViewById1() + +fun findViewById2(): T = null as T +fun test2(): I = findViewById2() + +inline fun findViewById3(): T = null as T +fun test3(): I = findViewById3() + +inline fun findViewById4(): T = null as T +fun test4(): I = findViewById4() + +fun findViewById5(): T = null as T +fun test5(): I = findViewById5() + +inline fun findViewById6(): T = null as T +fun test6(): I = findViewById6() + +fun findViewById7(): T = null as T +fun test7(): I = findViewById7() + +inline fun findViewById8(): T = null as T +fun test8(): I = findViewById8() + +fun findViewById9(): T where T: View3, T: View5 = null as T +fun test9(): I = findViewById9() + +inline fun findViewById10(): T where T: View3, T: View5 = null as T +fun test10(): I = findViewById10() + +fun findViewById11(): T = null as T +fun test11(): View4 = findViewById11() + +object Obj { + fun findViewById1(): T = null as T + fun test1(): View1 = findViewById1() + + fun findViewById2(): T = null as T + fun test2(): View2 = findViewById2() + + inline fun findViewById3(): T = null as T + fun test3(): View1 = findViewById3() + + inline fun findViewById4(): T = null as T + fun test4(): View2 = findViewById4() + + fun findViewById5(): T = null as T + fun test5(): View3 = findViewById5() + + inline fun findViewById6(): T = null as T + fun test6(): View3 = findViewById6() + + fun findViewById7(): T = null as T + fun test7(): View4 = findViewById7() + + inline fun findViewById8(): T = null as T + fun test8(): View4 = findViewById8() + + fun findViewById9(): T where T: View3, T: View5 = null as T + fun test9(): View1 = findViewById9() + + inline fun findViewById10(): T where T: View3, T: View5 = null as T + fun test10(): View1 = findViewById10() + + fun findViewById11(): T = null as T + fun test11(): View4 = findViewById11() +} + +interface A +open class B { + fun f(): T where T : A, T : B = null as T + fun g(): A = f() +} diff --git a/compiler/testData/diagnostics/tests/typeParameters/kt46186.txt b/compiler/testData/diagnostics/tests/typeParameters/kt46186.txt new file mode 100644 index 00000000000..4f02848e7c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/typeParameters/kt46186.txt @@ -0,0 +1,107 @@ +package + +public fun findViewById1(): T +public inline fun findViewById10(): T where T : View5 +public fun findViewById11(): T +public fun findViewById2(): T +public inline fun findViewById3(): T +public inline fun findViewById4(): T +public fun findViewById5(): T +public inline fun findViewById6(): T +public fun findViewById7(): T +public inline fun findViewById8(): T +public fun findViewById9(): T where T : View5 +public fun test1(): I +public fun test10(): I +public fun test11(): View4 +public fun test2(): I +public fun test3(): I +public fun test4(): I +public fun test5(): I +public fun test6(): I +public fun test7(): I +public fun test8(): I +public fun test9(): I + +public interface A { + 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 open class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun f(): T where T : B + public final fun g(): A + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface I { + 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 object Obj { + private constructor Obj() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun findViewById1(): T + public final inline fun findViewById10(): T where T : View5 + public final fun findViewById11(): T + public final fun findViewById2(): T + public final inline fun findViewById3(): T + public final inline fun findViewById4(): T + public final fun findViewById5(): T + public final inline fun findViewById6(): T + public final fun findViewById7(): T + public final inline fun findViewById8(): T + public final fun findViewById9(): T where T : View5 + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test1(): View1 + public final fun test10(): View1 + public final fun test11(): View4 + public final fun test2(): View2 + public final fun test3(): View1 + public final fun test4(): View2 + public final fun test5(): View3 + public final fun test6(): View3 + public final fun test7(): View4 + public final fun test8(): View4 + public final fun test9(): View1 + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class View1 { + public constructor View1() + 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 open class View2 { + public constructor View2() + 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 View3 { + 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 abstract class View4 { + public constructor View4() + 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 View5 { + 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-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 64ce0b1985b..aa9a57b2819 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 @@ -29125,6 +29125,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/typeParameters/kt42472.kt"); } + @Test + @TestMetadata("kt46186.kt") + public void testKt46186() throws Exception { + runTest("compiler/testData/diagnostics/tests/typeParameters/kt46186.kt"); + } + @Test @TestMetadata("misplacedConstraints.kt") public void testMisplacedConstraints() 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 ccd8219f564..b2219dc6b2f 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 @@ -303,6 +303,7 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun TypeConstructorMarker.supertypes(): Collection fun TypeConstructorMarker.isIntersection(): Boolean fun TypeConstructorMarker.isClassTypeConstructor(): Boolean + fun TypeConstructorMarker.isInterface(): Boolean fun TypeConstructorMarker.isIntegerLiteralTypeConstructor(): Boolean fun TypeConstructorMarker.isLocalType(): Boolean 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 589f024c610..3caaef7c6f3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.impl.AbstractTypeParameterDescriptor import org.jetbrains.kotlin.name.FqName 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.constants.IntegerLiteralTypeConstructor import org.jetbrains.kotlin.resolve.descriptorUtil.* @@ -237,6 +238,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy return declarationDescriptor is ClassDescriptor } + override fun TypeConstructorMarker.isInterface(): Boolean { + require(this is TypeConstructor, this::errorMessage) + return DescriptorUtils.isInterface(declarationDescriptor) + } + override fun TypeConstructorMarker.isCommonFinalClassConstructor(): Boolean { require(this is TypeConstructor, this::errorMessage) val classDescriptor = declarationDescriptor as? ClassDescriptor ?: return false