[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
+2
-4
@@ -21,13 +21,11 @@ object DefaultCallConflictResolverFactory : ConeCallConflictResolverFactory() {
|
||||
transformerComponents: BodyResolveComponents
|
||||
): ConeCompositeConflictResolver {
|
||||
val specificityComparator = TypeSpecificityComparator.NONE
|
||||
// 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),
|
||||
ConeIntegerOperatorConflictResolver,
|
||||
ConeOverloadConflictResolver(specificityComparator, components, transformerComponents),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -25,13 +25,11 @@ object JsCallConflictResolverFactory : ConeCallConflictResolverFactory() {
|
||||
transformerComponents: BodyResolveComponents
|
||||
): ConeCompositeConflictResolver {
|
||||
val specificityComparator = JsTypeSpecificityComparatorWithoutDelegate(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),
|
||||
ConeIntegerOperatorConflictResolver,
|
||||
ConeOverloadConflictResolver(specificityComparator, components, transformerComponents),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ class ConeCompositeConflictResolver(
|
||||
var index = 0
|
||||
while (currentCandidates.size > 1 && index < conflictResolvers.size) {
|
||||
val conflictResolver = conflictResolvers[index++]
|
||||
currentCandidates = conflictResolver.chooseMaximallySpecificCandidates(candidates, discriminateAbstracts)
|
||||
currentCandidates = conflictResolver.chooseMaximallySpecificCandidates(currentCandidates, discriminateAbstracts)
|
||||
}
|
||||
return currentCandidates
|
||||
}
|
||||
|
||||
+2
-2
@@ -131,13 +131,13 @@ fun main() {
|
||||
val list10 = buildList {
|
||||
add("one")
|
||||
|
||||
foo0(get(0), 0f)
|
||||
<!OVERLOAD_RESOLUTION_AMBIGUITY!>foo0<!>(get(0), 0f)
|
||||
}
|
||||
val list11 = buildList {
|
||||
add("one")
|
||||
|
||||
val x = get(0)
|
||||
foo0(x, 0f)
|
||||
<!OVERLOAD_RESOLUTION_AMBIGUITY!>foo0<!>(x, 0f)
|
||||
}
|
||||
val list12 = buildList {
|
||||
add("one")
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ private val validVal = 1
|
||||
private val <!REDECLARATION!>invalidProp0<!> = 1
|
||||
|
||||
// NB invalidFun0 and invalidProp0 are conflicting overloads, since the following is an ambiguity:
|
||||
fun useInvalidFun0() = invalidFun0()
|
||||
fun useInvalidProp0() = invalidProp0
|
||||
fun useInvalidFun0() = <!OVERLOAD_RESOLUTION_AMBIGUITY!>invalidFun0<!>()
|
||||
fun useInvalidProp0() = <!OVERLOAD_RESOLUTION_AMBIGUITY!>invalidProp0<!>
|
||||
|
||||
<!CONFLICTING_OVERLOADS!>private fun invalidFun1()<!> {}
|
||||
<!CONFLICTING_OVERLOADS!>private fun invalidFun1()<!> {}
|
||||
|
||||
+1
-1
@@ -4,5 +4,5 @@ fun <T> f1(l: <!UNRESOLVED_REFERENCE!>List2<T><!>): T {throw Exception()} // ERR
|
||||
fun <T> f1(c: Collection<T>): T{throw Exception()}
|
||||
|
||||
fun <T> test(l: List<T>) {
|
||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>f1<!>(l)
|
||||
<!OVERLOAD_RESOLUTION_AMBIGUITY!>f1<!>(l)
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
fun foo(vararg t : String) = ""
|
||||
fun foo(vararg t : Int) = ""
|
||||
|
||||
fun test() {
|
||||
foo()
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
fun foo(vararg t : String) = ""
|
||||
fun foo(vararg t : Int) = ""
|
||||
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ fun case1() {
|
||||
val a: Boolean? = false
|
||||
checkSubtype<Boolean?>(a)
|
||||
val x4 = <!CONDITION_TYPE_MISMATCH!>a<!> && true
|
||||
x4 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>() }
|
||||
x4 checkType { check<Boolean>() }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 2
|
||||
@@ -44,7 +44,7 @@ fun case2() {
|
||||
val a: Any = false
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>a<!>
|
||||
val x4 = <!CONDITION_TYPE_MISMATCH!>a<!> && true
|
||||
x4 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>() }
|
||||
x4 checkType { check<Boolean>() }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 3
|
||||
@@ -56,7 +56,7 @@ fun case3() {
|
||||
val x3 = a1 && <!CONDITION_TYPE_MISMATCH!>a2<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean")!>x3<!>
|
||||
|
||||
x3 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>() }
|
||||
x3 checkType { check<Boolean>() }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 4
|
||||
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
|
||||
// SKIP_TXT
|
||||
|
||||
/*
|
||||
* KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE)
|
||||
*
|
||||
* SPEC VERSION: 0.1-313
|
||||
* MAIN LINK: expressions, logical-conjunction-expression -> paragraph 2 -> sentence 1
|
||||
* PRIMARY LINKS: expressions, logical-conjunction-expression -> paragraph 2 -> sentence 2
|
||||
* NUMBER: 1
|
||||
* DESCRIPTION: Both operands of a logical conjunction expression must have a type which is a subtype of kotlin.Boolean
|
||||
* HELPERS: checkType
|
||||
*/
|
||||
|
||||
// MODULE: libModule
|
||||
// FILE: libModule/JavaClass.java
|
||||
package libModule;
|
||||
|
||||
public class JavaClass {
|
||||
public static boolean VALUE;
|
||||
|
||||
public static Object VALUE_OBJ = true;
|
||||
|
||||
public Boolean getValue ()
|
||||
{ return new Boolean ("true"); }
|
||||
}
|
||||
|
||||
// MODULE: mainModule(libModule)
|
||||
// FILE: KotlinClass.kt
|
||||
package mainModule
|
||||
import libModule.*
|
||||
import checkSubtype
|
||||
import checkType
|
||||
import check
|
||||
|
||||
fun foo() = run { false && JavaClass.VALUE && throw Exception() }
|
||||
|
||||
|
||||
// TESTCASE NUMBER: 1
|
||||
fun case1() {
|
||||
val a1 = false
|
||||
val a2 = JavaClass.VALUE
|
||||
val a3 = foo()
|
||||
val a4 = JavaClass().getValue()
|
||||
val a5 = JavaClass.VALUE_OBJ
|
||||
|
||||
checkSubtype<Boolean>(a1)
|
||||
checkSubtype<Boolean>(a2)
|
||||
checkSubtype<Boolean>(a3)
|
||||
checkSubtype<Boolean>(a4)
|
||||
|
||||
val x3 = a1 && a2 && a3 && a4 && a5 as Boolean
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean")!>x3<!>
|
||||
|
||||
x3 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>()}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
+3
-3
@@ -36,7 +36,7 @@ fun case1() {
|
||||
val a: Boolean? = false
|
||||
checkSubtype<Boolean?>(a)
|
||||
val x4 = <!CONDITION_TYPE_MISMATCH!>a<!> || true
|
||||
x4 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>() }
|
||||
x4 checkType { check<Boolean>() }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 2
|
||||
@@ -44,7 +44,7 @@ fun case2() {
|
||||
val a: Any = false
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>a<!>
|
||||
val x4 = <!CONDITION_TYPE_MISMATCH!>a<!> || true
|
||||
x4 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>() }
|
||||
x4 checkType { check<Boolean>() }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 3
|
||||
@@ -56,7 +56,7 @@ fun case3() {
|
||||
val x3 = a1 || <!CONDITION_TYPE_MISMATCH!>a2<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean")!>x3<!>
|
||||
|
||||
x3 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>() }
|
||||
x3 checkType { check<Boolean>() }
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 4
|
||||
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
|
||||
// SKIP_TXT
|
||||
|
||||
/*
|
||||
* KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE)
|
||||
*
|
||||
* SPEC VERSION: 0.1-313
|
||||
* MAIN LINK: expressions, logical-disjunction-expression -> paragraph 2 -> sentence 1
|
||||
* PRIMARY LINKS: expressions, logical-disjunction-expression -> paragraph 2 -> sentence 2
|
||||
* NUMBER: 1
|
||||
* DESCRIPTION:Both operands of a logical disjunction expression must have a type which is a subtype of kotlin.Boolean
|
||||
* HELPERS: checkType
|
||||
*/
|
||||
|
||||
// MODULE: libModule
|
||||
// FILE: libModule/JavaClass.java
|
||||
package libModule;
|
||||
|
||||
public class JavaClass {
|
||||
public static boolean VALUE;
|
||||
|
||||
public static Object VALUE_OBJ = true;
|
||||
|
||||
public Boolean getValue ()
|
||||
{ return new Boolean ("true"); }
|
||||
}
|
||||
|
||||
// MODULE: mainModule(libModule)
|
||||
// FILE: KotlinClass.kt
|
||||
package mainModule
|
||||
import libModule.*
|
||||
import checkSubtype
|
||||
import checkType
|
||||
import check
|
||||
|
||||
fun foo() = run { false || JavaClass.VALUE || throw Exception() }
|
||||
|
||||
|
||||
// TESTCASE NUMBER: 1
|
||||
fun case1() {
|
||||
val a1 = false
|
||||
val a2 = JavaClass.VALUE
|
||||
val a3 = foo()
|
||||
val a4 = JavaClass().getValue()
|
||||
val a5 = JavaClass.VALUE_OBJ
|
||||
|
||||
checkSubtype<Boolean>(a1)
|
||||
checkSubtype<Boolean>(a2)
|
||||
checkSubtype<Boolean>(a3)
|
||||
checkSubtype<Boolean>(a4)
|
||||
|
||||
val x3 = a1 || a2 || a3 || a4 || a5 as Boolean
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Boolean")!>x3<!>
|
||||
|
||||
x3 <!OVERLOAD_RESOLUTION_AMBIGUITY!>checkType<!> { <!NONE_APPLICABLE!>check<!><Boolean>()}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
+20
-1
@@ -44,7 +44,11 @@ class CustomK2Tests : KGPBaseTest() {
|
||||
@GradleTest
|
||||
@DisplayName("Serialization plugin in common source set. KT-56911")
|
||||
fun testHmppDependenciesInJsTests(gradleVersion: GradleVersion) {
|
||||
project("k2-serialization-plugin-in-common-sourceset", gradleVersion) {
|
||||
project(
|
||||
"k2-serialization-plugin-in-common-sourceset",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(languageVersion = "2.0"),
|
||||
) {
|
||||
val taskToExecute = ":compileKotlinJs"
|
||||
build(taskToExecute) {
|
||||
assertTasksExecuted(taskToExecute)
|
||||
@@ -155,4 +159,19 @@ class CustomK2Tests : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GradleTest
|
||||
@DisplayName("Common metadata compilation. KT-60943")
|
||||
fun kt60943CommonMetadataCompilation(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
"k2-serialization-plugin-in-common-sourceset",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(languageVersion = "2.0"),
|
||||
) {
|
||||
val taskToExecute = ":compileCommonMainKotlinMetadata"
|
||||
build(taskToExecute) {
|
||||
assertTasksExecuted(taskToExecute)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -23,11 +23,6 @@ kotlin {
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings.apply {
|
||||
languageVersion = "2.0"
|
||||
}
|
||||
}
|
||||
val commonMain by getting{
|
||||
dependencies{
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0-RC")
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun toArray(a : List<String>){
|
||||
a.toTypedArray()
|
||||
}
|
||||
Reference in New Issue
Block a user