[K/JS] Use declared upper-bound types for parameters inside inlined functions body, instead of the provided types

This commit is contained in:
Artem Kobzar
2023-06-20 12:01:28 +00:00
committed by Space Team
parent aa4ebe1991
commit 039b5fca7a
38 changed files with 269 additions and 53 deletions
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.backend.common.lower.inline
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.ir.isPure
import org.jetbrains.kotlin.backend.common.lower.InnerClassesSupport
@@ -90,7 +87,8 @@ class FunctionInlining(
private val inlinePureArguments: Boolean = true,
private val regenerateInlinedAnonymousObjects: Boolean = false,
private val inlineArgumentsWithTheirOriginalTypeAndOffset: Boolean = false,
private val allowExternalInlining: Boolean = false
private val allowExternalInlining: Boolean = false,
private val useTypeParameterUpperBound: Boolean = false
) : IrElementTransformerVoidWithContext(), BodyLoweringPass {
private var containerScope: ScopeWithIr? = null
@@ -746,7 +744,8 @@ class FunctionInlining(
// We take type parameter from copied callee and not from original because we need an actual copy. Without this copy,
// in case of recursive call, we can get a situation there the same type parameter will be mapped on different type arguments.
// (see compiler/testData/codegen/boxInline/complex/use.kt test file)
return copy.typeParameters[typeClassifier.index].defaultType.substituteSuperTypes()
val newTypeParameter = copy.typeParameters[typeClassifier.index].defaultType.substituteSuperTypes()
return if (useTypeParameterUpperBound) typeClassifier.firstRealUpperBound() else newTypeParameter
}
return when (this) {
@@ -755,12 +754,29 @@ class FunctionInlining(
copy.extensionReceiverParameter -> original.extensionReceiverParameter?.getTypeIfFromTypeParameter()
?: copy.extensionReceiverParameter!!.type
else -> copy.valueParameters.first { it == this }.let { valueParameter ->
original.valueParameters[valueParameter.index].getTypeIfFromTypeParameter()
original.valueParameters.getOrNull(valueParameter.index)?.getTypeIfFromTypeParameter()
?: valueParameter.type
}
}
}
private fun IrTypeParameter?.firstRealUpperBound(): IrType {
val queue = this?.superTypes?.toMutableList() ?: mutableListOf()
while (queue.isNotEmpty()) {
val superType = queue.removeFirst()
val superTypeClassifier = superType.classifierOrNull?.owner ?: continue
if (superTypeClassifier is IrTypeParameter) {
queue.addAll(superTypeClassifier.superTypes)
} else {
return superType
}
}
return context.irBuiltIns.anyNType
}
private fun evaluateArguments(callSite: IrFunctionAccessExpression, callee: IrFunction): List<IrStatement> {
val arguments = buildParameterToArgument(callSite, callee)
val evaluationStatements = mutableListOf<IrVariable>()
@@ -280,7 +280,17 @@ private val saveInlineFunctionsBeforeInlining = makeDeclarationTransformerPhase(
)
private val functionInliningPhase = makeBodyLoweringPhase(
{ FunctionInlining(it, JsInlineFunctionResolver(it), it.innerClassesSupport, allowExternalInlining = true) },
{
FunctionInlining(
it,
JsInlineFunctionResolver(it),
it.innerClassesSupport,
allowExternalInlining = true,
useTypeParameterUpperBound = true,
alwaysCreateTemporaryVariablesForArguments = true,
inlineArgumentsWithTheirOriginalTypeAndOffset = true
)
},
name = "FunctionInliningPhase",
description = "Perform function inlining",
prerequisite = setOf(saveInlineFunctionsBeforeInlining)
@@ -8,20 +8,14 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.defaultType
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassBoxing
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassUnboxing
typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpression
@@ -179,6 +181,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(0)!!)!!
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
JsNew(context.getNameForConstructor(constructor).makeRef(), listOf(arg))
.apply { isInlineClassBoxing = true }
}
add(intrinsics.jsUnboxIntrinsic) { call, context ->
@@ -186,7 +189,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(1)!!)!!
val field = getInlineClassBackingField(inlineClass)
val fieldName = context.getNameForField(field)
JsNameRef(fieldName, arg)
JsNameRef(fieldName, arg).apply { isInlineClassUnboxing = true }
}
add(intrinsics.jsCall) { call, context: JsGenerationContext ->
@@ -2,7 +2,7 @@
// WITH_COROUTINES
// CHECK_BYTECODE_LISTING
// FIR_IDENTICAL
// CHECK_NEW_COUNT: function=suspendHere count=0
// CHECK_NEW_COUNT: function=suspendHere count=0 TARGET_BACKENDS=JS
// FIXME: Coroutine inlining
// CHECK_NEW_COUNT: function=complexSuspend count=0 TARGET_BACKENDS=JS
import helpers.*
@@ -0,0 +1,32 @@
// WITH_STDLIB
// WORKS_WHEN_VALUE_CLASS
// LANGUAGE: +ValueClasses
// TARGET_BACKEND: JS_IR
// TARGET_BACKEND: JS_IR_ES6
interface Foo {
fun foo(): String
fun bar(): String
}
value class Value(val x: Int) : Foo, Comparable<Value> {
override fun foo() = "FOO $x"
override fun bar() = "BAR $x"
override fun compareTo(other: Value) = x.compareTo(other.x)
}
inline fun <T> foo(a: T, b: T): String where T: Foo, T: Comparable<T> {
return if (a > b) a.foo() else b.foo()
}
inline fun <T> bar(a: T, b: T): String where T: Comparable<T>, T: Foo {
return if (a > b) a.bar() else b.bar()
}
fun box(): String {
if (foo(Value(1), Value(2)) != "FOO 2") return "Fail"
if (bar(Value(2), Value(1)) != "BAR 2") return "Fail"
return "OK"
}
@@ -2,8 +2,6 @@
// WASM_MUTE_REASON: IGNORED_IN_JS
// WITH_COROUTINES
// WITH_STDLIB
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// WORKS_WHEN_VALUE_CLASS
// LANGUAGE: +ValueClasses
@@ -2,8 +2,6 @@
// WASM_MUTE_REASON: IGNORED_IN_JS
// WITH_COROUTINES
// WITH_STDLIB
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// WORKS_WHEN_VALUE_CLASS
// LANGUAGE: +ValueClasses, +GenericInlineClassParameter
+1
View File
@@ -81,6 +81,7 @@ fun box() {
// a.kt:6 exclamate: s="Jesse":kotlin.String
// test.kt:52 box: jesse="Jesse":kotlin.String, walter1="Walter!":kotlin.String, jesse1="Jesse!":kotlin.String
// a.kt:6 exclamate: s="Jesse!":kotlin.String
// a.kt:19 box: jesse="Jesse":kotlin.String, walter1="Walter!":kotlin.String, jesse1="Jesse!":kotlin.String
// a.kt:22 value:
// test.kt:63 box: jesse="Jesse":kotlin.String, walter1="Walter!":kotlin.String, jesse1="Jesse!":kotlin.String
// test.kt:59 localFun: hello="hello":kotlin.String, world="world":kotlin.String
+1
View File
@@ -47,6 +47,7 @@ fun box(): String {
// test.kt:30 box
// EXPECTATIONS JS_IR
// test.kt:16 box
// test.kt:17 box
// test.kt:7 box
// test.kt:30 box
@@ -25,4 +25,4 @@ inline fun bar(i: Int = 1) {
// EXPECTATIONS JS_IR
// test.kt:4 box
// test.kt:9 foo
// test.kt:6 box
// test.kt:6 box
@@ -22,6 +22,6 @@ fun g() {}
// EXPECTATIONS JS_IR
// test.kt:3 box
// test.kt:8 box
// test.kt:4 box
// test.kt:11 g
// test.kt:5 box