JVM_IR: partially fix inline methods using captured crossinline lambdas

The fields containing crossinline lambdas should be package-private to
avoid generating synthetic accessors, which break object regeneration.

Note that the inline methods cannot actually be called, as call sites
will attempt to read the captured lambda from a field through a *copy*
of the local containing the object, so these reads will not be inlined,
causing an exception at runtime:

    inline fun f(crossinline g: () -> Unit) = object : I {
        inline fun h() = g()
        // effectively `val tmp = this; return tmp.$g()`:
        override fun run() = h()
    }

    f {}.run() // NoSuchFieldError: $g

This particular example can be fixed by reusing locals for receiver
parameters in IrInlineCodegen, but explicitly assigning `this` to
another variable and calling an inline method on it will break it again.
(This is only applicable to the JVM_IR backend, as the non-IR one fails
to generate `f` at all for some other reason.)
This commit is contained in:
pyos
2020-02-07 11:17:32 +01:00
committed by max-kammerer
parent dd27b3d4f1
commit 2c06503311
14 changed files with 69 additions and 10 deletions
@@ -66,6 +66,9 @@ interface VisibilityPolicy {
fun forConstructor(declaration: IrConstructor, inInlineFunctionScope: Boolean): Visibility =
Visibilities.PRIVATE
fun forCapturedField(value: IrValueSymbol): Visibility =
Visibilities.PRIVATE
companion object {
val DEFAULT = object : VisibilityPolicy {}
}
@@ -778,7 +781,7 @@ class LocalDeclarationsLowering(
classDeclaration.startOffset,
classDeclaration.endOffset,
suggestNameForCapturedValue(owner, generatedNames),
Visibilities.PRIVATE,
visibilityPolicy.forCapturedField(capturedValue),
classDeclaration,
owner.type,
owner is IrValueParameter && owner.isCrossinline
@@ -11,12 +11,13 @@ import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.lower.optimizations.foldConstantLoweringPhase
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub
import org.jetbrains.kotlin.backend.jvm.lower.*
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor
import org.jetbrains.kotlin.ir.util.isAnonymousObject
import org.jetbrains.kotlin.ir.util.parentAsClass
@@ -129,6 +130,12 @@ internal val localDeclarationsPhase = makeIrFilePhase<CommonBackendContext>(
else
declaration.visibility
override fun forCapturedField(value: IrValueSymbol): Visibility =
if (value is IrValueParameterSymbol && value.owner.isCrossinline)
JavaVisibilities.PACKAGE_VISIBILITY // avoid requiring a synthetic accessor for it
else
Visibilities.PRIVATE
private fun scopedVisibility(inInlineFunctionScope: Boolean): Visibility =
if (inInlineFunctionScope) Visibilities.PUBLIC else JavaVisibilities.PACKAGE_VISIBILITY
}