JVM_IR: propagate reified type parameter usages from inline lambdas

...to whichever class they are inlined into, not the class they are
declared in (which is not the same if the lambda is crossinline).

 #KT-46584 Fixed
This commit is contained in:
pyos
2021-05-11 16:19:20 +02:00
committed by TeamCityServer
parent e079fb665e
commit 3fc2cc410c
19 changed files with 175 additions and 45 deletions
@@ -58,6 +58,8 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
lateinit var node: SMAPAndMethodNode
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
abstract fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>)
open val hasDispatchReceiver = true
@@ -198,7 +200,7 @@ abstract class DefaultLambda(
if (needReification) {
//nested classes could also require reification
reifiedTypeInliner.reifyInstructions(node.node)
reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(node.node))
}
}
@@ -225,7 +227,7 @@ internal fun Type.boxReceiverForBoundReference(kotlinType: KotlinType, typeMappe
abstract class ExpressionLambda(isCrossInline: Boolean) : LambdaInfo(isCrossInline) {
override fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>) {
node = sourceCompiler.generateLambdaBody(this)
node = sourceCompiler.generateLambdaBody(this, reifiedTypeParametersUsages)
node.node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
}
@@ -264,7 +264,7 @@ class MethodInliner(
visitInsn(Opcodes.NOP)
}
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info, sourceMapper.parent)
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info.node.node, sourceMapper.parent)
addInlineMarker(this, true)
val lambdaParameters = info.addAllParameters(nodeRemapper)
@@ -289,6 +289,7 @@ class MethodInliner(
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
result.mergeWithNotChangeInfo(lambdaResult)
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
result.reifiedTypeParametersUsages.mergeAll(info.reifiedTypeParametersUsages)
StackValue
.onStack(info.invokeMethod.returnType, info.invokeMethodReturnType)
@@ -48,7 +48,7 @@ interface SourceCompilerForInline {
val lazySourceMapper: SourceMapper
fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode
fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode
fun doCreateMethodNodeFromSource(
callableDescriptor: FunctionDescriptor,
@@ -130,7 +130,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
override val lazySourceMapper
get() = codegen.parentCodegen.orCreateSourceMapper
override fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode {
override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
lambdaInfo as? PsiExpressionLambda ?: error("TODO")
val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor
val jvmMethodSignature = state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
@@ -579,8 +579,8 @@ class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
const val LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER = 1
}
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo, smap: SourceMapper) {
val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
fun onInlineLambdaStart(mv: MethodVisitor, lambda: MethodNode, smap: SourceMapper) {
val firstLine = lambda.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
if (callLineNumber >= 0 && firstLine == callLineNumber) {
// We want the debugger to be able to break both on the inline call itself, plus on each
// invocation of the inline lambda passed to it. For that to happen there needs to be at least
@@ -3714,6 +3714,18 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -143,6 +143,7 @@ class ExpressionCodegen(
val classCodegen: ClassCodegen,
val inlinedInto: ExpressionCodegen?,
val smap: SourceMapper,
val reifiedTypeParametersUsages: ReifiedTypeParametersUsages,
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
var finallyDepth = 0
@@ -1426,32 +1427,13 @@ class ExpressionCodegen(
override fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) {
require(typeParameter is IrTypeParameterSymbol)
// 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())
if (irFunction != typeParameter.owner.parent) {
reifiedTypeParametersUsages.addUsedReifiedParameter(typeParameter.owner.name.asString())
}
}
override fun propagateChildReifiedTypeParametersUsages(reifiedTypeParametersUsages: ReifiedTypeParametersUsages) {
classCodegen.reifiedTypeParametersUsages.propagateChildUsagesWithinContext(reifiedTypeParametersUsages) {
this.reifiedTypeParametersUsages.propagateChildUsagesWithinContext(reifiedTypeParametersUsages) {
irFunction.typeParameters.filter { it.isReified }.map { it.name.asString() }.toSet()
}
}
@@ -13,10 +13,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.inline.MethodBodyVisitor
import org.jetbrains.kotlin.codegen.inline.SMAP
import org.jetbrains.kotlin.codegen.inline.SMAPAndMethodNode
import org.jetbrains.kotlin.codegen.inline.wrapWithMaxLocalCalc
import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.mangleNameIfNeeded
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.visitAnnotableParameterCount
@@ -40,21 +37,20 @@ import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class FunctionCodegen(
private val irFunction: IrFunction,
private val classCodegen: ClassCodegen,
private val inlinedInto: ExpressionCodegen? = null
) {
class FunctionCodegen(private val irFunction: IrFunction, private val classCodegen: ClassCodegen) {
private val context = classCodegen.context
fun generate(): SMAPAndMethodNode =
fun generate(
inlinedInto: ExpressionCodegen? = null,
reifiedTypeParameters: ReifiedTypeParametersUsages = classCodegen.reifiedTypeParametersUsages
): SMAPAndMethodNode =
try {
doGenerate()
doGenerate(inlinedInto, reifiedTypeParameters)
} catch (e: Throwable) {
throw RuntimeException("Exception while generating code for:\n${irFunction.dump()}", e)
}
private fun doGenerate(): SMAPAndMethodNode {
private fun doGenerate(inlinedInto: ExpressionCodegen?, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
val signature = context.methodSignatureMapper.mapSignatureWithGeneric(irFunction)
val flags = irFunction.calculateMethodFlags()
val isSynthetic = flags.and(Opcodes.ACC_SYNTHETIC) != 0
@@ -121,7 +117,9 @@ class FunctionCodegen(
context.state.globalInlineContext.enterDeclaration(irFunction.suspendFunctionOriginal().toIrBasedDescriptor())
try {
val adapter = InstructionAdapter(methodVisitor)
ExpressionCodegen(irFunction, signature, frameMap, adapter, classCodegen, inlinedInto, sourceMapper).generate()
ExpressionCodegen(
irFunction, signature, frameMap, adapter, classCodegen, inlinedInto, sourceMapper, reifiedTypeParameters
).generate()
} finally {
context.state.globalInlineContext.exitDeclaration()
}
@@ -95,8 +95,15 @@ class IrSourceCompilerForInline(
override val lazySourceMapper: SourceMapper
get() = codegen.smap
override fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode =
FunctionCodegen((lambdaInfo as IrExpressionLambdaImpl).function, codegen.classCodegen, codegen).generate()
override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
require(lambdaInfo is IrExpressionLambdaImpl)
for (typeParameter in lambdaInfo.function.typeParameters) {
if (typeParameter.isReified) {
reifiedTypeParameters.addUsedReifiedParameter(typeParameter.name.asString())
}
}
return FunctionCodegen(lambdaInfo.function, codegen.classCodegen).generate(codegen, reifiedTypeParameters)
}
override fun doCreateMethodNodeFromSource(
callableDescriptor: FunctionDescriptor,
@@ -116,7 +123,7 @@ class IrSourceCompilerForInline(
override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) =
ExpressionCodegen(
codegen.irFunction, codegen.signature, codegen.frameMap, InstructionAdapter(finallyNode), codegen.classCodegen,
codegen.inlinedInto, codegen.smap
codegen.inlinedInto, codegen.smap, codegen.reifiedTypeParametersUsages
).also {
it.finallyDepth = curFinallyDepth
}
+13
View File
@@ -0,0 +1,13 @@
// FILE: 1.kt
package test
inline fun foo(crossinline x: () -> String) = { x() }()
inline fun <reified T> bar() = foo { { T::class.simpleName!! }() }
// FILE: 2.kt
import test.*
class OK
fun box() = bar<OK>()
@@ -0,0 +1,13 @@
// FILE: 1.kt
package test
inline fun foo(x: () -> String) = x()
inline fun <reified T> bar() = { foo { { T::class.simpleName!! }() } }()
// FILE: 2.kt
import test.*
class OK
fun box() = bar<OK>()
@@ -3714,6 +3714,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -3714,6 +3714,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -3714,6 +3714,18 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -3714,6 +3714,18 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -3714,6 +3714,18 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -3714,6 +3714,18 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
runTest("compiler/testData/codegen/boxInline/reified/kt44770_2.kt");
}
@Test
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@Test
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@Test
@TestMetadata("kt6988.kt")
public void testKt6988() throws Exception {
@@ -3025,6 +3025,16 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline
runTest("compiler/testData/codegen/boxInline/reified/kt35511_try_values.kt");
}
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@TestMetadata("kt7017.kt")
public void testKt7017() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt7017.kt");
@@ -3025,6 +3025,16 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
runTest("compiler/testData/codegen/boxInline/reified/kt35511_try_values.kt");
}
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@TestMetadata("kt7017.kt")
public void testKt7017() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt7017.kt");
@@ -3025,6 +3025,16 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
runTest("compiler/testData/codegen/boxInline/reified/kt35511_try_values.kt");
}
@TestMetadata("kt46584.kt")
public void testKt46584() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584.kt");
}
@TestMetadata("kt46584_2.kt")
public void testKt46584_2() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt46584_2.kt");
}
@TestMetadata("kt7017.kt")
public void testKt7017() throws Exception {
runTest("compiler/testData/codegen/boxInline/reified/kt7017.kt");