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,
|
override val frameMap: IrFrameMap,
|
||||||
val mv: InstructionAdapter,
|
val mv: InstructionAdapter,
|
||||||
val classCodegen: ClassCodegen,
|
val classCodegen: ClassCodegen,
|
||||||
val isInlineLambda: Boolean = false
|
val inlinedInto: ExpressionCodegen?
|
||||||
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
||||||
|
|
||||||
var finallyDepth = 0
|
var finallyDepth = 0
|
||||||
@@ -202,7 +202,7 @@ class ExpressionCodegen(
|
|||||||
if (state.isParamAssertionsDisabled)
|
if (state.isParamAssertionsDisabled)
|
||||||
return
|
return
|
||||||
|
|
||||||
val notCallableFromJava = isInlineLambda ||
|
val notCallableFromJava = inlinedInto != null ||
|
||||||
Visibilities.isPrivate(irFunction.visibility) ||
|
Visibilities.isPrivate(irFunction.visibility) ||
|
||||||
irFunction.origin.isSynthetic ||
|
irFunction.origin.isSynthetic ||
|
||||||
// TODO: refine this condition to not generate nullability assertions on parameters
|
// TODO: refine this condition to not generate nullability assertions on parameters
|
||||||
@@ -314,7 +314,7 @@ class ExpressionCodegen(
|
|||||||
visitStatementContainer(expression, data).coerce(expression.type)
|
visitStatementContainer(expression, data).coerce(expression.type)
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
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 {
|
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||||
@@ -1012,7 +1012,26 @@ class ExpressionCodegen(
|
|||||||
|
|
||||||
override fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) {
|
override fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) {
|
||||||
require(typeParameter is IrTypeParameterSymbol)
|
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())
|
classCodegen.reifiedTypeParametersUsages.addUsedReifiedParameter(typeParameter.owner.name.asString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1058,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isFinallyMarkerRequired(): Boolean {
|
fun isFinallyMarkerRequired(): Boolean {
|
||||||
return irFunction.isInline || isInlineLambda
|
return irFunction.isInline || inlinedInto != null
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrType.isReifiedTypeParameter: Boolean
|
private val IrType.isReifiedTypeParameter: Boolean
|
||||||
|
|||||||
+2
-2
@@ -30,7 +30,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
|||||||
open class FunctionCodegen(
|
open class FunctionCodegen(
|
||||||
private val irFunction: IrFunction,
|
private val irFunction: IrFunction,
|
||||||
private val classCodegen: ClassCodegen,
|
private val classCodegen: ClassCodegen,
|
||||||
private val isInlineLambda: Boolean = false
|
private val inlinedInto: ExpressionCodegen? = null
|
||||||
) {
|
) {
|
||||||
val context = classCodegen.context
|
val context = classCodegen.context
|
||||||
val state = classCodegen.state
|
val state = classCodegen.state
|
||||||
@@ -95,7 +95,7 @@ open class FunctionCodegen(
|
|||||||
)
|
)
|
||||||
else -> methodVisitor
|
else -> methodVisitor
|
||||||
}
|
}
|
||||||
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, isInlineLambda).generate()
|
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate()
|
||||||
methodVisitor.visitMaxs(-1, -1)
|
methodVisitor.visitMaxs(-1, -1)
|
||||||
continuationClassBuilder?.done()
|
continuationClassBuilder?.done()
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -74,7 +74,7 @@ class IrSourceCompilerForInline(
|
|||||||
|
|
||||||
private fun makeInlineNode(function: IrFunction, classCodegen: ClassCodegen, isLambda: Boolean): SMAPAndMethodNode {
|
private fun makeInlineNode(function: IrFunction, classCodegen: ClassCodegen, isLambda: Boolean): SMAPAndMethodNode {
|
||||||
var node: MethodNode? = null
|
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 {
|
override fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor {
|
||||||
val asmMethod = signature.asmMethod
|
val asmMethod = signature.asmMethod
|
||||||
node = MethodNode(Opcodes.API_VERSION, flags, asmMethod.name, asmMethod.descriptor, signature.genericsSignature, null)
|
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) =
|
override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) =
|
||||||
ExpressionCodegen(
|
ExpressionCodegen(
|
||||||
codegen.irFunction, codegen.signature, codegen.frameMap, InstructionAdapter(finallyNode), codegen.classCodegen,
|
codegen.irFunction, codegen.signature, codegen.frameMap, InstructionAdapter(finallyNode), codegen.classCodegen,
|
||||||
codegen.isInlineLambda
|
codegen.inlinedInto
|
||||||
).also {
|
).also {
|
||||||
it.finallyDepth = curFinallyDepth
|
it.finallyDepth = curFinallyDepth
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
Reference in New Issue
Block a user