[PL] Handle illegal fun interface SAM conversions
^KT-53967
This commit is contained in:
committed by
Space Team
parent
7f4d9ddde5
commit
1536bca1bb
@@ -103,6 +103,17 @@ sealed interface PartialLinkageCase {
|
||||
val functionValueParameterCount: Int
|
||||
) : PartialLinkageCase
|
||||
|
||||
/**
|
||||
* SAM-conversion to a function interface that effectively has more than one abstract function or at least one abstract property.
|
||||
*
|
||||
* Applicable to: Expressions.
|
||||
*/
|
||||
class InvalidSamConversion(
|
||||
val expression: IrTypeOperatorCall,
|
||||
val abstractFunctionSymbols: Set<IrSimpleFunctionSymbol>,
|
||||
val abstractPropertySymbol: IrPropertySymbol?
|
||||
) : PartialLinkageCase
|
||||
|
||||
/**
|
||||
* An [IrCall] of suspendable function at the place where no coroutine context is available.
|
||||
*
|
||||
|
||||
+30
-1
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.linkage.partial.ExploredClassifier.Unusable
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.util.IdSignature.*
|
||||
import org.jetbrains.kotlin.ir.util.isAnonymousObject
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
@@ -54,6 +55,10 @@ internal fun PartialLinkageCase.renderLinkageError(): String = buildString {
|
||||
)
|
||||
}
|
||||
|
||||
is InvalidSamConversion -> expression(expression) {
|
||||
invalidSamConversion(expression, abstractFunctionSymbols, abstractPropertySymbol)
|
||||
}
|
||||
|
||||
is SuspendableFunctionCallWithoutCoroutineContext -> expression(expression) {
|
||||
suspendableCallWithoutCoroutine()
|
||||
}
|
||||
@@ -143,6 +148,7 @@ private enum class ExpressionKind(val prefix: String?, val postfix: String?) {
|
||||
WRITING("Can not write value to", null),
|
||||
GETTING_INSTANCE("Can not get instance of", null),
|
||||
TYPE_OPERATOR("Type operator expression", "can not be evaluated"),
|
||||
SAM_CONVERSION("Single abstract method (SAM) conversion expression", "can not be evaluated"),
|
||||
ANONYMOUS_OBJECT_LITERAL("Anonymous object literal", "can not be evaluated"),
|
||||
OTHER_EXPRESSION("Expression", "can not be evaluated")
|
||||
}
|
||||
@@ -169,7 +175,12 @@ private val IrExpression.expression: Expression
|
||||
else -> Expression(REFERENCE, OTHER_DECLARATION)
|
||||
}
|
||||
is IrInstanceInitializerCall -> Expression(CALLING_INSTANCE_INITIALIZER, classSymbol.declarationKind)
|
||||
is IrTypeOperatorCall -> Expression(TYPE_OPERATOR, null)
|
||||
is IrTypeOperatorCall -> {
|
||||
if (operator == IrTypeOperator.SAM_CONVERSION)
|
||||
Expression(SAM_CONVERSION, null)
|
||||
else
|
||||
Expression(TYPE_OPERATOR, null)
|
||||
}
|
||||
else -> {
|
||||
if (this is IrBlock && origin == IrStatementOrigin.OBJECT_LITERAL)
|
||||
Expression(ANONYMOUS_OBJECT_LITERAL, null)
|
||||
@@ -266,6 +277,11 @@ private fun Appendable.declarationKindName(symbol: IrSymbol, capitalized: Boolea
|
||||
return append(" ").declarationName(symbol)
|
||||
}
|
||||
|
||||
private fun Appendable.sortedDeclarationsName(symbols: Set<IrSymbol>): Appendable {
|
||||
symbols.map { symbol -> buildString inner@ { this@inner.declarationName(symbol) } }.sorted().joinTo(this)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun Appendable.declarationNameIsKind(symbol: IrSymbol): Appendable =
|
||||
declarationName(symbol).append(" is ").declarationKind(symbol, capitalized = false)
|
||||
|
||||
@@ -515,6 +531,19 @@ private fun Appendable.memberAccessExpressionArgumentsMismatch(
|
||||
.append(functionValueParameterCount.toString()).append(")")
|
||||
}
|
||||
|
||||
private fun Appendable.invalidSamConversion(
|
||||
expression: IrTypeOperatorCall,
|
||||
abstractFunctionSymbols: Set<IrSimpleFunctionSymbol>,
|
||||
abstractPropertySymbol: IrPropertySymbol?,
|
||||
): Appendable {
|
||||
declarationKindName(expression.typeOperand.classifierOrFail, capitalized = true)
|
||||
return when {
|
||||
abstractPropertySymbol != null -> append(" has abstract ").declarationKindName(abstractPropertySymbol, capitalized = false)
|
||||
abstractFunctionSymbols.isEmpty() -> append(" does not have an abstract function")
|
||||
else -> append(" has more than one abstract function: ").sortedDeclarationsName(abstractFunctionSymbols)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Appendable.suspendableCallWithoutCoroutine(): Appendable =
|
||||
append("Suspend function can be called only from a coroutine or another suspend function")
|
||||
|
||||
|
||||
+36
-3
@@ -24,9 +24,7 @@ import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageCase.*
|
||||
import org.jetbrains.kotlin.ir.overrides.isEffectivelyPrivate
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
@@ -35,6 +33,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import org.jetbrains.kotlin.utils.newHashSetWithExpectedSize
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageUtils.File as PLFile
|
||||
@@ -481,6 +480,7 @@ internal class PartiallyLinkedIrTreePatcher(
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall) = expression.maybeThrowLinkageError {
|
||||
(typeOperand !== type).ifTrue { checkExpressionType(typeOperand) }
|
||||
?: checkSamConversion()
|
||||
}
|
||||
|
||||
override fun visitDeclarationReference(expression: IrDeclarationReference) = expression.maybeThrowLinkageError {
|
||||
@@ -813,6 +813,39 @@ internal class PartiallyLinkedIrTreePatcher(
|
||||
null
|
||||
}
|
||||
|
||||
private fun IrTypeOperatorCall.checkSamConversion(): PartialLinkageCase? {
|
||||
if (operator != IrTypeOperator.SAM_CONVERSION) return null
|
||||
|
||||
val funInterface: IrClass = typeOperand.classOrNull?.owner ?: return null
|
||||
|
||||
val abstractFunctionSymbols = newHashSetWithExpectedSize<IrSimpleFunctionSymbol>(funInterface.declarations.size)
|
||||
funInterface.declarations.forEach { member ->
|
||||
when (member) {
|
||||
is IrSimpleFunction -> {
|
||||
if (member.modality == Modality.ABSTRACT)
|
||||
abstractFunctionSymbols += member.symbol
|
||||
}
|
||||
is IrProperty -> {
|
||||
if (member.modality == Modality.ABSTRACT)
|
||||
return InvalidSamConversion(
|
||||
expression = this,
|
||||
abstractFunctionSymbols = emptySet(),
|
||||
abstractPropertySymbol = member.symbol
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (abstractFunctionSymbols.size != 1)
|
||||
InvalidSamConversion(
|
||||
expression = this,
|
||||
abstractFunctionSymbols = abstractFunctionSymbols,
|
||||
abstractPropertySymbol = null
|
||||
)
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
private fun IrConstructorCall.checkNotAbstractClass(): PartialLinkageCase? {
|
||||
val createdClass = symbol.owner.parentAsClass
|
||||
return if (createdClass.modality == Modality.ABSTRACT || createdClass.modality == Modality.SEALED)
|
||||
|
||||
+13
-4
@@ -133,10 +133,6 @@ class ClassToValue(val x: Int)
|
||||
|
||||
data class DataToClass(val x: Int, val y: Int)
|
||||
|
||||
fun interface FunctionalInterfaceToInterface {
|
||||
fun work()
|
||||
}
|
||||
|
||||
class ClassToAbstractClass {
|
||||
var name: String = "Alice"
|
||||
fun getGreeting() = "Hello, $name!"
|
||||
@@ -150,3 +146,16 @@ object PublicTopLevelLib1 {
|
||||
class ClassThatBecomesPrivate
|
||||
enum class EnumClassThatBecomesPrivate { ENTRY }
|
||||
}
|
||||
|
||||
interface XAnswer { fun answer(): Int }
|
||||
interface XAnswerDefault { fun answer(): Int /*= 42*/ }
|
||||
interface XFunction1 { /*fun function1(): Int*/ }
|
||||
interface XFunction1Default { /*fun function1(): Int = 42*/ }
|
||||
interface XFunction2 { /*fun function2(): Int*/ }
|
||||
interface XFunction2Default { /*fun function2(): Int = -42*/ }
|
||||
interface XProperty1 { /*val property1: Int*/ }
|
||||
interface XProperty1Default { /*val property1: Int get() = 42*/ }
|
||||
interface XProperty2 { /*val property2: Int*/ }
|
||||
interface XProperty2Default { /*val property2: Int get() = 42*/ }
|
||||
|
||||
fun interface FunctionalInterfaceToInterface : XAnswer
|
||||
|
||||
+13
-4
@@ -133,10 +133,6 @@ value class ClassToValue(val x: Int)
|
||||
|
||||
/*data*/ class DataToClass(val x: Int, val y: Int)
|
||||
|
||||
/*fun*/ interface FunctionalInterfaceToInterface {
|
||||
fun work()
|
||||
}
|
||||
|
||||
abstract class ClassToAbstractClass {
|
||||
abstract var name: String
|
||||
fun getGreeting() = "Hello, $name!"
|
||||
@@ -150,3 +146,16 @@ object PublicTopLevelLib1 {
|
||||
private class ClassThatBecomesPrivate
|
||||
private enum class EnumClassThatBecomesPrivate { ENTRY }
|
||||
}
|
||||
|
||||
interface XAnswer { fun answer(): Int }
|
||||
interface XAnswerDefault { fun answer(): Int /*= 42*/ }
|
||||
interface XFunction1 { fun function1(): Int }
|
||||
interface XFunction1Default { fun function1(): Int = 42 }
|
||||
interface XFunction2 { fun function2(): Int }
|
||||
interface XFunction2Default { fun function2(): Int = -42 }
|
||||
interface XProperty1 { val property1: Int }
|
||||
interface XProperty1Default { val property1: Int get() = 42 }
|
||||
interface XProperty2 { val property2: Int }
|
||||
interface XProperty2Default { val property2: Int get() = 42 }
|
||||
|
||||
/*fun*/ interface FunctionalInterfaceToInterface : XAnswer
|
||||
|
||||
+103
-6
@@ -1,3 +1,4 @@
|
||||
|
||||
fun getClassToEnumFoo(): ClassToEnum.Foo = ClassToEnum.Foo()
|
||||
inline fun getClassToEnumFooInline(): ClassToEnum.Foo = ClassToEnum.Foo()
|
||||
fun getClassToEnumFooAsAny(): Any = ClassToEnum.Foo()
|
||||
@@ -378,12 +379,6 @@ fun getSumFromDataClass(): Int {
|
||||
return x + y
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceToInterface(): FunctionalInterfaceToInterface {
|
||||
val worker = FunctionalInterfaceToInterface { /* do some work */ }
|
||||
worker.work()
|
||||
return worker
|
||||
}
|
||||
|
||||
fun instantiationOfAbstractClass() {
|
||||
// Accessing uninitialized members of abstract class is an UB. We shall not allow instantiating
|
||||
// abstract classes except for from their direct inheritors.
|
||||
@@ -424,3 +419,105 @@ class StableInheritorOfClassThatUsesPrivateTopLevelClass : AbstractIterator<Stri
|
||||
fun testStableInheritorOfClassThatUsesPrivateTopLevelClass(): String = buildString {
|
||||
for (s in StableInheritorOfClassThatUsesPrivateTopLevelClass()) append(s)
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceToInterface(answer: Int): FunctionalInterfaceToInterface {
|
||||
val worker = FunctionalInterfaceToInterface { answer }
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceToInterfaceAsObject(answer: Int): FunctionalInterfaceToInterface {
|
||||
val worker = object : FunctionalInterfaceToInterface {
|
||||
override fun answer() = answer
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceToInterfaceAnswer(answer: Int): Int {
|
||||
return getFunctionalInterfaceToInterface(answer).answer()
|
||||
}
|
||||
|
||||
fun interface FunctionalInterfaceWith0AbstractFunctions : XAnswerDefault
|
||||
fun interface FunctionalInterfaceWith1AbstractFunction : XAnswer, XFunction1Default, XFunction2Default, XProperty1Default, XProperty2Default
|
||||
fun interface FunctionalInterfaceWith2AbstractFunctions : XAnswer, XFunction1, XFunction2Default, XProperty1Default, XProperty2Default
|
||||
fun interface FunctionalInterfaceWith3AbstractFunctions : XAnswer, XFunction1, XFunction2, XProperty1Default, XProperty2Default
|
||||
fun interface FunctionalInterfaceWithAbstractProperty : XAnswer, XFunction1Default, XFunction2Default, XProperty1, XProperty2Default
|
||||
|
||||
fun getFunctionalInterfaceWith0AbstractFunctions(answer: Int): FunctionalInterfaceWith0AbstractFunctions {
|
||||
val worker = FunctionalInterfaceWith0AbstractFunctions { answer }
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith0AbstractFunctionsAsObject(answer: Int): FunctionalInterfaceWith0AbstractFunctions {
|
||||
val worker = object : FunctionalInterfaceWith0AbstractFunctions {
|
||||
override fun answer() = answer
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith0AbstractFunctionsAnswer(answer: Int): Int {
|
||||
return getFunctionalInterfaceWith0AbstractFunctions(answer).answer()
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith1AbstractFunction(answer: Int): FunctionalInterfaceWith1AbstractFunction {
|
||||
val worker = FunctionalInterfaceWith1AbstractFunction { answer }
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith1AbstractFunctionAsObject(answer: Int): FunctionalInterfaceWith1AbstractFunction {
|
||||
val worker = object : FunctionalInterfaceWith1AbstractFunction {
|
||||
override fun answer() = answer
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith1AbstractFunctionAnswer(answer: Int): Int {
|
||||
return getFunctionalInterfaceWith1AbstractFunction(answer).answer()
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith2AbstractFunctions(answer: Int): FunctionalInterfaceWith2AbstractFunctions {
|
||||
val worker = FunctionalInterfaceWith2AbstractFunctions { answer }
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith2AbstractFunctionsAsObject(answer: Int): FunctionalInterfaceWith2AbstractFunctions {
|
||||
val worker = object : FunctionalInterfaceWith2AbstractFunctions {
|
||||
override fun answer() = answer
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith2AbstractFunctionsAnswer(answer: Int): Int {
|
||||
return getFunctionalInterfaceWith2AbstractFunctions(answer).answer()
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith3AbstractFunctions(answer: Int): FunctionalInterfaceWith3AbstractFunctions {
|
||||
val worker = FunctionalInterfaceWith3AbstractFunctions { answer }
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith3AbstractFunctionsAsObject(answer: Int): FunctionalInterfaceWith3AbstractFunctions {
|
||||
val worker = object : FunctionalInterfaceWith3AbstractFunctions {
|
||||
override fun answer() = answer
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWith3AbstractFunctionsAnswer(answer: Int): Int {
|
||||
return getFunctionalInterfaceWith3AbstractFunctions(answer).answer()
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWithAbstractProperty(answer: Int): FunctionalInterfaceWithAbstractProperty {
|
||||
val worker = FunctionalInterfaceWithAbstractProperty { answer }
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWithAbstractPropertyAsObject(answer: Int): FunctionalInterfaceWithAbstractProperty {
|
||||
val worker = object : FunctionalInterfaceWithAbstractProperty {
|
||||
override fun answer() = answer
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
fun getFunctionalInterfaceWithAbstractPropertyAnswer(answer: Int): Int {
|
||||
return getFunctionalInterfaceWithAbstractProperty(answer).answer()
|
||||
}
|
||||
|
||||
+19
-2
@@ -333,12 +333,29 @@ fun box() = abiTest {
|
||||
|
||||
expectFailure(linkage("Function 'component1' can not be called: No function found for symbol '/DataToClass.component1'")) { getSumFromDataClass() }
|
||||
|
||||
expectSuccess { getFunctionalInterfaceToInterface(); "OK" }
|
||||
|
||||
expectFailure(linkage("Constructor 'ClassToAbstractClass.<init>' can not be called: Can not instantiate abstract class 'ClassToAbstractClass'")) { instantiationOfAbstractClass() }
|
||||
|
||||
expectSuccess { StableEnum.FOO.test }
|
||||
expectSuccess { StableEnum.BAR.test }
|
||||
|
||||
expectSuccess("01234") { testStableInheritorOfClassThatUsesPrivateTopLevelClass() }
|
||||
|
||||
expectSuccess(1) { getFunctionalInterfaceToInterface(1).answer() }
|
||||
expectSuccess(2) { getFunctionalInterfaceToInterfaceAsObject(2).answer() }
|
||||
expectSuccess(3) { getFunctionalInterfaceToInterfaceAnswer(3) }
|
||||
expectSuccess(4) { getFunctionalInterfaceWith0AbstractFunctions(4).answer() }
|
||||
expectSuccess(5) { getFunctionalInterfaceWith0AbstractFunctionsAsObject(5).answer() }
|
||||
expectSuccess(6) { getFunctionalInterfaceWith0AbstractFunctionsAnswer(6) }
|
||||
expectSuccess(7) { getFunctionalInterfaceWith1AbstractFunction(7).answer() }
|
||||
expectSuccess(8) { getFunctionalInterfaceWith1AbstractFunctionAsObject(8).answer() }
|
||||
expectSuccess(9) { getFunctionalInterfaceWith1AbstractFunctionAnswer(9) }
|
||||
expectFailure(linkage("Single abstract method (SAM) conversion expression can not be evaluated: Fun interface 'FunctionalInterfaceWith2AbstractFunctions' has more than one abstract function: 'answer', 'function1'")) { getFunctionalInterfaceWith2AbstractFunctions(10).answer() }
|
||||
expectSuccess(11) { getFunctionalInterfaceWith2AbstractFunctionsAsObject(11).answer() }
|
||||
expectFailure(linkage("Single abstract method (SAM) conversion expression can not be evaluated: Fun interface 'FunctionalInterfaceWith2AbstractFunctions' has more than one abstract function: 'answer', 'function1'")) { getFunctionalInterfaceWith2AbstractFunctionsAnswer(12) }
|
||||
expectFailure(linkage("Single abstract method (SAM) conversion expression can not be evaluated: Fun interface 'FunctionalInterfaceWith3AbstractFunctions' has more than one abstract function: 'answer', 'function1', 'function2'")) { getFunctionalInterfaceWith3AbstractFunctions(13).answer() }
|
||||
expectSuccess(14) { getFunctionalInterfaceWith3AbstractFunctionsAsObject(14).answer() }
|
||||
expectFailure(linkage("Single abstract method (SAM) conversion expression can not be evaluated: Fun interface 'FunctionalInterfaceWith3AbstractFunctions' has more than one abstract function: 'answer', 'function1', 'function2'")) { getFunctionalInterfaceWith3AbstractFunctionsAnswer(15) }
|
||||
expectFailure(linkage("Single abstract method (SAM) conversion expression can not be evaluated: Fun interface 'FunctionalInterfaceWithAbstractProperty' has abstract property 'property1'")) { getFunctionalInterfaceWithAbstractProperty(16).answer() }
|
||||
expectSuccess(17) { getFunctionalInterfaceWithAbstractPropertyAsObject(17).answer() }
|
||||
expectFailure(linkage("Single abstract method (SAM) conversion expression can not be evaluated: Fun interface 'FunctionalInterfaceWithAbstractProperty' has abstract property 'property1'")) { getFunctionalInterfaceWithAbstractPropertyAnswer(18) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user