[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:
Kirill Rakhman
2023-08-10 12:45:09 +02:00
committed by Space Team
parent 86ef313233
commit 724d527fd8
19 changed files with 55 additions and 167 deletions
@@ -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)
}
}
}
}
@@ -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),
)
}
}