Skip to content

Commit 039b5fc

Browse files
JSMonkSpace Team
authored and
Space Team
committed
[K/JS] Use declared upper-bound types for parameters inside inlined functions body, instead of the provided types
1 parent aa4ebe1 commit 039b5fc

File tree

38 files changed

+269
-53
lines changed

38 files changed

+269
-53
lines changed

compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
package org.jetbrains.kotlin.backend.common.lower.inline
77

88

9-
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
10-
import org.jetbrains.kotlin.backend.common.CommonBackendContext
11-
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
12-
import org.jetbrains.kotlin.backend.common.ScopeWithIr
9+
import org.jetbrains.kotlin.backend.common.*
1310
import org.jetbrains.kotlin.backend.common.ir.Symbols
1411
import org.jetbrains.kotlin.backend.common.ir.isPure
1512
import org.jetbrains.kotlin.backend.common.lower.InnerClassesSupport
@@ -90,7 +87,8 @@ class FunctionInlining(
9087
private val inlinePureArguments: Boolean = true,
9188
private val regenerateInlinedAnonymousObjects: Boolean = false,
9289
private val inlineArgumentsWithTheirOriginalTypeAndOffset: Boolean = false,
93-
private val allowExternalInlining: Boolean = false
90+
private val allowExternalInlining: Boolean = false,
91+
private val useTypeParameterUpperBound: Boolean = false
9492
) : IrElementTransformerVoidWithContext(), BodyLoweringPass {
9593
private var containerScope: ScopeWithIr? = null
9694

@@ -746,7 +744,8 @@ class FunctionInlining(
746744
// We take type parameter from copied callee and not from original because we need an actual copy. Without this copy,
747745
// in case of recursive call, we can get a situation there the same type parameter will be mapped on different type arguments.
748746
// (see compiler/testData/codegen/boxInline/complex/use.kt test file)
749-
return copy.typeParameters[typeClassifier.index].defaultType.substituteSuperTypes()
747+
val newTypeParameter = copy.typeParameters[typeClassifier.index].defaultType.substituteSuperTypes()
748+
return if (useTypeParameterUpperBound) typeClassifier.firstRealUpperBound() else newTypeParameter
750749
}
751750

752751
return when (this) {
@@ -755,12 +754,29 @@ class FunctionInlining(
755754
copy.extensionReceiverParameter -> original.extensionReceiverParameter?.getTypeIfFromTypeParameter()
756755
?: copy.extensionReceiverParameter!!.type
757756
else -> copy.valueParameters.first { it == this }.let { valueParameter ->
758-
original.valueParameters[valueParameter.index].getTypeIfFromTypeParameter()
757+
original.valueParameters.getOrNull(valueParameter.index)?.getTypeIfFromTypeParameter()
759758
?: valueParameter.type
760759
}
761760
}
762761
}
763762

763+
private fun IrTypeParameter?.firstRealUpperBound(): IrType {
764+
val queue = this?.superTypes?.toMutableList() ?: mutableListOf()
765+
766+
while (queue.isNotEmpty()) {
767+
val superType = queue.removeFirst()
768+
val superTypeClassifier = superType.classifierOrNull?.owner ?: continue
769+
770+
if (superTypeClassifier is IrTypeParameter) {
771+
queue.addAll(superTypeClassifier.superTypes)
772+
} else {
773+
return superType
774+
}
775+
}
776+
777+
return context.irBuiltIns.anyNType
778+
}
779+
764780
private fun evaluateArguments(callSite: IrFunctionAccessExpression, callee: IrFunction): List<IrStatement> {
765781
val arguments = buildParameterToArgument(callSite, callee)
766782
val evaluationStatements = mutableListOf<IrVariable>()

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,17 @@ private val saveInlineFunctionsBeforeInlining = makeDeclarationTransformerPhase(
280280
)
281281

282282
private val functionInliningPhase = makeBodyLoweringPhase(
283-
{ FunctionInlining(it, JsInlineFunctionResolver(it), it.innerClassesSupport, allowExternalInlining = true) },
283+
{
284+
FunctionInlining(
285+
it,
286+
JsInlineFunctionResolver(it),
287+
it.innerClassesSupport,
288+
allowExternalInlining = true,
289+
useTypeParameterUpperBound = true,
290+
alwaysCreateTemporaryVariablesForArguments = true,
291+
inlineArgumentsWithTheirOriginalTypeAndOffset = true
292+
)
293+
},
284294
name = "FunctionInliningPhase",
285295
description = "Perform function inlining",
286296
prerequisite = setOf(saveInlineFunctionsBeforeInlining)

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ConstLowering.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,14 @@ package org.jetbrains.kotlin.ir.backend.js.lower
88
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
99
import org.jetbrains.kotlin.backend.common.compilationException
1010
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
11-
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
12-
import org.jetbrains.kotlin.ir.declarations.IrConstructor
1311
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
1412
import org.jetbrains.kotlin.ir.expressions.IrBody
1513
import org.jetbrains.kotlin.ir.expressions.IrConst
1614
import org.jetbrains.kotlin.ir.expressions.IrConstKind
1715
import org.jetbrains.kotlin.ir.expressions.IrExpression
18-
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
1916
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
2017
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
2118
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
22-
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
23-
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
24-
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
2519
import org.jetbrains.kotlin.ir.types.IrType
2620
import org.jetbrains.kotlin.ir.types.classifierOrNull
2721
import org.jetbrains.kotlin.ir.types.defaultType

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
2323
import org.jetbrains.kotlin.ir.types.classifierOrFail
2424
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
2525
import org.jetbrains.kotlin.js.backend.ast.*
26+
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassBoxing
27+
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassUnboxing
2628

2729
typealias IrCallTransformer = (IrCall, context: JsGenerationContext) -> JsExpression
2830

@@ -179,14 +181,15 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
179181
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(0)!!)!!
180182
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
181183
JsNew(context.getNameForConstructor(constructor).makeRef(), listOf(arg))
184+
.apply { isInlineClassBoxing = true }
182185
}
183186

184187
add(intrinsics.jsUnboxIntrinsic) { call, context ->
185188
val arg = translateCallArguments(call, context).single()
186189
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(1)!!)!!
187190
val field = getInlineClassBackingField(inlineClass)
188191
val fieldName = context.getNameForField(field)
189-
JsNameRef(fieldName, arg)
192+
JsNameRef(fieldName, arg).apply { isInlineClassUnboxing = true }
190193
}
191194

