FIR: intersect return types of declarations in intersection scopes
^KT-54378 Fixed
This commit is contained in:
+12
@@ -29621,6 +29621,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/intersectionScope"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("capturedSpecificity.kt")
|
||||
public void testCapturedSpecificity() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/capturedSpecificity.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("conflictTypeParameters.kt")
|
||||
public void testConflictTypeParameters() throws Exception {
|
||||
@@ -29639,6 +29645,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/flexibleTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intersectReturnType.kt")
|
||||
public void testIntersectReturnType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/intersectReturnType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("moreSpecificSetter.kt")
|
||||
public void testMoreSpecificSetter() throws Exception {
|
||||
|
||||
+12
@@ -29621,6 +29621,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/intersectionScope"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("capturedSpecificity.kt")
|
||||
public void testCapturedSpecificity() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/capturedSpecificity.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("conflictTypeParameters.kt")
|
||||
public void testConflictTypeParameters() throws Exception {
|
||||
@@ -29639,6 +29645,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/flexibleTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intersectReturnType.kt")
|
||||
public void testIntersectReturnType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/intersectReturnType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("moreSpecificSetter.kt")
|
||||
public void testMoreSpecificSetter() throws Exception {
|
||||
|
||||
+12
@@ -29621,6 +29621,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/intersectionScope"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("capturedSpecificity.kt")
|
||||
public void testCapturedSpecificity() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/capturedSpecificity.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("conflictTypeParameters.kt")
|
||||
public void testConflictTypeParameters() throws Exception {
|
||||
@@ -29639,6 +29645,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/flexibleTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intersectReturnType.kt")
|
||||
public void testIntersectReturnType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/intersectReturnType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("moreSpecificSetter.kt")
|
||||
public void testMoreSpecificSetter() throws Exception {
|
||||
|
||||
+2
-1
@@ -22,7 +22,8 @@ abstract class AbstractFirUseSiteMemberScope(
|
||||
dispatchReceiverType: ConeSimpleKotlinType,
|
||||
protected val declaredMemberScope: FirContainingNamesAwareScope
|
||||
) : AbstractFirOverrideScope(session, overrideChecker) {
|
||||
protected val supertypeScopeContext = FirTypeIntersectionScopeContext(session, overrideChecker, superTypeScopes, dispatchReceiverType)
|
||||
protected val supertypeScopeContext =
|
||||
FirTypeIntersectionScopeContext(session, overrideChecker, superTypeScopes, dispatchReceiverType, forSubtyping = true)
|
||||
|
||||
private val functions: MutableMap<Name, Collection<FirNamedFunctionSymbol>> = hashMapOf()
|
||||
|
||||
|
||||
+2
-1
@@ -18,7 +18,8 @@ class FirTypeIntersectionScope private constructor(
|
||||
private val scopes: List<FirTypeScope>,
|
||||
dispatchReceiverType: ConeSimpleKotlinType,
|
||||
) : AbstractFirOverrideScope(session, overrideChecker) {
|
||||
private val intersectionContext = FirTypeIntersectionScopeContext(session, overrideChecker, scopes, dispatchReceiverType)
|
||||
private val intersectionContext =
|
||||
FirTypeIntersectionScopeContext(session, overrideChecker, scopes, dispatchReceiverType, forSubtyping = false)
|
||||
|
||||
private val absentFunctions: MutableSet<Name> = mutableSetOf()
|
||||
private val absentProperties: MutableSet<Name> = mutableSetOf()
|
||||
|
||||
+32
-16
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFull
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScopeContext.ResultOfIntersection
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.ConeSimpleKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.classId
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
@@ -36,6 +34,7 @@ class FirTypeIntersectionScopeContext(
|
||||
private val overrideChecker: FirOverrideChecker,
|
||||
val scopes: List<FirTypeScope>,
|
||||
private val dispatchReceiverType: ConeSimpleKotlinType,
|
||||
private val forSubtyping: Boolean,
|
||||
) {
|
||||
private val overrideService = session.overrideService
|
||||
|
||||
@@ -164,15 +163,12 @@ class FirTypeIntersectionScopeContext(
|
||||
}.takeIf { it.isNotEmpty() } ?: extractBothWaysWithPrivate
|
||||
val baseMembersForIntersection = extractedOverrides.calcBaseMembersForIntersectionOverride()
|
||||
if (baseMembersForIntersection.size > 1) {
|
||||
result += ResultOfIntersection.NonTrivial(
|
||||
this,
|
||||
overrideService.selectMostSpecificMembers(baseMembersForIntersection, ReturnTypeCalculatorForFullBodyResolve),
|
||||
extractedOverrides,
|
||||
containingScope = null
|
||||
)
|
||||
val mostSpecific =
|
||||
overrideService.selectMostSpecificMembers(baseMembersForIntersection, ReturnTypeCalculatorForFullBodyResolve)
|
||||
result += ResultOfIntersection.NonTrivial(this, mostSpecific, extractedOverrides, containingScope = null)
|
||||
} else {
|
||||
val (mostSpecific, containingScope) = baseMembersForIntersection.single()
|
||||
result += ResultOfIntersection.SingleMember(mostSpecific, extractedOverrides, containingScope)
|
||||
val (member, containingScope) = baseMembersForIntersection.single()
|
||||
result += ResultOfIntersection.SingleMember(member, extractedOverrides, containingScope)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +211,10 @@ class FirTypeIntersectionScopeContext(
|
||||
// we should just take most specific member without creating intersection
|
||||
// A typical sample here is inheritance of the same class in different places of hierarchy
|
||||
if (unwrappedMemberSet.size == 1) {
|
||||
return listOf(overrideService.selectMostSpecificMember(this, ReturnTypeCalculatorForFullBodyResolve))
|
||||
return when {
|
||||
forSubtyping -> listOf(overrideService.selectMostSpecificMember(this, ReturnTypeCalculatorForFullBodyResolve))
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
val baseMembers = mutableSetOf<S>()
|
||||
@@ -239,10 +238,7 @@ class FirTypeIntersectionScopeContext(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = this.toMutableList()
|
||||
result.removeIf { (member, _) -> member.fir.unwrapSubstitutionOverrides().symbol in baseMembers }
|
||||
return result
|
||||
return filter { it.member.fir.unwrapSubstitutionOverrides().symbol !in baseMembers }
|
||||
}
|
||||
|
||||
private fun <D : FirCallableSymbol<*>> findMemberWithMaxVisibility(members: Collection<MemberWithBaseScope<D>>): MemberWithBaseScope<D> {
|
||||
@@ -381,6 +377,7 @@ class FirTypeIntersectionScopeContext(
|
||||
newModality = newModality,
|
||||
newVisibility = newVisibility,
|
||||
newDispatchReceiverType = dispatchReceiverType,
|
||||
newReturnType = if (!forSubtyping) intersectReturnTypes(mostSpecific) else null,
|
||||
).apply {
|
||||
originalForIntersectionOverrideAttr = keyFir
|
||||
}
|
||||
@@ -405,11 +402,30 @@ class FirTypeIntersectionScopeContext(
|
||||
newModality = newModality,
|
||||
newVisibility = newVisibility,
|
||||
newDispatchReceiverType = dispatchReceiverType,
|
||||
// If any of the properties are vars and the types are not equal, these declarations are conflicting
|
||||
// anyway and their uses should result in an overload resolution error.
|
||||
newReturnType = if (!forSubtyping && !mostSpecific.any { (it as FirPropertySymbol).fir.isVar })
|
||||
intersectReturnTypes(mostSpecific)
|
||||
else
|
||||
null,
|
||||
).apply {
|
||||
originalForIntersectionOverrideAttr = keyFir
|
||||
}
|
||||
return newSymbol
|
||||
}
|
||||
|
||||
private fun intersectReturnTypes(overrides: Collection<FirCallableSymbol<*>>): ConeKotlinType? {
|
||||
val key = overrides.first()
|
||||
// Remap type parameters to the first declaration's:
|
||||
// (fun <A, B> foo(): B) & (fun <C, D> foo(): D?) -> (fun <A, B> foo(): B & B?)
|
||||
val substituted = overrides.mapNotNull {
|
||||
val returnType = it.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>()
|
||||
if (it == key) return@mapNotNull returnType
|
||||
val substitutor = buildSubstitutorForOverridesCheck(it.fir, key.fir, session) ?: return@mapNotNull null
|
||||
returnType?.let(substitutor::substituteOrSelf)
|
||||
}
|
||||
return if (substituted.isNotEmpty()) session.typeContext.intersectTypes(substituted) else null
|
||||
}
|
||||
}
|
||||
|
||||
private fun <D : FirCallableSymbol<*>> D.withScope(baseScope: FirTypeScope) = MemberWithBaseScope(this, baseScope)
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// FIR_IDENTICAL
|
||||
// SKIP_TXT
|
||||
class C<T>(val value: T?)
|
||||
|
||||
fun <T> assignable(x: () -> T) {}
|
||||
|
||||
fun <V> foo(t: C<out Any>, v: C<out V>) {
|
||||
assignable<V?> { v.value }
|
||||
if (t == v) {
|
||||
// `value: CapturedType(out V)?` <: `value: CapturedType(out Any)?` - same instantiation
|
||||
assignable<V?> { v.value }
|
||||
assignable<V?> { t.value }
|
||||
}
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
// SKIP_TXT
|
||||
class C<T>(val value: T)
|
||||
|
||||
fun <T> assignable(x: () -> T) {}
|
||||
|
||||
fun <T, V> foo(t: C<out T>, v: C<out V>) {
|
||||
assignable<T> { t.value } // sure
|
||||
assignable<V> { v.value } // obviously
|
||||
if (t == v) {
|
||||
// => {t,v} is C<out T> & C<out V>
|
||||
// => {t,v}.value is T & V
|
||||
assignable<T> { t.value }
|
||||
assignable<V> { v.value }
|
||||
assignable<T> { v.value }
|
||||
assignable<V> { t.value }
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// SKIP_TXT
|
||||
class C<T>(val value: T)
|
||||
|
||||
fun <T> assignable(x: () -> T) {}
|
||||
|
||||
fun <T, V> foo(t: C<out T>, v: C<out V>) {
|
||||
assignable<T> { t.value } // sure
|
||||
assignable<V> { v.value } // obviously
|
||||
if (t == v) {
|
||||
// => {t,v} is C<out T> & C<out V>
|
||||
// => {t,v}.value is T & V
|
||||
assignable<T> { <!TYPE_MISMATCH!>t.value<!> }
|
||||
assignable<V> { <!TYPE_MISMATCH!>v.value<!> }
|
||||
assignable<T> { v.value }
|
||||
assignable<V> { t.value }
|
||||
}
|
||||
}
|
||||
Generated
+12
@@ -29711,6 +29711,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/intersectionScope"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("capturedSpecificity.kt")
|
||||
public void testCapturedSpecificity() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/capturedSpecificity.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("conflictTypeParameters.kt")
|
||||
public void testConflictTypeParameters() throws Exception {
|
||||
@@ -29729,6 +29735,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/flexibleTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("intersectReturnType.kt")
|
||||
public void testIntersectReturnType() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/intersectionScope/intersectReturnType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("moreSpecificSetter.kt")
|
||||
public void testMoreSpecificSetter() throws Exception {
|
||||
|
||||
@@ -12,5 +12,5 @@ fun case_1() {
|
||||
val strs = list as MutableList<String>
|
||||
strs.add("two")
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.String> & kotlin.collections.MutableList<kotlin.Int> & kotlin.collections.MutableList<kotlin.String>")!>list<!>
|
||||
val s: String = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!><!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.String> & kotlin.collections.MutableList<kotlin.Int> & kotlin.collections.MutableList<kotlin.String>")!>list<!>[0]<!>
|
||||
val s: String = <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.String> & kotlin.collections.MutableList<kotlin.Int> & kotlin.collections.MutableList<kotlin.String>")!>list<!>[0]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user