FIR: intersect return types of declarations in intersection scopes

^KT-54378 Fixed
This commit is contained in:
pyos
2022-10-13 10:21:44 +02:00
committed by teamcity
parent 2879e7a74c
commit ee921412fc
11 changed files with 133 additions and 19 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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()
@@ -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()
@@ -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)
@@ -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 }
}
}
@@ -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 }
}
}
@@ -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 }
}
}
@@ -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]
}