192195
add(intrinsics.jsCall) { call, context: JsGenerationContext ->

compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// WITH_COROUTINES
33
// CHECK_BYTECODE_LISTING
44
// FIR_IDENTICAL
5-
// CHECK_NEW_COUNT: function=suspendHere count=0
5+
// CHECK_NEW_COUNT: function=suspendHere count=0 TARGET_BACKENDS=JS
66
// FIXME: Coroutine inlining
77
// CHECK_NEW_COUNT: function=complexSuspend count=0 TARGET_BACKENDS=JS
88
import helpers.*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// WITH_STDLIB
2+
// WORKS_WHEN_VALUE_CLASS
3+
// LANGUAGE: +ValueClasses
4+
// TARGET_BACKEND: JS_IR
5+
// TARGET_BACKEND: JS_IR_ES6
6+
7+
interface Foo {
8+
fun foo(): String
9+
fun bar(): String
10+
}
11+
12+
value class Value(val x: Int) : Foo, Comparable<Value> {
13+
override fun foo() = "FOO $x"
14+
15+
override fun bar() = "BAR $x"
16+
17+
override fun compareTo(other: Value) = x.compareTo(other.x)
18+
}
19+
20+
inline fun <T> foo(a: T, b: T): String where T: Foo, T: Comparable<T> {
21+
return if (a > b) a.foo() else b.foo()
22+
}
23+
24+
inline fun <T> bar(a: T, b: T): String where T: Comparable<T>, T: Foo {
25+
return if (a > b) a.bar() else b.bar()
26+
}
27+
28+
fun box(): String {
29+
if (foo(Value(1), Value(2)) != "FOO 2") return "Fail"
30+
if (bar(Value(2), Value(1)) != "BAR 2") return "Fail"
31+
return "OK"
32+
}

compiler/testData/codegen/box/inlineClasses/unboxParameterOfSuspendLambdaBeforeInvoke.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// WASM_MUTE_REASON: IGNORED_IN_JS
33
// WITH_COROUTINES
44
// WITH_STDLIB
5-
// IGNORE_BACKEND: JS_IR
6-
// IGNORE_BACKEND: JS_IR_ES6
75
// WORKS_WHEN_VALUE_CLASS
86
// LANGUAGE: +ValueClasses
97

compiler/testData/codegen/box/inlineClasses/unboxParameterOfSuspendLambdaBeforeInvokeGeneric.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// WASM_MUTE_REASON: IGNORED_IN_JS
33
// WITH_COROUTINES
44
// WITH_STDLIB
5-
// IGNORE_BACKEND: JS_IR
6-
// IGNORE_BACKEND: JS_IR_ES6
75
// WORKS_WHEN_VALUE_CLASS
86
// LANGUAGE: +ValueClasses, +GenericInlineClassParameter
97

compiler/testData/debug/localVariables/jsCode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ fun box() {
8181
// a.kt:6 exclamate: s="Jesse":kotlin.String
8282
// test.kt:52 box: jesse="Jesse":kotlin.String, walter1="Walter!":kotlin.String, jesse1="Jesse!":kotlin.String
8383
// a.kt:6 exclamate: s="Jesse!":kotlin.String
84+
// a.kt:19 box: jesse="Jesse":kotlin.String, walter1="Walter!":kotlin.String, jesse1="Jesse!":kotlin.String
8485
// a.kt:22 value:
8586
// test.kt:63 box: jesse="Jesse":kotlin.String, walter1="Walter!":kotlin.String, jesse1="Jesse!":kotlin.String
8687
// test.kt:59 localFun: hello="hello":kotlin.String, world="world":kotlin.String

compiler/testData/debug/stepping/assertion.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ fun box(): String {
4747
// test.kt:30 box
4848

4949
// EXPECTATIONS JS_IR
50+
// test.kt:16 box
5051
// test.kt:17 box
5152
// test.kt:7 box
5253
// test.kt:30 box

compiler/testData/debug/stepping/functionCallWithDefault.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ inline fun bar(i: Int = 1) {
2525
// EXPECTATIONS JS_IR
2626
// test.kt:4 box
2727
// test.kt:9 foo
28-
// test.kt:6 box
28+
// test.kt:6 box

compiler/testData/debug/stepping/inlineNamedCallableReference.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ fun g() {}
2222

2323
// EXPECTATIONS JS_IR
2424
// test.kt:3 box
25-
// test.kt:8 box
25+
// test.kt:4 box
2626
// test.kt:11 g
2727
// test.kt:5 box

js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ var JsReturn.returnTarget: FunctionDescriptor? by MetadataProperty(default = nul
7979

8080
var HasMetadata.synthetic: Boolean by MetadataProperty(default = false)
8181

82+
var HasMetadata.isInlineClassBoxing: Boolean by MetadataProperty(default = false)
83+
var HasMetadata.isInlineClassUnboxing: Boolean by MetadataProperty(default = false)
84+
8285
var HasMetadata.sideEffects: SideEffectKind by MetadataProperty(default = SideEffectKind.AFFECTS_STATE)
8386

8487
/**
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2010-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.js.inline.clean
18+
19+
import org.jetbrains.kotlin.js.backend.ast.*
20+
import org.jetbrains.kotlin.js.backend.ast.JsExpression.JsExpressionHasArguments
21+
import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata
22+
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassBoxing
23+
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassUnboxing
24+
import org.jetbrains.kotlin.js.backend.ast.metadata.isJsCall
25+
import org.jetbrains.kotlin.js.inline.util.isCallInvocation
26+
27+
// Replaces box(unbox(value)) and unbox(box(value)) with value
28+
class BoxingUnboxingElimination(private val root: JsBlock) {
29+
private var changed = false
30+
31+
fun apply(): Boolean {
32+
val visitor = object : JsVisitorWithContextImpl() {
33+
override fun endVisit(x: JsInvocation, ctx: JsContext<JsNode>) {
34+
super.endVisit(x, ctx)
35+
tryEliminate(x, ctx)
36+
}
37+
38+
override fun endVisit(x: JsNew, ctx: JsContext<JsNode>) {
39+
super.endVisit(x, ctx)
40+
tryEliminate(x, ctx)
41+
}
42+
43+
44+
override fun endVisit(x: JsNameRef, ctx: JsContext<JsNode>) {
45+
super.endVisit(x, ctx)
46+
tryEliminate(x, ctx)
47+
}
48+
49+
override fun endVisit(x: JsArrayAccess, ctx: JsContext<*>) {
50+
super.endVisit(x, ctx)
51+
}
52+
53+
override fun visit(x: JsFunction, ctx: JsContext<JsNode>) = false
54+
55+
private fun tryEliminate(expression: JsExpression, ctx: JsContext<JsNode>) {
56+
if (!expression.isInlineClassBoxing && !expression.isInlineClassUnboxing) return
57+
58+
val firstArg = expression.arguments.first()
59+
60+
if (!firstArg.isInlineClassBoxing && !firstArg.isInlineClassUnboxing) return
61+
62+
if (firstArg.isInlineClassBoxing != expression.isInlineClassBoxing) {
63+
ctx.replaceMe(firstArg.arguments.first())
64+
changed = true
65+
}
66+
}
67+
68+
private val JsExpression.arguments: List<JsExpression>
69+
get() = when (this) {
70+
is JsExpressionHasArguments -> arguments
71+
is JsNameRef -> listOfNotNull(qualifier)
72+
else -> emptyList()
73+
}
74+
}
75+
76+
visitor.accept(root)
77+
78+
return changed
79+
}
80+
}

js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/FunctionPostProcessor.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ class FunctionPostProcessor(val root: JsFunction) {
3131
{ DeadCodeElimination(root.body).apply() },
3232
{ RedundantVariableDeclarationElimination(root.body).apply() },
3333
{ RedundantStatementElimination(root).apply() },
34-
{ CoroutineStateElimination(root.body).apply() }
34+
{ CoroutineStateElimination(root.body).apply() },
35+
{ BoxingUnboxingElimination(root.body).apply() }
3536
)
3637
// TODO: reduce to A || B, A && B if possible
3738

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)