From d6b01f10077d562468e77b2e301d80e414d30d13 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 14 Jan 2020 12:35:17 +0300 Subject: [PATCH] NI: In subtyping do not intersect arguments for non-covariant types It partially reverts 7898922066a06649bdcac4a937ccf0458733bad6 because it's not obvious that it's a safe operation for invariant/contravariant types. Also, there's a necessary fix in prepareReceiverRegardingCaptureTypes to make types order stable Otherwise test bareTypesWithStarProjections becomes flaky. Also, the changes in bareTypesWithStarProjections.kt are also expected because the type of the expression `coneSymbol` after the second "if" is FirVariableSymbol<*> & FirPropertySymbol & AbstractFirBasedSymbol<*> thus we fix D in the call `coneSymbol.phasedFir()` to FirVariableSymbol<*> because it's the first type in the list (see the next line after the last changed in AbstractTypeChecker) --- .../resolve/scopes/receivers/QualifierReceiver.kt | 7 +++++-- .../tests/generics/bareTypesWithStarProjections.fir.kt | 2 +- .../tests/generics/bareTypesWithStarProjections.kt | 2 +- .../tests/inference/regressions/kt35844.fir.kt | 10 ++++++++++ .../diagnostics/tests/inference/regressions/kt35844.kt | 10 ++++++++++ .../diagnostics/tests/inference/tooEagerSmartcast.kt | 4 ++-- .../testData/diagnostics/notLinked/dfa/pos/41.kt | 2 +- .../testData/diagnostics/notLinked/dfa/pos/6.kt | 8 ++++---- .../org/jetbrains/kotlin/types/AbstractTypeChecker.kt | 5 ++++- 9 files changed, 38 insertions(+), 12 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt index 74adef110c6..5e236630aec 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/receivers/QualifierReceiver.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.prepareArgumentTypeRegardingCaptureTypes +import org.jetbrains.kotlin.utils.newLinkedHashSetWithExpectedSize // this receiver used only for resolution. see subtypes interface DetailedReceiver @@ -48,8 +49,10 @@ fun ReceiverValueWithSmartCastInfo.prepareReceiverRegardingCaptureTypes(): Recei val preparedBaseType = prepareArgumentTypeRegardingCaptureTypes(receiverValue.type.unwrap()) if (preparedBaseType == null && possibleTypes.isEmpty()) return this - val newPossibleTypes = possibleTypes.mapTo(HashSet()) { prepareArgumentTypeRegardingCaptureTypes(it.unwrap()) ?: it } + val newPossibleTypes = possibleTypes.mapTo(newLinkedHashSetWithExpectedSize(possibleTypes.size)) { + prepareArgumentTypeRegardingCaptureTypes(it.unwrap()) ?: it + } val newReceiver = if (preparedBaseType != null) receiverValue.replaceType(preparedBaseType) else receiverValue return ReceiverValueWithSmartCastInfo(newReceiver, newPossibleTypes, isStable) -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.fir.kt b/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.fir.kt index 6c8fcf68cb7..4653efc9429 100644 --- a/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.fir.kt @@ -36,5 +36,5 @@ fun foo(coneSymbol: AbstractFirBasedSymbol<*>) { coneSymbol.phasedFir() checkType { _>() } if (coneSymbol !is FirPropertySymbol) return - coneSymbol.phasedFir() checkType { _() } + coneSymbol.phasedFir() checkType { _>() } } diff --git a/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.kt b/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.kt index 6c8fcf68cb7..4653efc9429 100644 --- a/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.kt +++ b/compiler/testData/diagnostics/tests/generics/bareTypesWithStarProjections.kt @@ -36,5 +36,5 @@ fun foo(coneSymbol: AbstractFirBasedSymbol<*>) { coneSymbol.phasedFir() checkType { _>() } if (coneSymbol !is FirPropertySymbol) return - coneSymbol.phasedFir() checkType { _() } + coneSymbol.phasedFir() checkType { _>() } } diff --git a/compiler/testData/diagnostics/tests/inference/regressions/kt35844.fir.kt b/compiler/testData/diagnostics/tests/inference/regressions/kt35844.fir.kt index d4e793c5d54..0c7f66ecae5 100644 --- a/compiler/testData/diagnostics/tests/inference/regressions/kt35844.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/regressions/kt35844.fir.kt @@ -15,3 +15,13 @@ fun main(a: A) { foo(a).b() } + +class AOut + +fun foo(c: AOut): Y = TODO() + +fun mainOut(a: AOut) { + a as AOut> + + foo(a).b() +} diff --git a/compiler/testData/diagnostics/tests/inference/regressions/kt35844.kt b/compiler/testData/diagnostics/tests/inference/regressions/kt35844.kt index 126a39c63bd..b837712cc75 100644 --- a/compiler/testData/diagnostics/tests/inference/regressions/kt35844.kt +++ b/compiler/testData/diagnostics/tests/inference/regressions/kt35844.kt @@ -15,3 +15,13 @@ fun main(a: A) { foo(a).b() } + +class AOut + +fun foo(c: AOut): Y = TODO() + +fun mainOut(a: AOut) { + a as AOut> + + foo(a).b() +} diff --git a/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt b/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt index 8ce727216cc..36cf744239c 100644 --- a/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt +++ b/compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt @@ -20,7 +20,7 @@ fun InvBase.myLastInv(): X = TODO() fun fooInv(x: InvBase) { if (x is InvDerived<*>) { - val l: T = x.myLastInv() // required T, found Cap(*). Only in NI + val l: T = x.myLastInv() // required T, found Cap(*). Only in NI } } @@ -36,7 +36,7 @@ fun Number.num() {} fun main(b: Base) { b.foo().num() if (b is Derived<*>) { - b.foo().num() + b.foo().num() b.baz().length } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/41.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/41.kt index 5e12c2a1b14..3c16fb00041 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/41.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/41.kt @@ -19,7 +19,7 @@ fun case_1(x: Any) { if (x is Interface1) { if (x is Interface2) { x - x.itest() + x.itest() x.itest1() x.itest2() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.kt index 0c0a501ec6d..e8274287da1 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/6.kt @@ -1309,7 +1309,7 @@ fun case_71(t: Any?) { t t.itest1() t.itest2() - t.itest() + t.itest() t.let { it.itest1(); it.itest2() } } @@ -1354,7 +1354,7 @@ fun case_73(t: Any?) { if (t != `null`) { t.itest2() t.itest1() - t.itest() + t.itest() t.test1() t.test2() t @@ -1379,7 +1379,7 @@ fun case_74(t: Any?) { if (t is Interface3?) { t.itest2() t.itest1() - t.itest() + t.itest() t.test1() t.test2() t @@ -1402,7 +1402,7 @@ fun case_75(t: Any?, z: Nothing?) { if (t !is Interface2? || t !is Interface3?) {} else { t.itest2() t.itest1() - t.itest() + t.itest() t.test1() t.test2() t diff --git a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index 3b35de221cc..e8d71f762b7 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -270,7 +270,10 @@ object AbstractTypeChecker { else -> { // at least 2 supertypes with same constructors. Such case is rare val newArguments = ArgumentList(superConstructor.parametersCount()) + var anyNonOutParameter = false for (index in 0 until superConstructor.parametersCount()) { + anyNonOutParameter = anyNonOutParameter || superConstructor.getParameter(index).getVariance() != TypeVariance.OUT + if (anyNonOutParameter) continue val allProjections = supertypesWithSameConstructor.map { it.getArgumentOrNull(index)?.takeIf { it.getVariance() == TypeVariance.INV }?.getType() ?: error("Incorrect type: $it, subType: $subType, superType: $superType") @@ -281,7 +284,7 @@ object AbstractTypeChecker { newArguments.add(intersection) } - if (isSubtypeForSameConstructor(newArguments, superType)) return true + if (!anyNonOutParameter && isSubtypeForSameConstructor(newArguments, superType)) return true return supertypesWithSameConstructor.any { isSubtypeForSameConstructor(it.asArgumentList(), superType) } }