JVM_IR: "fix" reified type parameter scope check
where "fix" means "work around a bug that is much harder to fix". When lambdas are extracted into the nearest class, captured type parameters are not transformed into type parameters of the new method; because of this, to check whether a reified type parameter is captured by a method we need to check the outermost named function, not the current function.
This commit is contained in:
+24
-5
@@ -100,7 +100,7 @@ class ExpressionCodegen(
|
||||
override val frameMap: IrFrameMap,
|
||||
val mv: InstructionAdapter,
|
||||
val classCodegen: ClassCodegen,
|
||||
val isInlineLambda: Boolean = false
|
||||
val inlinedInto: ExpressionCodegen?
|
||||
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
||||
|
||||
var finallyDepth = 0
|
||||
@@ -202,7 +202,7 @@ class ExpressionCodegen(
|
||||
if (state.isParamAssertionsDisabled)
|
||||
return
|
||||
|
||||
val notCallableFromJava = isInlineLambda ||
|
||||
val notCallableFromJava = inlinedInto != null ||
|
||||
Visibilities.isPrivate(irFunction.visibility) ||
|
||||
irFunction.origin.isSynthetic ||
|
||||
// TODO: refine this condition to not generate nullability assertions on parameters
|
||||
@@ -314,7 +314,7 @@ class ExpressionCodegen(
|
||||
visitStatementContainer(expression, data).coerce(expression.type)
|
||||
|
||||
override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
return visitFunctionAccess(expression.createSuspendFunctionCallViewIfNeeded(context, irFunction, isInlineLambda), data)
|
||||
return visitFunctionAccess(expression.createSuspendFunctionCallViewIfNeeded(context, irFunction, inlinedInto != null), data)
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
@@ -1012,7 +1012,26 @@ class ExpressionCodegen(
|
||||
|
||||
override fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) {
|
||||
require(typeParameter is IrTypeParameterSymbol)
|
||||
if (typeParameter.owner.parent != irFunction) {
|
||||
// This is a hack to work around the problem in LocalDeclarationsLowering. Specifically, suppose an inline
|
||||
// lambda uses a reified type parameter declared by a function:
|
||||
//
|
||||
// object {
|
||||
// inline fun <reified T : Any> f() = run { T::class.java.getName() }
|
||||
// }
|
||||
//
|
||||
// LocalDeclarationsLowering would extract that lambda into a method of the enclosing type, but will not create
|
||||
// a reified type parameter in it (in fact, the lambda method isn't even marked as inline):
|
||||
//
|
||||
// object {
|
||||
// /* static */ private fun `f$lambda-0`() = T::class.java.getName()
|
||||
// inline fun <reified T : Any> f() = run(::`f$lambda-0`)
|
||||
// }
|
||||
//
|
||||
// The parent of the type parameter then is not `irFunction` (i.e. the lambda itself), but the function
|
||||
// it is inlined into.
|
||||
//
|
||||
// TODO make LocalDeclarationsLowering handle captured type parameters and only compare with `irFunction`.
|
||||
if (generateSequence(this) { it.inlinedInto }.none { it.irFunction == typeParameter.owner.parent }) {
|
||||
classCodegen.reifiedTypeParametersUsages.addUsedReifiedParameter(typeParameter.owner.name.asString())
|
||||
}
|
||||
}
|
||||
@@ -1039,7 +1058,7 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
fun isFinallyMarkerRequired(): Boolean {
|
||||
return irFunction.isInline || isInlineLambda
|
||||
return irFunction.isInline || inlinedInto != null
|
||||
}
|
||||
|
||||
private val IrType.isReifiedTypeParameter: Boolean
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
open class FunctionCodegen(
|
||||
private val irFunction: IrFunction,
|
||||
private val classCodegen: ClassCodegen,
|
||||
private val isInlineLambda: Boolean = false
|
||||
private val inlinedInto: ExpressionCodegen? = null
|
||||
) {
|
||||
val context = classCodegen.context
|
||||
val state = classCodegen.state
|
||||
@@ -95,7 +95,7 @@ open class FunctionCodegen(
|
||||
)
|
||||
else -> methodVisitor
|
||||
}
|
||||
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, isInlineLambda).generate()
|
||||
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate()
|
||||
methodVisitor.visitMaxs(-1, -1)
|
||||
continuationClassBuilder?.done()
|
||||
}
|
||||
|
||||
+2
-2
@@ -74,7 +74,7 @@ class IrSourceCompilerForInline(
|
||||
|
||||
private fun makeInlineNode(function: IrFunction, classCodegen: ClassCodegen, isLambda: Boolean): SMAPAndMethodNode {
|
||||
var node: MethodNode? = null
|
||||
val functionCodegen = object : FunctionCodegen(function, classCodegen, isLambda) {
|
||||
val functionCodegen = object : FunctionCodegen(function, classCodegen, codegen.takeIf { isLambda }) {
|
||||
override fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor {
|
||||
val asmMethod = signature.asmMethod
|
||||
node = MethodNode(Opcodes.API_VERSION, flags, asmMethod.name, asmMethod.descriptor, signature.genericsSignature, null)
|
||||
@@ -134,7 +134,7 @@ class IrSourceCompilerForInline(
|
||||
override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) =
|
||||
ExpressionCodegen(
|
||||
codegen.irFunction, codegen.signature, codegen.frameMap, InstructionAdapter(finallyNode), codegen.classCodegen,
|
||||
codegen.isInlineLambda
|
||||
codegen.inlinedInto
|
||||
).also {
|
||||
it.finallyDepth = curFinallyDepth
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_RUNTIME
|
||||
|
||||
Reference in New Issue
Block a user