FIR: Refine SAM support for methods with names from j.l.Object
This commit is contained in:
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.fir.types.*
|
|||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
|
||||||
@@ -263,10 +264,13 @@ private fun FirRegularClass.findSingleAbstractMethodByNames(
|
|||||||
classUseSiteMemberScope.processFunctionsByName(candidateName) { functionSymbol ->
|
classUseSiteMemberScope.processFunctionsByName(candidateName) { functionSymbol ->
|
||||||
val firFunction = functionSymbol.fir
|
val firFunction = functionSymbol.fir
|
||||||
require(firFunction is FirSimpleFunction) {
|
require(firFunction is FirSimpleFunction) {
|
||||||
"${functionSymbol.callableId.callableName} is expected to be _root_ide_package_.org.jetbrains.kotlin.fir.declarations.FirSimpleFunction, but ${functionSymbol::class} was found"
|
"${functionSymbol.callableId
|
||||||
|
.callableName} is expected to be _root_ide_package_.org.jetbrains.kotlin.fir.declarations.FirSimpleFunction, but ${functionSymbol::class} was found"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firFunction.modality != Modality.ABSTRACT || firFunction.isPublicInObject()) return@processFunctionsByName ProcessorAction.NEXT
|
if (firFunction.modality != Modality.ABSTRACT || firFunction
|
||||||
|
.isPublicInObject(checkOnlyName = false)
|
||||||
|
) return@processFunctionsByName ProcessorAction.NEXT
|
||||||
|
|
||||||
if (resultMethod != null) {
|
if (resultMethod != null) {
|
||||||
metIncorrectMember = true
|
metIncorrectMember = true
|
||||||
@@ -287,7 +291,9 @@ private fun FirRegularClass.hasMoreThenOneAbstractFunctionOrHasAbstractProperty(
|
|||||||
var wasAbstractFunction = false
|
var wasAbstractFunction = false
|
||||||
for (declaration in declarations) {
|
for (declaration in declarations) {
|
||||||
if (declaration is FirProperty && declaration.modality == Modality.ABSTRACT) return true
|
if (declaration is FirProperty && declaration.modality == Modality.ABSTRACT) return true
|
||||||
if (declaration is FirSimpleFunction && declaration.modality == Modality.ABSTRACT && !declaration.isPublicInObject()) {
|
if (declaration is FirSimpleFunction && declaration.modality == Modality.ABSTRACT &&
|
||||||
|
!declaration.isPublicInObject(checkOnlyName = true)
|
||||||
|
) {
|
||||||
if (wasAbstractFunction) return true
|
if (wasAbstractFunction) return true
|
||||||
wasAbstractFunction = true
|
wasAbstractFunction = true
|
||||||
}
|
}
|
||||||
@@ -300,9 +306,36 @@ private fun FirRegularClass.hasMoreThenOneAbstractFunctionOrHasAbstractProperty(
|
|||||||
// "methods that are members of I that do not have the same signature as any public instance method of the class Object"
|
// "methods that are members of I that do not have the same signature as any public instance method of the class Object"
|
||||||
// It means that if an interface declares `int hashCode()` then the method won't be taken into account when
|
// It means that if an interface declares `int hashCode()` then the method won't be taken into account when
|
||||||
// checking if the interface is SAM.
|
// checking if the interface is SAM.
|
||||||
private fun FirSimpleFunction.isPublicInObject(): Boolean {
|
private fun FirSimpleFunction.isPublicInObject(checkOnlyName: Boolean): Boolean {
|
||||||
// TODO: We make here a conservative check just filtering out methods by name, check the signature as well
|
if (name.asString() !in PUBLIC_METHOD_NAMES_IN_OBJECT) return false
|
||||||
return name.asString() in PUBLIC_METHOD_NAMES_IN_OBJECT
|
if (checkOnlyName) return true
|
||||||
|
|
||||||
|
return when (name.asString()) {
|
||||||
|
"hashCode", "getClass", "notify", "notifyAll", "toString" -> valueParameters.isEmpty()
|
||||||
|
"equals" -> valueParameters.singleOrNull()?.hasTypeOf(StandardClassIds.Any, allowNullable = true) == true
|
||||||
|
"wait" -> when (valueParameters.size) {
|
||||||
|
0 -> true
|
||||||
|
1 -> valueParameters[0].hasTypeOf(StandardClassIds.Long, allowNullable = false)
|
||||||
|
2 -> valueParameters[0].hasTypeOf(StandardClassIds.Long, allowNullable = false) &&
|
||||||
|
valueParameters[1].hasTypeOf(StandardClassIds.Int, allowNullable = false)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
else -> error("Unexpected method name: $name")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirValueParameter.hasTypeOf(classId: ClassId, allowNullable: Boolean): Boolean {
|
||||||
|
val type = returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return false
|
||||||
|
|
||||||
|
val classLike = when (type) {
|
||||||
|
is ConeClassLikeType -> type
|
||||||
|
is ConeFlexibleType -> type.upperBound as? ConeClassLikeType ?: return false
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classLike.isMarkedNullable && !allowNullable) return false
|
||||||
|
return classLike.lookupTag.classId == classId
|
||||||
}
|
}
|
||||||
|
|
||||||
private val PUBLIC_METHOD_NAMES_IN_OBJECT = setOf("equals", "hashCode", "getClass", "wait", "notify", "notifyAll", "toString")
|
private val PUBLIC_METHOD_NAMES_IN_OBJECT = setOf("equals", "hashCode", "getClass", "wait", "notify", "notifyAll", "toString")
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// FILE: KotlinTypeChecker.java
|
||||||
|
|
||||||
|
public interface KotlinTypeChecker {
|
||||||
|
|
||||||
|
interface TypeConstructorEquality {
|
||||||
|
boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b);
|
||||||
|
}
|
||||||
|
|
||||||
|
KotlinTypeChecker DEFAULT = NewKotlinTypeChecker.Companion.getDefault();
|
||||||
|
|
||||||
|
boolean isSubtypeOf(@NotNull KotlinType subtype, @NotNull KotlinType supertype);
|
||||||
|
boolean equalTypes(@NotNull KotlinType a, @NotNull KotlinType b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: OverridingUtil.java
|
||||||
|
public class OverridingUtil {
|
||||||
|
public static OverridingUtil createWithEqualityAxioms(@NotNull KotlinTypeChecker.TypeConstructorEquality equalityAxioms) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
interface TypeConstructor {
|
||||||
|
val x: String
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
OverridingUtil.createWithEqualityAxioms l1@{ c1, c2 ->
|
||||||
|
if (c1.x == c2.x) return@l1 true
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
FILE: main.kt
|
||||||
|
public abstract interface TypeConstructor : R|kotlin/Any| {
|
||||||
|
public abstract val x: R|kotlin/String|
|
||||||
|
public get(): R|kotlin/String|
|
||||||
|
|
||||||
|
}
|
||||||
|
public final fun main(): R|kotlin/Unit| {
|
||||||
|
Q|OverridingUtil|.R|/OverridingUtil.createWithEqualityAxioms|(<L> = l1@fun <anonymous>(c1: R|ft<TypeConstructor, TypeConstructor?>!|, c2: R|ft<TypeConstructor, TypeConstructor?>!|): R|kotlin/Boolean| {
|
||||||
|
when () {
|
||||||
|
==(R|<local>/c1|.R|/TypeConstructor.x|, R|<local>/c2|.R|/TypeConstructor.x|) -> {
|
||||||
|
^@l1 Boolean(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
+5
@@ -1115,6 +1115,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
|||||||
runTest("compiler/fir/resolve/testData/resolve/samConversions/samSupertypeWithOverride.kt");
|
runTest("compiler/fir/resolve/testData/resolve/samConversions/samSupertypeWithOverride.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("samWithEquals.kt")
|
||||||
|
public void testSamWithEquals() throws Exception {
|
||||||
|
runTest("compiler/fir/resolve/testData/resolve/samConversions/samWithEquals.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("simple.kt")
|
@TestMetadata("simple.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testSimple() throws Exception {
|
||||||
runTest("compiler/fir/resolve/testData/resolve/samConversions/simple.kt");
|
runTest("compiler/fir/resolve/testData/resolve/samConversions/simple.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user