[FIR] Don't remove subsumed members from intersection overrides's overriddens

This fixes a bunch of missing overridden symbols in IR.
This is also required for fixing KT-59921 in the following commit
where we need to keep all overridden symbols of intersection overrides
so that we can enhance them properly.

#KT-57300 Fixed
#KT-57299 Fixed
#KT-59921
#KT-57300
#KT-62788
#KT-64271
#KT-64382
This commit is contained in:
Kirill Rakhman
2024-01-17 09:59:17 +01:00
committed by Space Team
parent d80dee6e1c
commit 3b841dcb98
58 changed files with 899 additions and 530 deletions
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.constructors
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.getNonSubsumedOverriddenSymbols
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -31,6 +32,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.unwrapFakeOverridesOrDelegated
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.popLast
import org.jetbrains.kotlin.fir.resolve.SessionHolder
sealed class FirJsNameClashClassMembersChecker(mppKind: MppCheckerKind) : FirClassChecker(mppKind) {
object Regular : FirJsNameClashClassMembersChecker(MppCheckerKind.Platform) {
@@ -113,13 +115,17 @@ sealed class FirJsNameClashClassMembersChecker(mppKind: MppCheckerKind) : FirCla
}
}
fun addAllSymbolsFrom(symbols: Collection<FirCallableSymbol<*>>) {
fun addAllSymbolsFrom(symbols: Collection<FirCallableSymbol<*>>, sessionHolder: SessionHolder) {
for (symbol in symbols) {
when (symbol) {
is FirIntersectionCallableSymbol -> {
addAllSymbolsFrom(symbol.intersections)
for (intersectedSymbol in symbol.intersections) {
overrideIntersections.getOrPut(intersectedSymbol) { hashSetOf() }.addAll(symbol.intersections)
val nonSubsumedOverriddenSymbols = symbol.getNonSubsumedOverriddenSymbols(
sessionHolder.session,
sessionHolder.scopeSession
)
addAllSymbolsFrom(nonSubsumedOverriddenSymbols, sessionHolder)
for (intersectedSymbol in nonSubsumedOverriddenSymbols) {
overrideIntersections.getOrPut(intersectedSymbol) { hashSetOf() }.addAll(nonSubsumedOverriddenSymbols)
}
}
else -> allSymbols.add(symbol)
@@ -137,8 +143,8 @@ sealed class FirJsNameClashClassMembersChecker(mppKind: MppCheckerKind) : FirCla
val scope = declaration.symbol.unsubstitutedScope(context)
scope.processDeclaredConstructors(allSymbols::add)
addAllSymbolsFrom(scope.collectAllFunctions())
addAllSymbolsFrom(scope.collectAllProperties())
addAllSymbolsFrom(scope.collectAllFunctions(), context.sessionHolder)
addAllSymbolsFrom(scope.collectAllProperties(), context.sessionHolder)
for (callableMemberSymbol in allSymbols) {
val overriddenLeaves = scope.collectOverriddenLeaves(callableMemberSymbol)
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
@@ -335,7 +334,11 @@ fun FirCallableSymbol<*>.getImplementationStatus(
}
if (symbol is FirIntersectionCallableSymbol) {
if (containingClassSymbol === parentClassSymbol && symbol.subjectToManyNotImplemented(sessionHolder)) {
val dispatchReceiverScope = symbol.dispatchReceiverScope(sessionHolder.session, sessionHolder.scopeSession)
val memberWithBaseScope = MemberWithBaseScope(symbol, dispatchReceiverScope)
val nonSubsumed = memberWithBaseScope.getNonSubsumedOverriddenSymbols()
if (containingClassSymbol === parentClassSymbol && !memberWithBaseScope.isTrivialIntersection() && nonSubsumed.subjectToManyNotImplemented(sessionHolder)) {
return ImplementationStatus.AMBIGUOUSLY_INHERITED
}
@@ -345,7 +348,7 @@ fun FirCallableSymbol<*>.getImplementationStatus(
var hasImplementation = false
var hasImplementationVar = false
for (intersection in symbol.intersections) {
for (intersection in nonSubsumed) {
val unwrapped = intersection.unwrapFakeOverrides()
val isVar = unwrapped is FirPropertySymbol && unwrapped.isVar
val isFromClass = unwrapped.getContainingClassSymbol(sessionHolder.session)?.classKind == ClassKind.CLASS
@@ -403,11 +406,11 @@ fun FirCallableSymbol<*>.getImplementationStatus(
}
}
private fun FirIntersectionCallableSymbol.subjectToManyNotImplemented(sessionHolder: SessionHolder): Boolean {
private fun List<FirCallableSymbol<*>>.subjectToManyNotImplemented(sessionHolder: SessionHolder): Boolean {
var nonAbstractCountInClass = 0
var nonAbstractCountInInterface = 0
var abstractCountInInterface = 0
for (intersectionSymbol in intersections) {
for (intersectionSymbol in this) {
val containingClassSymbol = intersectionSymbol.getContainingClassSymbol(sessionHolder.session) as? FirRegularClassSymbol
val hasInterfaceContainer = containingClassSymbol?.classKind == ClassKind.INTERFACE
if (intersectionSymbol.modality != Modality.ABSTRACT) {
@@ -17,8 +17,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.isVisibleInClass
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
@@ -26,15 +25,14 @@ import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.delegatedWrapperData
import org.jetbrains.kotlin.fir.isSubstitutionOverride
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenMembers
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenProperties
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
@@ -121,6 +119,15 @@ sealed class FirImplementationMismatchChecker(mppKind: MppCheckerKind) : FirClas
AbstractTypeChecker.isSubtypeOf(typeCheckerState, inheritedTypeSubstituted, baseType)
}
/**
* An intersection override is trivial if one of the overridden symbols subsumes all others.
*
* @see org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScopeContext.convertGroupedCallablesToIntersectionResults
*/
fun FirCallableSymbol<*>.isTrivialIntersectionOverride(): Boolean {
return callableId.classId != containingClass.classId || MemberWithBaseScope(this, classScope).isTrivialIntersection()
}
val intersectionSymbols = when {
//substitution override means simple materialization of single method, so nothing to check
symbol.isSubstitutionOverride -> return
@@ -134,7 +141,9 @@ sealed class FirImplementationMismatchChecker(mppKind: MppCheckerKind) : FirClas
//current symbol needs to be added, because basically it is the implementation
cleared + symbol
}
symbol is FirIntersectionCallableSymbol && symbol.callableId.classId == containingClass.classId ->
symbol is FirIntersectionCallableSymbol && !symbol.isTrivialIntersectionOverride() ->
// We intentionally don't use getNonSubsumedOverriddenSymbols here, otherwise we'll get lots of new errors (compared to K1)
// in cases where a Java superclass inherits multiple members with conflicting nullability annotations.
symbol.intersections
else -> return
}
@@ -203,12 +212,18 @@ sealed class FirImplementationMismatchChecker(mppKind: MppCheckerKind) : FirClas
reporter.reportOn(containingClass.source, FirErrors.VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION, symbol, overriddenVar, context)
}
private fun FirTypeScope.collectFunctionsNamed(name: Name, containingClass: FirClass): List<FirNamedFunctionSymbol> {
private fun FirTypeScope.collectFunctionsNamed(
name: Name,
containingClass: FirClass,
context: CheckerContext,
): List<FirNamedFunctionSymbol> {
val allFunctions = mutableListOf<FirNamedFunctionSymbol>()
processFunctionsByName(name) { sym ->
when (sym) {
is FirIntersectionOverrideFunctionSymbol -> sym.intersections.mapNotNullTo(allFunctions) { it as? FirNamedFunctionSymbol }
is FirIntersectionOverrideFunctionSymbol -> sym
.getNonSubsumedOverriddenSymbols(context.session, context.scopeSession)
.mapNotNullTo(allFunctions) { it as? FirNamedFunctionSymbol }
else -> allFunctions.add(sym)
}
}
@@ -225,7 +240,7 @@ sealed class FirImplementationMismatchChecker(mppKind: MppCheckerKind) : FirClas
scope: FirTypeScope,
name: Name
) {
val allFunctions = scope.collectFunctionsNamed(name, containingClass)
val allFunctions = scope.collectFunctionsNamed(name, containingClass, context)
val sameArgumentGroups = allFunctions.groupBy { function ->
buildList {
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.collectOverriddenFunctionsWhere
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.processOverriddenFunctions
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
@@ -17,11 +16,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.getSingleMatchedExpectForActualOrNull
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.isSubstitutionOverride
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
@@ -66,7 +64,7 @@ sealed class FirMultipleDefaultsInheritedFromSupertypesChecker(mppKind: MppCheck
// default values of actual functions are located in corresponding expect functions
val overriddenWithDefaults = overridden.getSingleMatchedExpectForActualOrNull() as? FirNamedFunctionSymbol ?: overridden
// Substitution overrides copy default values from originals
if (!overriddenWithDefaults.isSubstitutionOverride && overriddenWithDefaults.valueParameterSymbols.any { it.hasDefaultValue }) {
if (!overriddenWithDefaults.isSubstitutionOrIntersectionOverride && overriddenWithDefaults.valueParameterSymbols.any { it.hasDefaultValue }) {
overriddenFunctions += overriddenWithDefaults
}
}
@@ -78,7 +78,7 @@ sealed class FirNotImplementedOverrideChecker(mppKind: MppCheckerKind) : FirClas
fun collectSymbol(symbol: FirCallableSymbol<*>) {
val delegatedWrapperData = symbol.delegatedWrapperData
if (delegatedWrapperData != null) {
if (delegatedWrapperData != null && symbol !is FirIntersectionCallableSymbol) {
val directOverriddenMembersWithBaseScope = classScope
.getDirectOverriddenMembersWithBaseScope(symbol)
.filter { it.member != symbol }