[FIR] Fixes for call conflict resolution
Let ConeCompositeConflictResolver pass the results of the previous resolver to the next one. Otherwise, we get false positive conflicts when a set of candidates can't be fully reduced by one resolver but could be resolved by the subsequent application of multiple ones. This change makes ConeCompositeConflictResolver order-dependent and thus, ConeOverloadConflictResolver must be invoked last, because it must work on a pre-filtered list. Also, let ConeEquivalentCallConflictResolver use FirStandardOverrideChecker instead of compareCallsByUsedArguments because it's stricter. This all fixes a false positive overload resolution ambiguity in common metadata compilation that is caused by stdlib using the new KMP format. Now stdlib metadata is in the classpath, and so declarations from the stdlib are returned from both MetadataSymbolProvider and KlibBasedSymbolProvider. This isn't a problem per se because duplicate candidates are filtered out by ConeEquivalentCallConflictResolver (K1 works analogously), but in the case of top-level functions with generic receivers like Collection<T>.toTypedArray, the check failed because of the direct comparison of receiver types. #KT-60943 Fixed
This commit is contained in:
committed by
Space Team
parent
86ef313233
commit
724d527fd8
+11
-19
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.AbstractConeCallConflictResolver
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
|
||||
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
|
||||
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
|
||||
@@ -64,21 +64,24 @@ class ConeEquivalentCallConflictResolver(
|
||||
): Boolean {
|
||||
if (first.symbol.callableId != second.symbol.callableId) return false
|
||||
if (first.isExpect != second.isExpect) return false
|
||||
if (first.receiverParameter?.typeRef?.coneType != second.receiverParameter?.typeRef?.coneType) {
|
||||
return false
|
||||
}
|
||||
if (first is FirVariable != second is FirVariable) {
|
||||
return false
|
||||
}
|
||||
if (!firstCandidate.mappedArgumentsOrderRepresentation.contentEquals(secondCandidate.mappedArgumentsOrderRepresentation)) {
|
||||
return false
|
||||
}
|
||||
val firstSignature = createFlatSignature(firstCandidate, first)
|
||||
val secondSignature = createFlatSignature(secondCandidate, second)
|
||||
return compareCallsByUsedArguments(firstSignature, secondSignature, discriminateGenerics = false, useOriginalSamTypes = false) &&
|
||||
compareCallsByUsedArguments(secondSignature, firstSignature, discriminateGenerics = false, useOriginalSamTypes = false)
|
||||
|
||||
val overrideChecker = FirStandardOverrideChecker(inferenceComponents.session)
|
||||
return if (first is FirProperty && second is FirProperty) {
|
||||
overrideChecker.isOverriddenProperty(first, second) && overrideChecker.isOverriddenProperty(second, first)
|
||||
} else if (first is FirSimpleFunction && second is FirSimpleFunction) {
|
||||
overrideChecker.isOverriddenFunction(first, second) && overrideChecker.isOverriddenFunction(second, first)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the candidate is a function, then the arguments
|
||||
* order representation is an array containing the
|
||||
@@ -99,15 +102,4 @@ class ConeEquivalentCallConflictResolver(
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createFlatSignature(call: Candidate, declaration: FirCallableDeclaration): FlatSignature<Candidate> {
|
||||
return when (declaration) {
|
||||
is FirSimpleFunction -> createFlatSignature(call, declaration)
|
||||
is FirConstructor -> createFlatSignature(call, declaration)
|
||||
is FirVariable -> createFlatSignature(call, declaration)
|
||||
else -> errorWithAttachment("Not supported: ${this::class}") {
|
||||
withFirEntry("declaration", declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -24,14 +24,12 @@ object JvmCallConflictResolverFactory : ConeCallConflictResolverFactory() {
|
||||
transformerComponents: BodyResolveComponents
|
||||
): ConeCompositeConflictResolver {
|
||||
val specificityComparator = JvmTypeSpecificityComparator(components.session.typeContext)
|
||||
// NB: Please, be aware that adding might not necessarily help you because ConeOverloadConflictResolver doesn't just filter out
|
||||
// less specific candidates, but leave the set the same if there are more than one same-specifity candidates.
|
||||
// Thus, in that case, your new ConeCallConflictResolver might get all the candidates in that case.
|
||||
// NB: Adding new resolvers is strongly discouraged because the results are order-dependent.
|
||||
return ConeCompositeConflictResolver(
|
||||
ConeOverloadConflictResolver(specificityComparator, components, transformerComponents),
|
||||
ConeEquivalentCallConflictResolver(specificityComparator, components, transformerComponents),
|
||||
JvmPlatformOverloadsConflictResolver(components.session),
|
||||
ConeIntegerOperatorConflictResolver,
|
||||
ConeOverloadConflictResolver(specificityComparator, components, transformerComponents),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user