Simplify LocalDeclarationsLowering, support declaration pop up via separate lower

1. Scheme of capturing local variables not touched
 2. Lowered local functions are transposed to the nearest class (including local) or file
 3. Local classes are also transpose to the nearest class (including local) or file
This commit is contained in:
Mikhael Bogdanov
2019-06-26 07:25:21 +02:00
parent bb48e756ca
commit 9b6fef005f
14 changed files with 130 additions and 111 deletions
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.isLocal
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -45,12 +44,12 @@ import kotlin.collections.toList
class Closure(val capturedValues: List<IrValueSymbol> = emptyList())
class ClosureAnnotator(declaration: IrDeclaration) {
class ClosureAnnotator(irFile: IrFile) {
private val closureBuilders = mutableMapOf<IrDeclaration, ClosureBuilder>()
init {
// Collect all closures for classes and functions. Collect call graph
declaration.acceptChildrenVoid(ClosureCollectorVisitor())
irFile.acceptChildrenVoid(ClosureCollectorVisitor())
}
fun getFunctionClosure(declaration: IrFunction) = getClosure(declaration)
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
@@ -161,7 +162,7 @@ class InitializersLowering(
companion object {
val clinitName = Name.special("<clinit>")
fun IrStatement.copy(containingDeclaration: IrDeclarationParent) = deepCopyWithWrappedDescriptors(containingDeclaration)
fun IrExpression.copy(containingDeclaration: IrDeclarationParent) = deepCopyWithWrappedDescriptors(containingDeclaration)
fun IrStatement.copy(containingDeclaration: IrDeclarationParent) = deepCopyWithSymbols(containingDeclaration)
fun IrExpression.copy(containingDeclaration: IrDeclarationParent) = deepCopyWithSymbols(containingDeclaration)
}
}
@@ -0,0 +1,41 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
//This lower takes part of old LocalDeclarationLowering job to pop up local classes from functions
class LocalClassPopupLowering(val context: BackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
val extractedLocalClasses = arrayListOf<Pair<IrClass, IrDeclarationContainer>>()
irFile.transform(object : IrElementTransformerVoidWithContext() {
override fun visitClassNew(declaration: IrClass): IrStatement {
val newDeclaration = super.visitClassNew(declaration)
if (newDeclaration !is IrClass || !newDeclaration.isLocalNotInner()) {
return newDeclaration
}
val newContainer = allScopes.asReversed().drop(1/*skip self*/).firstOrNull {
//find first class local or not;
// to reproduce original LocalDeclarationLowering behaviour add: '&& !it.irElement.isLocal' condition
it.irElement is IrClass
}?.irElement as? IrClass ?: currentFile
extractedLocalClasses.add(newDeclaration to newContainer)
return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.irBuiltIns.unitType)
}
}, null)
for ((local, newContainer) in extractedLocalClasses) {
newContainer.addChild(local)
}
}
}
@@ -6,15 +6,20 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementVisitorVoidWithContext
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.descriptors.*
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
@@ -28,11 +33,10 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import java.util.*
@@ -59,52 +63,18 @@ class LocalDeclarationsLowering(
val localNameProvider: LocalNameProvider = LocalNameProvider.DEFAULT,
val loweredConstructorVisibility: Visibility = Visibilities.PRIVATE
) :
DeclarationContainerLoweringPass {
FileLoweringPass {
private object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE", isSynthetic = true)
private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE :
IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE")
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
if (irDeclarationContainer is IrDeclaration) {
val parents = irDeclarationContainer.parents
if (parents.any { it is IrFunction || it is IrField }) {
// Lowering of non-local declarations handles all local declarations inside.
// This declaration is local and shouldn't be considered.
return
}
}
// Continuous numbering across all declarations in the container.
lambdasCount = 0
irDeclarationContainer.transformDeclarationsFlat { memberDeclaration ->
// TODO: may be do the opposite - specify the list of IR elements which need not to be transformed
when (memberDeclaration) {
is IrFunction -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations()
is IrProperty -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations()
is IrField -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations()
is IrAnonymousInitializer -> LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations()
is IrEnumEntry -> {
// The responsibility of pulling up classes for enum entries is on EnumClassLowering.
// Moreover, EnumClassLowering needs information whether a enum entry has its own class or not.
val correspondingClass = memberDeclaration.correspondingClass
memberDeclaration.correspondingClass = null
LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations().also {
memberDeclaration.correspondingClass = correspondingClass
}
}
// TODO: visit children as well
else -> null
}
}
override fun lower(irFile: IrFile) {
LocalDeclarationsTransformer(irFile).lowerLocalDeclarations()
}
private var lambdasCount = 0
private abstract class LocalContext {
/**
* @return the expression to get the value for given declaration, or `null` if [IrGetValue] should be used.
@@ -126,12 +96,15 @@ class LocalDeclarationsLowering(
}
}
private class LocalFunctionContext(override val declaration: IrSimpleFunction) : LocalContextWithClosureAsParameters() {
private class LocalFunctionContext(
override val declaration: IrSimpleFunction,
val index: Int,
val ownerForLoweredDeclaration: IrDeclarationContainer
) :
LocalContextWithClosureAsParameters() {
lateinit var closure: Closure
override lateinit var transformedDeclaration: IrSimpleFunction
var index: Int = -1
}
private class LocalClassConstructorContext(override val declaration: IrConstructor) : LocalContextWithClosureAsParameters() {
@@ -167,7 +140,7 @@ class LocalDeclarationsLowering(
}
private inner class LocalDeclarationsTransformer(val memberDeclaration: IrDeclaration) {
private inner class LocalDeclarationsTransformer(val irFile: IrFile) {
val localFunctions: MutableMap<IrFunction, LocalFunctionContext> = LinkedHashMap()
val localClasses: MutableMap<IrClass, LocalClassContext> = LinkedHashMap()
val localClassConstructors: MutableMap<IrConstructor, LocalClassConstructorContext> = LinkedHashMap()
@@ -181,9 +154,9 @@ class LocalDeclarationsLowering(
val oldParameterToNew: MutableMap<IrValueParameter, IrValueParameter> = mutableMapOf()
val newParameterToCaptured: MutableMap<IrValueParameter, IrValueSymbol> = mutableMapOf()
fun lowerLocalDeclarations(): List<IrDeclaration> {
fun lowerLocalDeclarations() {
collectLocalDeclarations()
if (localFunctions.isEmpty() && localClasses.isEmpty()) return listOf(memberDeclaration)
if (localFunctions.isEmpty() && localClasses.isEmpty()) return
collectClosureForLocalDeclarations()
@@ -191,34 +164,24 @@ class LocalDeclarationsLowering(
rewriteDeclarations()
return collectRewrittenDeclarations().apply {
forEach { newDeclaration ->
newDeclaration.patchDeclarationParents(memberDeclaration.parent)
insertLoweredDeclarationForLocalFunctions()
}
private fun insertLoweredDeclarationForLocalFunctions() {
localFunctions.values.forEach {
it.transformedDeclaration.apply {
val original = it.declaration
this.body = original.body
original.valueParameters.filter { v -> v.defaultValue != null }.forEach { argument ->
val body = argument.defaultValue!!
oldParameterToNew[argument]!!.defaultValue = body
}
}
it.ownerForLoweredDeclaration.addChild(it.transformedDeclaration)
}
}
private fun collectRewrittenDeclarations(): ArrayList<IrDeclaration> =
ArrayList<IrDeclaration>(localFunctions.size + localClasses.size + 1).apply {
localFunctions.values.mapTo(this) {
val original = it.declaration
it.transformedDeclaration.apply {
this.body = original.body
original.valueParameters.filter { v -> v.defaultValue != null }.forEach { argument ->
val body = argument.defaultValue
oldParameterToNew[argument]!!.defaultValue = body
}
}
}
localClasses.values.mapTo(this) {
it.declaration.parent = memberDeclaration.parent
it.declaration
}
add(memberDeclaration)
}
private inner class FunctionBodiesRewriter(val localContext: LocalContext?) : IrElementTransformerVoid() {
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) =
@@ -226,8 +189,7 @@ class LocalDeclarationsLowering(
declaration.delegate.transform(this, null)
override fun visitClass(declaration: IrClass) = if (declaration in localClasses) {
// Replace local class definition with an empty composite.
IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.irBuiltIns.unitType)
localClasses[declaration]!!.declaration
} else {
super.visitClass(declaration)
}
@@ -398,7 +360,7 @@ class LocalDeclarationsLowering(
}
}
private fun rewriteFunctionBody(irDeclaration: IrDeclaration, localContext: LocalContext?) {
private fun rewriteFunctionBody(irDeclaration: IrElement, localContext: LocalContext?) {
irDeclaration.transformChildrenVoid(FunctionBodiesRewriter(localContext))
}
@@ -451,7 +413,7 @@ class LocalDeclarationsLowering(
rewriteClassMembers(it.declaration, it)
}
rewriteFunctionBody(memberDeclaration, null)
rewriteFunctionBody(irFile, null)
}
private fun createNewCall(oldCall: IrCall, newCallee: IrFunction) =
@@ -515,7 +477,7 @@ class LocalDeclarationsLowering(
private fun createLiftedDeclaration(localFunctionContext: LocalFunctionContext) {
val oldDeclaration = localFunctionContext.declaration
val memberOwner = memberDeclaration.parent
val memberOwner = localFunctionContext.ownerForLoweredDeclaration
val newDescriptor = WrappedSimpleFunctionDescriptor(oldDeclaration.descriptor)
val newSymbol = IrSimpleFunctionSymbolImpl(newDescriptor)
val newName = generateNameForLiftedDeclaration(oldDeclaration, memberOwner)
@@ -629,12 +591,11 @@ class LocalDeclarationsLowering(
newDeclaration.parent = localClassContext.declaration
newDeclaration.copyTypeParametersFrom(oldDeclaration)
// TODO: should dispatch receiver be copied?
newDeclaration.dispatchReceiverParameter = oldDeclaration.dispatchReceiverParameter?.copyTo(newDeclaration)?.also {
newParameterToOld.putAbsentOrSame(it, oldDeclaration.dispatchReceiverParameter!!)
oldDeclaration.dispatchReceiverParameter?.run {
throw AssertionError("Local class constructor can't have dispatch receiver: ${ir2string(oldDeclaration)}")
}
newDeclaration.extensionReceiverParameter = oldDeclaration.extensionReceiverParameter?.run {
throw AssertionError("constructors can't have extension receiver")
oldDeclaration.extensionReceiverParameter?.run {
throw AssertionError("Local class constructor can't have extension receiver: ${ir2string(oldDeclaration)}")
}
newDeclaration.valueParameters += createTransformedValueParameters(capturedValues, oldDeclaration, newDeclaration)
@@ -707,7 +668,8 @@ class LocalDeclarationsLowering(
private fun collectClosureForLocalDeclarations() {
val annotator = ClosureAnnotator(memberDeclaration)
//TODO: maybe use for granular declarations
val annotator = ClosureAnnotator(irFile)
localFunctions.forEach { (declaration, context) ->
context.closure = annotator.getFunctionClosure(declaration)
@@ -719,40 +681,49 @@ class LocalDeclarationsLowering(
}
private fun collectLocalDeclarations() {
memberDeclaration.acceptChildrenVoid(object : IrElementVisitorVoid {
class ScopeWithCounter(scope: Scope, irElement: IrElement) : ScopeWithIr(scope, irElement) {
// Continuous numbering across all declarations in the container.
var counter: Int = 0
}
irFile.acceptVoid(object : IrElementVisitorVoidWithContext() {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun createScope(declaration: IrSymbolOwner): ScopeWithIr {
return ScopeWithCounter(Scope(declaration.symbol), declaration)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
super.visitSimpleFunction(declaration)
if (declaration.visibility == Visibilities.LOCAL) {
val localFunctionContext = LocalFunctionContext(declaration)
localFunctions[declaration] = localFunctionContext
if (declaration.name.isSpecial) {
localFunctionContext.index = lambdasCount++
}
val scopeWithIr =
(currentClass ?: currentFile /*file is required for K/N cause file declarations are not split by classes*/
?: error("No scope for ${declaration.dump()}"))
localFunctions[declaration] =
LocalFunctionContext(
declaration,
if (declaration.name.isSpecial) (scopeWithIr as ScopeWithCounter).counter++ else -1,
scopeWithIr.irElement as IrDeclarationContainer
)
}
}
override fun visitConstructor(declaration: IrConstructor) {
super.visitConstructor(declaration)
assert(declaration.visibility != Visibilities.LOCAL)
if ((declaration.parent as IrClass).isInner) return
if (!(declaration.parent as IrClass).isLocalNotInner()) return
localClassConstructors[declaration] = LocalClassConstructorContext(declaration)
}
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
override fun visitClassNew(declaration: IrClass) {
super.visitClassNew(declaration)
if (declaration.isInner) return
if (!declaration.isLocalNotInner()) return
val localClassContext = LocalClassContext(declaration)
localClasses[declaration] = localClassContext
@@ -762,3 +733,6 @@ class LocalDeclarationsLowering(
}
}
// Local inner classes capture anything through outer
internal fun IrClass.isLocalNotInner(): Boolean = visibility == Visibilities.LOCAL && !isInner
@@ -179,6 +179,13 @@ private val localDeclarationsLoweringPhase = makeJsModulePhase(
prerequisite = setOf(sharedVariablesLoweringPhase, localDelegatedPropertiesLoweringPhase)
)
private val localClassExtractionPhase = makeJsModulePhase(
::LocalClassPopupLowering,
name = "LocalClassExtractionPhase",
description = "Move local declarations into nearest declaration container",
prerequisite = setOf(localDeclarationsLoweringPhase)
)
private val innerClassesLoweringPhase = makeJsModulePhase(
::InnerClassesLowering,
name = "InnerClassesLowering",
@@ -372,6 +379,7 @@ val jsPhases = namedIrModulePhase(
sharedVariablesLoweringPhase then
localDelegatedPropertiesLoweringPhase then
localDeclarationsLoweringPhase then
localClassExtractionPhase then
innerClassesLoweringPhase then
innerClassConstructorCallsLoweringPhase then
propertiesLoweringPhase then
@@ -97,7 +97,7 @@ class JvmDeclarationFactory(
oldConstructor.isInline, oldConstructor.isExternal, oldConstructor.isPrimary
).apply {
newDescriptor.bind(this)
annotations.addAll(oldConstructor.annotations.map { it.deepCopyWithWrappedDescriptors(this) })
annotations.addAll(oldConstructor.annotations.map { it.deepCopyWithSymbols(this) })
parent = oldConstructor.parent
returnType = oldConstructor.returnType
copyTypeParametersFrom(oldConstructor)
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_OVERLOADS_FQ_NAME
@@ -150,7 +151,7 @@ private class JvmOverloadsAnnotationLowering(val context: JvmBackendContext) : C
}
res.parent = oldFunction.parent
res.annotations.addAll(oldFunction.annotations.map { it.deepCopyWithWrappedDescriptors(res) })
res.annotations.addAll(oldFunction.annotations.map { it.deepCopyWithSymbols(res) })
res.copyTypeParametersFrom(oldFunction)
res.dispatchReceiverParameter = oldFunction.dispatchReceiverParameter?.copyTo(res)
res.extensionReceiverParameter = oldFunction.extensionReceiverParameter?.copyTo(res)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
class A {
var value: String = "fail1"
private set
@@ -1,5 +1,4 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.test.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// WITH_REFLECT
open class C
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// WITH_REFLECT
interface C {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT