[PL] Handle illegal fun interface SAM conversions

^KT-53967
This commit is contained in:
Dmitriy Dolovov
2023-05-17 17:38:21 +02:00
committed by Space Team
parent 7f4d9ddde5
commit 1536bca1bb
7 changed files with 225 additions and 20 deletions
@@ -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")
@@ -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)