[FIR] Introduce containsMultipleNonSubsumed to intersection overrides
After this change the IO created for `SimpleColoredComponent::setToolTipText` will be `containsMultipleNonSubsumed == true` while IOs created for classes in `intersectionOverridesIntersection.kt` will have this property set to `false`. ^KT-65972 Fixed
This commit is contained in:
committed by
Space Team
parent
ee20d979cb
commit
341a23d05c
+4
-1
@@ -334,7 +334,10 @@ class FirSignatureEnhancement(
|
||||
this.name = name!!
|
||||
status = firMethod.status
|
||||
symbol = if (isIntersectionOverride) {
|
||||
FirIntersectionOverrideFunctionSymbol(methodId, overriddenMembers.map { it.symbol })
|
||||
FirIntersectionOverrideFunctionSymbol(
|
||||
methodId, overriddenMembers.map { it.symbol },
|
||||
containsMultipleNonSubsumed = (firMethod.symbol as? FirIntersectionCallableSymbol)?.containsMultipleNonSubsumed == true,
|
||||
)
|
||||
} else {
|
||||
FirNamedFunctionSymbol(methodId)
|
||||
}.also { functionSymbol = it }
|
||||
|
||||
@@ -375,7 +375,11 @@ class JavaOverrideChecker internal constructor(
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>,
|
||||
dispatchClassSymbol: FirRegularClassSymbol?,
|
||||
): Visibility {
|
||||
val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }
|
||||
// It's crucial that we only unwrap phantom intersection overrides.
|
||||
// See comments in the following tests for explanation:
|
||||
// - intersectionWithMultipleDefaultsInJavaOverriddenByIntersectionInKotlin.kt
|
||||
// - intersectionOverridesIntersection.kt
|
||||
val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenPhantomIntersectionsRecursively() }
|
||||
val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates()
|
||||
|
||||
// In Java it's OK to inherit multiple implementations of the same function
|
||||
|
||||
@@ -176,6 +176,16 @@ fun MemberWithBaseScope<FirCallableSymbol<*>>.flattenIntersectionsRecursively():
|
||||
return baseScope.getDirectOverriddenMembersWithBaseScope(member).flatMap { it.flattenIntersectionsRecursively() }
|
||||
}
|
||||
|
||||
fun MemberWithBaseScope<FirCallableSymbol<*>>.flattenPhantomIntersectionsRecursively(): List<MemberWithBaseScope<FirCallableSymbol<*>>> {
|
||||
val symbol = member.unwrapSubstitutionOverrides<FirCallableSymbol<*>>()
|
||||
|
||||
if (symbol !is FirIntersectionCallableSymbol || symbol.containsMultipleNonSubsumed) {
|
||||
return listOf(this)
|
||||
}
|
||||
|
||||
return baseScope.getDirectOverriddenMembersWithBaseScope(member).flatMap { it.flattenPhantomIntersectionsRecursively() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable declaration D [subsumes](https://kotlinlang.org/spec/inheritance.html#matching-and-subsumption-of-declarations)
|
||||
* a callable declaration B if D overrides B.
|
||||
|
||||
+5
-1
@@ -166,7 +166,11 @@ class FirStandardOverrideChecker(private val session: FirSession) : FirAbstractO
|
||||
extractedOverrides: Collection<MemberWithBaseScope<D>>,
|
||||
dispatchClassSymbol: FirRegularClassSymbol?,
|
||||
): Visibility {
|
||||
val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }
|
||||
// It's crucial that we only unwrap phantom intersection overrides.
|
||||
// See comments in the following tests for explanation:
|
||||
// - intersectionWithMultipleDefaultsInJavaOverriddenByIntersectionInKotlin.kt
|
||||
// - intersectionOverridesIntersection.kt
|
||||
val overridesWithoutIntersections = extractedOverrides.flatMap { it.flattenPhantomIntersectionsRecursively() }
|
||||
val nonSubsumed = overridesWithoutIntersections.nonSubsumed().filterOutDuplicates()
|
||||
return chooseIntersectionVisibilityOrNull(nonSubsumed) ?: Visibilities.Unknown
|
||||
}
|
||||
|
||||
+31
-9
@@ -64,7 +64,8 @@ class FirTypeIntersectionScopeContext(
|
||||
val context: FirTypeIntersectionScopeContext,
|
||||
val mostSpecific: List<MemberWithBaseScope<D>>,
|
||||
overriddenMembers: List<MemberWithBaseScope<D>>,
|
||||
containingScope: FirTypeScope?
|
||||
containingScope: FirTypeScope?,
|
||||
val containsMultipleNonSubsumed: Boolean,
|
||||
) : ResultOfIntersection<D>(overriddenMembers, containingScope) {
|
||||
override val chosenSymbol: D by lazy {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -174,7 +175,15 @@ class FirTypeIntersectionScopeContext(
|
||||
}
|
||||
if (nonTrivial) {
|
||||
// Only add non-subsumed members to list of overridden in intersection override.
|
||||
result += ResultOfIntersection.NonTrivial(this, mostSpecific, overriddenMembers = group, containingScope = null)
|
||||
result += ResultOfIntersection.NonTrivial(
|
||||
this, mostSpecific,
|
||||
overriddenMembers = group,
|
||||
containingScope = null,
|
||||
containsMultipleNonSubsumed = when {
|
||||
forClassUseSiteScope -> group.flatMap { it.flattenPhantomIntersectionsRecursively() }.nonSubsumed().size > 1
|
||||
else -> mostSpecific.flatMap { it.flattenPhantomIntersectionsRecursively() }.nonSubsumed().size > 1
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val (member, containingScope) = mostSpecific.first()
|
||||
result += ResultOfIntersection.SingleMember(member, group, containingScope)
|
||||
@@ -203,6 +212,7 @@ class FirTypeIntersectionScopeContext(
|
||||
fun <D : FirCallableSymbol<*>> createIntersectionOverride(
|
||||
mostSpecific: List<MemberWithBaseScope<D>>,
|
||||
extractedOverrides: List<MemberWithBaseScope<D>>,
|
||||
containsMultipleNonSubsumed: Boolean,
|
||||
): MemberWithBaseScope<FirCallableSymbol<*>> {
|
||||
val newModality = chooseIntersectionOverrideModality(extractedOverrides.flatMap { it.flattenIntersectionsRecursively() }.nonSubsumed())
|
||||
val newVisibility = overrideChecker.chooseIntersectionVisibility(extractedOverrides, dispatchClassSymbol)
|
||||
@@ -211,14 +221,20 @@ class FirTypeIntersectionScopeContext(
|
||||
val key = mostSpecific.first()
|
||||
return when (key.member) {
|
||||
is FirNamedFunctionSymbol ->
|
||||
createIntersectionOverrideFunction(mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility)
|
||||
createIntersectionOverrideFunction(
|
||||
mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility, containsMultipleNonSubsumed
|
||||
)
|
||||
|
||||
is FirPropertySymbol ->
|
||||
createIntersectionOverrideProperty(mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility)
|
||||
createIntersectionOverrideProperty(
|
||||
mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility, containsMultipleNonSubsumed
|
||||
)
|
||||
|
||||
is FirFieldSymbol -> {
|
||||
if (forClassUseSiteScope) error("Can not create intersection override in class scope for field ${key.member}")
|
||||
createIntersectionOverrideField(mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility)
|
||||
createIntersectionOverrideField(
|
||||
mostSpecificSymbols, extractedOverridesSymbols, newModality, newVisibility, containsMultipleNonSubsumed
|
||||
)
|
||||
}
|
||||
|
||||
else -> error("Unsupported symbol type for creating intersection overrides: ${key.member}")
|
||||
@@ -323,6 +339,7 @@ class FirTypeIntersectionScopeContext(
|
||||
overrides: Collection<FirCallableSymbol<*>>,
|
||||
newModality: Modality?,
|
||||
newVisibility: Visibility,
|
||||
containsMultipleNonSubsumed: Boolean,
|
||||
): FirNamedFunctionSymbol {
|
||||
val key = mostSpecific.first() as FirNamedFunctionSymbol
|
||||
val keyFir = key.fir
|
||||
@@ -330,7 +347,7 @@ class FirTypeIntersectionScopeContext(
|
||||
dispatchReceiverType.classId ?: keyFir.dispatchReceiverClassLookupTagOrNull()?.classId!!,
|
||||
keyFir.name
|
||||
)
|
||||
val newSymbol = FirIntersectionOverrideFunctionSymbol(callableId, overrides)
|
||||
val newSymbol = FirIntersectionOverrideFunctionSymbol(callableId, overrides, containsMultipleNonSubsumed)
|
||||
val deferredReturnTypeCalculation = deferredReturnTypeCalculationOrNull(mostSpecific)
|
||||
FirFakeOverrideGenerator.createCopyForFirFunction(
|
||||
newSymbol, keyFir, derivedClassLookupTag = null, session,
|
||||
@@ -353,10 +370,12 @@ class FirTypeIntersectionScopeContext(
|
||||
overrides: Collection<FirCallableSymbol<*>>,
|
||||
newModality: Modality?,
|
||||
newVisibility: Visibility,
|
||||
containsMultipleNonSubsumed: Boolean,
|
||||
): FirPropertySymbol {
|
||||
return createIntersectionOverrideVariable<FirPropertySymbol, _>(
|
||||
mostSpecific,
|
||||
overrides,
|
||||
containsMultipleNonSubsumed,
|
||||
::FirIntersectionOverridePropertySymbol,
|
||||
) { symbol, fir, deferredReturnTypeCalculation, returnType ->
|
||||
FirFakeOverrideGenerator.createCopyForFirProperty(
|
||||
@@ -380,10 +399,12 @@ class FirTypeIntersectionScopeContext(
|
||||
overrides: Collection<FirCallableSymbol<*>>,
|
||||
newModality: Modality?,
|
||||
newVisibility: Visibility,
|
||||
containsMultipleNonSubsumed: Boolean,
|
||||
): FirFieldSymbol {
|
||||
return createIntersectionOverrideVariable<FirFieldSymbol, _>(
|
||||
mostSpecific,
|
||||
overrides,
|
||||
containsMultipleNonSubsumed,
|
||||
::FirIntersectionOverrideFieldSymbol
|
||||
) { symbol, fir, deferredReturnTypeCalculation, returnType ->
|
||||
FirFakeOverrideGenerator.createCopyForFirField(
|
||||
@@ -404,13 +425,14 @@ class FirTypeIntersectionScopeContext(
|
||||
private inline fun <reified S : FirVariableSymbol<F>, F : FirVariable> createIntersectionOverrideVariable(
|
||||
mostSpecific: Collection<FirCallableSymbol<*>>,
|
||||
overrides: Collection<FirCallableSymbol<*>>,
|
||||
createIntersectionOverrideSymbol: (CallableId, Collection<FirCallableSymbol<*>>) -> S,
|
||||
containsMultipleNonSubsumed: Boolean,
|
||||
createIntersectionOverrideSymbol: (CallableId, Collection<FirCallableSymbol<*>>, Boolean) -> S,
|
||||
createCopy: (S, F, deferredReturnTypeCalculation: CallableCopyDeferredReturnTypeCalculation?, returnType: ConeKotlinType?) -> F
|
||||
): S {
|
||||
val key = mostSpecific.first() as S
|
||||
val keyFir = key.fir
|
||||
val callableId = CallableId(dispatchReceiverType.classId ?: keyFir.dispatchReceiverClassLookupTagOrNull()?.classId!!, keyFir.name)
|
||||
val newSymbol = createIntersectionOverrideSymbol(callableId, overrides)
|
||||
val newSymbol = createIntersectionOverrideSymbol(callableId, overrides, containsMultipleNonSubsumed)
|
||||
val deferredReturnTypeCalculation = deferredReturnTypeCalculationOrNull(mostSpecific)
|
||||
val newReturnType =
|
||||
runIf(!forClassUseSiteScope && mostSpecific.none { (it as FirVariableSymbol<*>).fir.isVar } && deferredReturnTypeCalculation == null) {
|
||||
@@ -459,7 +481,7 @@ class FirIntersectionOverrideStorage(val session: FirSession) : FirSessionCompon
|
||||
val cacheByScope: FirCache<ConeKotlinType, FirIntersectionOverrideCache, Nothing?> =
|
||||
cachesFactory.createCache { _ ->
|
||||
cachesFactory.createCache { _, result ->
|
||||
result.context.createIntersectionOverride(result.mostSpecific, result.overriddenMembers)
|
||||
result.context.createIntersectionOverride(result.mostSpecific, result.overriddenMembers, result.containsMultipleNonSubsumed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,19 @@ open class FirNamedFunctionSymbol(callableId: CallableId) : FirFunctionSymbol<Fi
|
||||
|
||||
interface FirIntersectionCallableSymbol {
|
||||
val intersections: Collection<FirCallableSymbol<*>>
|
||||
|
||||
/**
|
||||
* `true` iff a call to `nonSubsumed()` for `intersections` would result into a list with more than one symbol.
|
||||
* Intuitively, `false` means this intersection is, strictly speaking, redundant, but we still created it
|
||||
* as an implementation detail.
|
||||
*/
|
||||
val containsMultipleNonSubsumed: Boolean
|
||||
}
|
||||
|
||||
class FirIntersectionOverrideFunctionSymbol(
|
||||
callableId: CallableId,
|
||||
override val intersections: Collection<FirCallableSymbol<*>>,
|
||||
override val containsMultipleNonSubsumed: Boolean,
|
||||
) : FirNamedFunctionSymbol(callableId), FirIntersectionCallableSymbol
|
||||
|
||||
class FirConstructorSymbol(callableId: CallableId) : FirFunctionSymbol<FirConstructor>(callableId), ConstructorSymbolMarker {
|
||||
|
||||
@@ -72,12 +72,14 @@ open class FirPropertySymbol(callableId: CallableId, ) : FirVariableSymbol<FirPr
|
||||
|
||||
class FirIntersectionOverridePropertySymbol(
|
||||
callableId: CallableId,
|
||||
override val intersections: Collection<FirCallableSymbol<*>>
|
||||
override val intersections: Collection<FirCallableSymbol<*>>,
|
||||
override val containsMultipleNonSubsumed: Boolean,
|
||||
) : FirPropertySymbol(callableId), FirIntersectionCallableSymbol
|
||||
|
||||
class FirIntersectionOverrideFieldSymbol(
|
||||
callableId: CallableId,
|
||||
override val intersections: Collection<FirCallableSymbol<*>>
|
||||
override val intersections: Collection<FirCallableSymbol<*>>,
|
||||
override val containsMultipleNonSubsumed: Boolean,
|
||||
) : FirFieldSymbol(callableId), FirIntersectionCallableSymbol
|
||||
|
||||
class FirBackingFieldSymbol(callableId: CallableId) : FirVariableSymbol<FirBackingField>(callableId) {
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// FILE: ColoredTextContainer.java
|
||||
public interface ColoredTextContainer {
|
||||
default void setToolTipText(String text) {}
|
||||
}
|
||||
|
||||
// FILE: JComponent.java
|
||||
public abstract class JComponent {
|
||||
public void setToolTipText(String text) {}
|
||||
}
|
||||
|
||||
// FILE: SimpleColoredComponent.java
|
||||
// IDEALLY:
|
||||
// Provides `IO SimpleColoredComponent::setToolTipText`, because inherits
|
||||
// `JComponent::setToolTipText` and `ColoredTextContainer::setToolTipText`,
|
||||
// which don't sumbsume one another, but Java allows picking the class-based one in this case.
|
||||
// REALITY:
|
||||
// Contains an IO for the above. We check if this is green Java via a
|
||||
// modified check that accounts for this case.
|
||||
public class SimpleColoredComponent extends JComponent implements ColoredTextContainer {}
|
||||
|
||||
// FILE: Main.kt
|
||||
|
||||
// Not important, left for the record
|
||||
interface TextFragment : ColoredTextContainer
|
||||
|
||||
// IDEALLY:
|
||||
// Provides `JComponent::setToolTipText`, because inherits
|
||||
// `ColoredTextContainer::setToolTipText` and `IO SimpleColoredComponent::setToolTipText`,
|
||||
// and the latter subsumes the former.
|
||||
// REALITY:
|
||||
// Contains IO between `ColoredTextContainer::setToolTipText` and `IO SimpleColoredComponent::setToolTipText`.
|
||||
// In this case `IO SimpleColoredComponent::setToolTipText` should not be unwrapped, otherwise
|
||||
// we miss `nonSubsumed()` check and since this is a Kotlin class we are not allowed to implicitly choose
|
||||
// between `JComponent::setToolTipText` and `ColoredTextContainer::setToolTipText`.
|
||||
private <!CANNOT_INFER_VISIBILITY!>class TextFragmentImpl<!> : TextFragment, SimpleColoredComponent()
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// FILE: ColoredTextContainer.java
|
||||
public interface ColoredTextContainer {
|
||||
default void setToolTipText(String text) {}
|
||||
|
||||
Reference in New Issue
Block a user