[FIR] Bring equivalent call behavior closer to K1
#KT-61159 Fixed
This commit is contained in:
committed by
Space Team
parent
7c67e9e08b
commit
fa77e3952d
+2
-2
@@ -12,13 +12,13 @@ FILE: mixingImplicitAndExplicitReceivers.kt
|
||||
public final fun withThis(): R|kotlin/Unit| {
|
||||
when () {
|
||||
!=(this@R|/Wrapper|.R|/Wrapper.s|, Null(null)) -> {
|
||||
R|/takeString|(this@R|/Wrapper|.R|/Wrapper.s|)
|
||||
<Ambiguity: takeString, [/takeString, /takeString]>#(this@R|/Wrapper|.R|/Wrapper.s|)
|
||||
}
|
||||
}
|
||||
|
||||
when () {
|
||||
!=(this@R|/Wrapper|.R|/Wrapper.s|, Null(null)) -> {
|
||||
R|/takeString|(this@R|/Wrapper|.R|/Wrapper.s|)
|
||||
<Ambiguity: takeString, [/takeString, /takeString]>#(this@R|/Wrapper|.R|/Wrapper.s|)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -3,10 +3,10 @@
|
||||
class Wrapper(val s: String?) {
|
||||
fun withThis() {
|
||||
if (s != null) {
|
||||
takeString(this.s) // Should be OK
|
||||
<!OVERLOAD_RESOLUTION_AMBIGUITY!>takeString<!>(this.s) // Should be OK
|
||||
}
|
||||
if (this.s != null) {
|
||||
takeString(s) // Should be OK
|
||||
<!OVERLOAD_RESOLUTION_AMBIGUITY!>takeString<!>(s) // Should be OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -13,7 +13,7 @@ FILE: beyoundCalls.kt
|
||||
}
|
||||
public final fun foo(): R|kotlin/Unit| {
|
||||
lval x: R|(kotlin/String) -> kotlin/Int| = ::R|/bar|
|
||||
lval y: R|kotlin/reflect/KFunction1<kotlin/String, kotlin/Int>| = ::R|/bar|
|
||||
lval y: <ERROR TYPE REF: Ambiguity: bar, [/bar, /bar]> = ::<Ambiguity: bar, [/bar, /bar]>#
|
||||
lval z: R|kotlin/reflect/KFunction1<kotlin/String, kotlin/Int>| = ::R|/baz|
|
||||
lval w: R|(kotlin/String) -> kotlin/Int| = ::R|/foobaz<kotlin/String, kotlin/Int>|
|
||||
::R|/baz|
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ fun <T, R> foobaz(x: T): R = TODO()
|
||||
|
||||
fun foo() {
|
||||
val x: (String) -> Int = ::bar
|
||||
val y = ::bar
|
||||
val y = ::<!OVERLOAD_RESOLUTION_AMBIGUITY!>bar<!>
|
||||
val z = ::baz
|
||||
val w: (String) -> Int = ::foobaz
|
||||
|
||||
|
||||
+6
@@ -28143,6 +28143,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/allLambdas.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equivalentCallsDifferentVisibility.kt")
|
||||
public void testEquivalentCallsDifferentVisibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/equivalentCallsDifferentVisibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("extensionReceiverAndVarargs.kt")
|
||||
public void testExtensionReceiverAndVarargs() throws Exception {
|
||||
|
||||
+6
@@ -28155,6 +28155,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/allLambdas.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equivalentCallsDifferentVisibility.kt")
|
||||
public void testEquivalentCallsDifferentVisibility() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/resolve/overloadConflicts/equivalentCallsDifferentVisibility.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("extensionReceiverAndVarargs.kt")
|
||||
public void testExtensionReceiverAndVarargs() throws Exception {
|
||||
|
||||
+16
@@ -17402,6 +17402,22 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/equivalentCalls")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class EquivalentCalls {
|
||||
@Test
|
||||
public void testAllFilesPresentInEquivalentCalls() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/equivalentCalls"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localEquivalentWins.kt")
|
||||
public void testLocalEquivalentWins() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/equivalentCalls/localEquivalentWins.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/evaluate")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+16
@@ -17402,6 +17402,22 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/equivalentCalls")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class EquivalentCalls {
|
||||
@Test
|
||||
public void testAllFilesPresentInEquivalentCalls() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/equivalentCalls"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localEquivalentWins.kt")
|
||||
public void testLocalEquivalentWins() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/equivalentCalls/localEquivalentWins.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/evaluate")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+23
-10
@@ -13,17 +13,19 @@ 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.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
|
||||
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
|
||||
|
||||
// This conflict resolver filters JVM equivalent top-level functions
|
||||
// like emptyArray() from intrinsics and built-ins
|
||||
/**
|
||||
* Resolver that filters out equivalent calls, mainly to deduplicate multiples of the same declaration coming from different versions
|
||||
* of the same dependency, e.g., multiple stdlibs.
|
||||
*
|
||||
* Currently, it will also consider a declaration from source and one from binary equivalent if all conditions are met for backward
|
||||
* compatibility with K1.
|
||||
*/
|
||||
class ConeEquivalentCallConflictResolver(
|
||||
specificityComparator: TypeSpecificityComparator,
|
||||
inferenceComponents: InferenceComponents,
|
||||
transformerComponents: BodyResolveComponents
|
||||
transformerComponents: BodyResolveComponents,
|
||||
) : AbstractConeCallConflictResolver(
|
||||
specificityComparator,
|
||||
inferenceComponents,
|
||||
@@ -38,8 +40,13 @@ class ConeEquivalentCallConflictResolver(
|
||||
}
|
||||
|
||||
private fun filterOutEquivalentCalls(candidates: Collection<Candidate>): Set<Candidate> {
|
||||
// Since we can consider a declaration from source and one from binary equivalent, we need to make sure we favor the one from
|
||||
// source, otherwise we might get a behavior change to K1.
|
||||
// See org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver.filterOutEquivalentCalls.
|
||||
val fromSourceFirst = candidates.sortedBy { it.symbol.fir.source == null }
|
||||
|
||||
val result = mutableSetOf<Candidate>()
|
||||
outerLoop@ for (myCandidate in candidates) {
|
||||
outerLoop@ for (myCandidate in fromSourceFirst) {
|
||||
val me = myCandidate.symbol.fir
|
||||
if (me is FirCallableDeclaration && me.symbol.containingClassLookupTag() == null) {
|
||||
for (otherCandidate in result) {
|
||||
@@ -63,6 +70,11 @@ class ConeEquivalentCallConflictResolver(
|
||||
secondCandidate: Candidate
|
||||
): Boolean {
|
||||
if (first.symbol.callableId != second.symbol.callableId) return false
|
||||
// Emulate behavior from K1 where declarations from the same module are never equivalent.
|
||||
// We expect REDECLARATION or CONFLICTING_OVERLOADS to be reported in those cases.
|
||||
// See a.containingDeclaration == b.containingDeclaration check in
|
||||
// org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent.
|
||||
if (first.moduleData == second.moduleData) return false
|
||||
if (first.isExpect != second.isExpect) return false
|
||||
if (first is FirVariable != second is FirVariable) {
|
||||
return false
|
||||
@@ -73,15 +85,16 @@ class ConeEquivalentCallConflictResolver(
|
||||
|
||||
val overrideChecker = FirStandardOverrideChecker(inferenceComponents.session)
|
||||
return if (first is FirProperty && second is FirProperty) {
|
||||
overrideChecker.isOverriddenProperty(first, second) && overrideChecker.isOverriddenProperty(second, first)
|
||||
overrideChecker.isOverriddenProperty(first, second, ignoreVisibility = true) &&
|
||||
overrideChecker.isOverriddenProperty(second, first, ignoreVisibility = true)
|
||||
} else if (first is FirSimpleFunction && second is FirSimpleFunction) {
|
||||
overrideChecker.isOverriddenFunction(first, second) && overrideChecker.isOverriddenFunction(second, first)
|
||||
overrideChecker.isOverriddenFunction(first, second, ignoreVisibility = true) &&
|
||||
overrideChecker.isOverriddenFunction(second, first, ignoreVisibility = true)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the candidate is a function, then the arguments
|
||||
* order representation is an array containing the
|
||||
|
||||
+17
-5
@@ -122,32 +122,44 @@ class FirStandardOverrideChecker(private val session: FirSession) : FirAbstractO
|
||||
}
|
||||
|
||||
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
|
||||
return isOverriddenFunction(overrideCandidate, baseDeclaration, ignoreVisibility = false)
|
||||
}
|
||||
|
||||
fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction, ignoreVisibility: Boolean): Boolean {
|
||||
if (overrideCandidate.valueParameters.size != baseDeclaration.valueParameters.size) return false
|
||||
|
||||
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
|
||||
|
||||
if (!commonCallableChecks(overrideCandidate, baseDeclaration, substitutor)) return false
|
||||
if (!commonCallableChecks(overrideCandidate, baseDeclaration, substitutor, ignoreVisibility)) return false
|
||||
|
||||
return overrideCandidate.valueParameters.zip(baseDeclaration.valueParameters).all { (memberParam, selfParam) ->
|
||||
isEqualTypes(memberParam.returnTypeRef, selfParam.returnTypeRef, substitutor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isOverriddenProperty(
|
||||
override fun isOverriddenProperty(overrideCandidate: FirCallableDeclaration, baseDeclaration: FirProperty): Boolean {
|
||||
return isOverriddenProperty(overrideCandidate, baseDeclaration, ignoreVisibility = false)
|
||||
}
|
||||
|
||||
fun isOverriddenProperty(
|
||||
overrideCandidate: FirCallableDeclaration,
|
||||
baseDeclaration: FirProperty
|
||||
baseDeclaration: FirProperty,
|
||||
ignoreVisibility: Boolean,
|
||||
): Boolean {
|
||||
if (overrideCandidate !is FirProperty) return false
|
||||
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false
|
||||
return commonCallableChecks(overrideCandidate, baseDeclaration, substitutor)
|
||||
return commonCallableChecks(overrideCandidate, baseDeclaration, substitutor, ignoreVisibility)
|
||||
}
|
||||
|
||||
private fun FirStandardOverrideChecker.commonCallableChecks(
|
||||
overrideCandidate: FirCallableDeclaration,
|
||||
baseDeclaration: FirCallableDeclaration,
|
||||
substitutor: ConeSubstitutor,
|
||||
// Overload-ability is used to filter out equivalent calls (see ConeEquivalentCallConflictResolver) in which case visibility
|
||||
// must be ignored.
|
||||
ignoreVisibility: Boolean,
|
||||
): Boolean {
|
||||
if (Visibilities.isPrivate(baseDeclaration.visibility)) return false
|
||||
if (!ignoreVisibility && Visibilities.isPrivate(baseDeclaration.visibility)) return false
|
||||
if (overrideCandidate.contextReceivers.size != baseDeclaration.contextReceivers.size) return false
|
||||
|
||||
overrideCandidate.lazyResolveToPhase(FirResolvePhase.TYPES)
|
||||
|
||||
Reference in New Issue
Block a user