[JS IR BE] fix default arguments lowering

This commit is contained in:
Anton Bannykh
2018-10-16 17:45:45 +03:00
parent e9c932260c
commit 3a105debb3
38 changed files with 124 additions and 129 deletions
@@ -171,6 +171,7 @@ fun IrValueParameter.copyTo(
).also {
descriptor.bind(it)
it.parent = irFunction
it.defaultValue = defaultValue
}
}
@@ -50,7 +50,6 @@ open class DefaultArgumentStubGenerator constructor(val context: CommonBackendCo
else
null
}
}
private val symbols = context.ir.symbols
@@ -64,91 +63,91 @@ open class DefaultArgumentStubGenerator constructor(val context: CommonBackendCo
log { "detected ${irFunction.name.asString()} has got #${bodies.size} default expressions" }
if (bodies.isNotEmpty()) {
val newIrFunction = irFunction.generateDefaultsFunction(context)
newIrFunction.parent = irFunction.parent
log { "$irFunction -> $newIrFunction" }
val builder = context.createIrBuilder(newIrFunction.symbol)
newIrFunction.body = builder.irBlockBody(newIrFunction) {
val params = mutableListOf<IrVariable>()
val variables = mutableMapOf<IrValueDeclaration, IrValueDeclaration>()
irFunction.dispatchReceiverParameter?.let {
variables[it] = newIrFunction.dispatchReceiverParameter!!
}
irFunction.extensionReceiverParameter?.let {
variables[it] = newIrFunction.extensionReceiverParameter!!
}
for (valueParameter in irFunction.valueParameters) {
val parameter = newIrFunction.valueParameters[valueParameter.index]
val argument = if (valueParameter.defaultValue != null) {
val kIntAnd = symbols.intAnd.owner
val condition = irNotEquals(irCall(kIntAnd).apply {
dispatchReceiver = irGet(maskParameter(newIrFunction, valueParameter.index / 32))
putValueArgument(0, irInt(1 shl (valueParameter.index % 32)))
}, irInt(0))
val expressionBody = valueParameter.defaultValue!!
expressionBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
log { "GetValue: ${expression.symbol.owner}" }
val valueSymbol = variables[expression.symbol.owner] ?: return expression
return irGet(valueSymbol)
}
})
irIfThenElse(
type = parameter.type,
condition = condition,
thenPart = expressionBody.expression,
elsePart = irGet(parameter)
)
} else {
irGet(parameter)
}
val temporaryVariable = irTemporary(argument, nameHint = parameter.name.asString())
params.add(temporaryVariable)
variables[valueParameter] = temporaryVariable
}
if (irFunction is IrConstructor) {
+IrDelegatingConstructorCallImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
type = context.irBuiltIns.unitType,
symbol = irFunction.symbol, descriptor = irFunction.symbol.descriptor,
typeArgumentsCount = irFunction.typeParameters.size
).apply {
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
}
} else {
+irReturn(irCall(irFunction).apply {
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
extensionReceiver = newIrFunction.extensionReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
})
}
}
// Remove default argument initializers.
irFunction.valueParameters.forEach {
it.defaultValue = IrExpressionBodyImpl(IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, "Default Stub"))
}
if (bodies.isEmpty()) {
// Fake override
val newIrFunction = irFunction.generateDefaultsFunction(context, IrDeclarationOrigin.FAKE_OVERRIDE)
return listOf(irFunction, newIrFunction)
}
return listOf(irFunction)
val newIrFunction = irFunction.generateDefaultsFunction(context, DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER)
log { "$irFunction -> $newIrFunction" }
val builder = context.createIrBuilder(newIrFunction.symbol)
newIrFunction.body = builder.irBlockBody(newIrFunction) {
val params = mutableListOf<IrVariable>()
val variables = mutableMapOf<IrValueDeclaration, IrValueDeclaration>()
irFunction.dispatchReceiverParameter?.let {
variables[it] = newIrFunction.dispatchReceiverParameter!!
}
irFunction.extensionReceiverParameter?.let {
variables[it] = newIrFunction.extensionReceiverParameter!!
}
for (valueParameter in irFunction.valueParameters) {
val parameter = newIrFunction.valueParameters[valueParameter.index]
val argument = if (valueParameter.defaultValue != null) {
val kIntAnd = symbols.intAnd.owner
val condition = irNotEquals(irCall(kIntAnd).apply {
dispatchReceiver = irGet(maskParameter(newIrFunction, valueParameter.index / 32))
putValueArgument(0, irInt(1 shl (valueParameter.index % 32)))
}, irInt(0))
val expressionBody = valueParameter.defaultValue!!
expressionBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
log { "GetValue: ${expression.symbol.owner}" }
val valueSymbol = variables[expression.symbol.owner] ?: return expression
return irGet(valueSymbol)
}
})
irIfThenElse(
type = parameter.type,
condition = condition,
thenPart = expressionBody.expression,
elsePart = irGet(parameter)
)
} else {
irGet(parameter)
}
val temporaryVariable = irTemporary(argument, nameHint = parameter.name.asString())
params.add(temporaryVariable)
variables[valueParameter] = temporaryVariable
}
if (irFunction is IrConstructor) {
+IrDelegatingConstructorCallImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
type = context.irBuiltIns.unitType,
symbol = irFunction.symbol, descriptor = irFunction.symbol.descriptor,
typeArgumentsCount = irFunction.typeParameters.size
).apply {
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
}
} else {
+irReturn(irCall(irFunction).apply {
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
extensionReceiver = newIrFunction.extensionReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
})
}
}
// Remove default argument initializers.
irFunction.valueParameters.forEach {
it.defaultValue = IrExpressionBodyImpl(IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, "Default Stub"))
}
return listOf(irFunction, newIrFunction)
}
@@ -164,6 +163,23 @@ private fun maskParameter(function: IrFunction, number: Int) =
private fun markerParameterDeclaration(function: IrFunction) =
function.valueParameters.single { it.name == kConstructorMarkerName }
// Populates `overriddenSymbols` for the newly created functions
class DefaultParameterFakeOverrideCleanup(val context: CommonBackendContext): DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
for (func in irDeclarationContainer.declarations) {
if (func !is IrSimpleFunction) continue
val defFunc = context.ir.defaultParameterDeclarationsCache[func] as? IrSimpleFunction ?: continue
for (o in func.overriddenSymbols) {
(context.ir.defaultParameterDeclarationsCache[o.owner] as? IrSimpleFunction)?.let {
defFunc.overriddenSymbols.add(it.symbol)
}
}
}
}
}
open class DefaultParameterInjector constructor(
val context: CommonBackendContext,
private val skipInline: Boolean = true
@@ -264,9 +280,7 @@ open class DefaultParameterInjector constructor(
val declaration = expression.symbol.owner
val keyFunction = declaration.findSuperMethodWithDefaultArguments()!!
val realFunction = keyFunction.generateDefaultsFunction(context)
realFunction.parent = keyFunction.parent
val realFunction = keyFunction.generateDefaultsFunction(context, DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER)
log { "$declaration -> $realFunction" }
val maskValues = Array((declaration.valueParameters.size + 31) / 32) { 0 }
@@ -343,6 +357,7 @@ class DefaultParameterCleaner constructor(val context: CommonBackendContext) : F
}
}
// TODO this implementation is exponential
private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean): Boolean {
if (isInline && skipInlineMethods) return false
if (valueParameters.any { it.defaultValue != null }) return true
@@ -352,8 +367,8 @@ private fun IrFunction.needsDefaultArgumentsLowering(skipInlineMethods: Boolean)
return overriddenSymbols.any { it.owner.needsDefaultArgumentsLowering(skipInlineMethods) }
}
private fun IrFunction.generateDefaultsFunctionImpl(context: CommonBackendContext): IrFunction {
val newFunction = buildFunctionDeclaration(this)
private fun IrFunction.generateDefaultsFunctionImpl(context: CommonBackendContext, origin: IrDeclarationOrigin): IrFunction {
val newFunction = buildFunctionDeclaration(this, origin)
val syntheticParameters = MutableList((valueParameters.size + 31) / 32) { i ->
valueParameter(valueParameters.size + i, parameterMaskName(i), context.irBuiltIns.intType)
@@ -375,6 +390,9 @@ private fun IrFunction.generateDefaultsFunctionImpl(context: CommonBackendContex
newFunction.copyTypeParametersFrom(this)
val newValueParameters = valueParameters.map { it.copyTo(newFunction) } + syntheticParameters
newValueParameters.forEach {
it.defaultValue = null
}
newFunction.returnType = returnType
newFunction.dispatchReceiverParameter = dispatchReceiverParameter?.run {
@@ -385,17 +403,19 @@ private fun IrFunction.generateDefaultsFunctionImpl(context: CommonBackendContex
annotations.mapTo(newFunction.annotations) { it.deepCopyWithSymbols() }
newFunction.parent = parent
return newFunction
}
private fun buildFunctionDeclaration(irFunction: IrFunction): IrFunction {
private fun buildFunctionDeclaration(irFunction: IrFunction, origin: IrDeclarationOrigin): IrFunction {
when (irFunction) {
is IrConstructor -> {
val descriptor = WrappedClassConstructorDescriptor(irFunction.descriptor.annotations, irFunction.descriptor.source)
return IrConstructorImpl(
irFunction.startOffset,
irFunction.endOffset,
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
origin,
IrConstructorSymbolImpl(descriptor),
irFunction.name,
irFunction.visibility,
@@ -414,7 +434,7 @@ private fun buildFunctionDeclaration(irFunction: IrFunction): IrFunction {
return IrFunctionImpl(
irFunction.startOffset,
irFunction.endOffset,
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
origin,
IrSimpleFunctionSymbolImpl(descriptor),
name,
irFunction.visibility,
@@ -432,9 +452,9 @@ private fun buildFunctionDeclaration(irFunction: IrFunction): IrFunction {
}
}
private fun IrFunction.generateDefaultsFunction(context: CommonBackendContext): IrFunction =
private fun IrFunction.generateDefaultsFunction(context: CommonBackendContext, origin: IrDeclarationOrigin): IrFunction =
context.ir.defaultParameterDeclarationsCache.getOrPut(this) {
generateDefaultsFunctionImpl(context)
generateDefaultsFunctionImpl(context, origin)
}
object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER :
@@ -107,6 +107,7 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc
VarargLowering(this).lower(moduleFragment)
LateinitLowering(this, true).lower(moduleFragment)
DefaultArgumentStubGenerator(this).runOnFilesPostfix(moduleFragment)
DefaultParameterFakeOverrideCleanup(this).runOnFilesPostfix(moduleFragment)
DefaultParameterInjector(this).runOnFilesPostfix(moduleFragment)
DefaultParameterCleaner(this).runOnFilesPostfix(moduleFragment)
SharedVariablesLowering(this).runOnFilesPostfix(moduleFragment)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
enum class A(val a: Int = 1) {
FIRST(),
SECOND(2)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
enum class Foo(val a: Int = 1, val b: String) {
B(2, "b"),
C(b = "b")
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
enum class Foo(val a: Int = 1, val b: String = "a") {
A(),
B(2, "b"),
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
enum class Foo(val a: Double = 1.0, val b: Double = 1.0) {
A(),
B(2.0, 2.0),
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// See KT-21968
interface A {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface Foo {
fun foo(a: Double = 1.0): Double
}
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM
// See KT-15971
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A {
fun visit(a:String, b:String="") : String = b + a
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface Base {
fun bar(a: String = "abc"): String = a + " from interface"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface I {
fun foo(x: Int = 23): String
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface FooTrait<T> {
fun make(size: Int = 16) : T
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM
var log = ""
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
enum class Test(val str: String = "OK") {
OK
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
enum class Test(val str: String = "OK") {
OK {
fun foo() {}
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
enum class ClassTemplate(
// var bug: Int = 1,
var code: Int,
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A {
fun bar2(arg: Int = 239) : Int
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface A {
fun foo(x: Int, y: Int = x + 20, z: Int = y * 2) = z
}
@@ -1,5 +1,5 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR, JS_IR
// IGNORE_BACKEND: JVM_IR
inline class Z(val z: Int)
+1 -1
View File
@@ -1,5 +1,5 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR, JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
inline class Ucn(private val i: UInt)
@@ -1,5 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR, JS_IR, NATIVE
// IGNORE_BACKEND: JVM_IR, NATIVE
// FILE: common.kt
expect interface I {
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM
// FILE: lib.kt
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// FILE: lib.kt
expect interface I {
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// FILE: lib.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface I {
fun foo(): String = "foo"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface I<T> {
fun foo(x: T): String = "foo($x)"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1294
package foo
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1291
package foo
@@ -7,9 +6,14 @@ enum class A(val a: Int = 1) {
SECOND(2)
}
class B(val a: Int = 1)
fun box(): String {
if (A.FIRST.a == 1 && A.SECOND.a == 2) {
return "OK"
}
B()
return "fail"
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1291
package foo
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1295
package foo
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1270
// FILE: classes.kt
class C : J
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1304
interface I {
fun foo(x: String = "default"): String = "I.foo($x)"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1299
package foo
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1282
// MODULE: lib
// FILE: lib.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1284
// MODULE: lib
// FILE: lib.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1281
// FILE: a.kt
fun foo(n: Int): String = js("""