JVM IR: substitute generic type for inline class replacement function calls
The main change here is in `JvmInlineClassLowering.visitFunctionAccess`, where we now store the substituted return type of the function as the type of the call expression. Without it, the call could have a meaningless type, e.g. some `T` which is inaccessible at that place, and that could backfire in subsequent lowerings in codegen. For example, in the `stringPlus.kt` test, it would prevent the code in `FlattenStringConcatenationLowering.isStringPlusCall` from recognizing and replacing the `String.plus` call, leading to a codegen exception. Other changes are mostly cosmetics to make the code similar to `visitFunctionReference`, and preventive optimizations for the case when the substitution map is empty.
This commit is contained in:
Generated
+5
@@ -14117,6 +14117,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ fun IrType.substitute(params: List<IrTypeParameter>, arguments: List<IrType>): I
|
|||||||
substitute(params.map { it.symbol }.zip(arguments).toMap())
|
substitute(params.map { it.symbol }.zip(arguments).toMap())
|
||||||
|
|
||||||
fun IrType.substitute(substitutionMap: Map<IrTypeParameterSymbol, IrType>): IrType {
|
fun IrType.substitute(substitutionMap: Map<IrTypeParameterSymbol, IrType>): IrType {
|
||||||
if (this !is IrSimpleType) return this
|
if (this !is IrSimpleType || substitutionMap.isEmpty()) return this
|
||||||
|
|
||||||
val newAnnotations = annotations.map { it.deepCopyWithSymbols() }
|
val newAnnotations = annotations.map { it.deepCopyWithSymbols() }
|
||||||
|
|
||||||
|
|||||||
+13
-8
@@ -277,15 +277,16 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
|||||||
if (expression.origin == InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE)
|
if (expression.origin == InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE)
|
||||||
return super.visitFunctionReference(expression)
|
return super.visitFunctionReference(expression)
|
||||||
|
|
||||||
val function = context.inlineClassReplacements.getReplacementFunction(expression.symbol.owner)
|
val function = expression.symbol.owner
|
||||||
|
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
|
||||||
?: return super.visitFunctionReference(expression)
|
?: return super.visitFunctionReference(expression)
|
||||||
|
|
||||||
return IrFunctionReferenceImpl(
|
return IrFunctionReferenceImpl(
|
||||||
expression.startOffset, expression.endOffset, expression.type,
|
expression.startOffset, expression.endOffset, expression.type,
|
||||||
function.symbol, function.typeParameters.size,
|
replacement.symbol, replacement.typeParameters.size,
|
||||||
function.valueParameters.size, expression.reflectionTarget, expression.origin
|
replacement.valueParameters.size, expression.reflectionTarget, expression.origin
|
||||||
).apply {
|
).apply {
|
||||||
buildReplacement(expression.symbol.owner, expression, function)
|
buildReplacement(function, expression, replacement)
|
||||||
}.copyAttributes(expression)
|
}.copyAttributes(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,10 +294,14 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
|
|||||||
val function = expression.symbol.owner
|
val function = expression.symbol.owner
|
||||||
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
|
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
|
||||||
?: return super.visitFunctionAccess(expression)
|
?: return super.visitFunctionAccess(expression)
|
||||||
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
|
||||||
.irCall(replacement, expression.origin, expression.safeAs<IrCall>()?.superQualifierSymbol).apply {
|
return IrCallImpl(
|
||||||
buildReplacement(function, expression, replacement)
|
expression.startOffset, expression.endOffset, function.returnType.substitute(expression.typeSubstitutionMap),
|
||||||
}
|
replacement.symbol, replacement.typeParameters.size, replacement.valueParameters.size,
|
||||||
|
expression.origin, (expression as? IrCall)?.superQualifierSymbol
|
||||||
|
).apply {
|
||||||
|
buildReplacement(function, expression, replacement)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun coerceInlineClasses(argument: IrExpression, from: IrType, to: IrType) =
|
private fun coerceInlineClasses(argument: IrExpression, from: IrType, to: IrType) =
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
|||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.isAny
|
||||||
|
import org.jetbrains.kotlin.ir.types.isSubtypeOf
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
@@ -512,10 +515,12 @@ val IrFunction.allTypeParameters: List<IrTypeParameter>
|
|||||||
else
|
else
|
||||||
typeParameters
|
typeParameters
|
||||||
|
|
||||||
fun IrMemberAccessExpression<*>.getTypeSubstitutionMap(irFunction: IrFunction): Map<IrTypeParameterSymbol, IrType> =
|
fun IrMemberAccessExpression<*>.getTypeSubstitutionMap(irFunction: IrFunction): Map<IrTypeParameterSymbol, IrType> {
|
||||||
irFunction.allTypeParameters.withIndex().associate {
|
val typeParameters = irFunction.allTypeParameters
|
||||||
|
return if (typeParameters.isEmpty()) emptyMap() else typeParameters.withIndex().associate {
|
||||||
it.value.symbol to getTypeArgument(it.index)!!
|
it.value.symbol to getTypeArgument(it.index)!!
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val IrFunctionReference.typeSubstitutionMap: Map<IrTypeParameterSymbol, IrType>
|
val IrFunctionReference.typeSubstitutionMap: Map<IrTypeParameterSymbol, IrType>
|
||||||
get() = getTypeSubstitutionMap(symbol.owner)
|
get() = getTypeSubstitutionMap(symbol.owner)
|
||||||
@@ -536,4 +541,4 @@ val IrFunction.originalFunction: IrFunction
|
|||||||
get() = (this as? IrAttributeContainer)?.attributeOwnerId as? IrFunction ?: this
|
get() = (this as? IrAttributeContainer)?.attributeOwnerId as? IrFunction ?: this
|
||||||
|
|
||||||
val IrProperty.originalProperty: IrProperty
|
val IrProperty.originalProperty: IrProperty
|
||||||
get() = attributeOwnerId as? IrProperty ?: this
|
get() = attributeOwnerId as? IrProperty ?: this
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// !LANGUAGE: +InlineClasses
|
||||||
|
|
||||||
|
fun <T> foo(a: IC): T = a.value as T
|
||||||
|
|
||||||
|
inline class IC(val value: String)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return foo<String>(IC("O")) + "K"
|
||||||
|
}
|
||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
// !LANGUAGE: +InlineClasses
|
// !LANGUAGE: +InlineClasses
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
|
|
||||||
fun <T> underlying(a: IC): T = bar(a) {
|
fun <T> underlying(a: IC): T = bar(a) {
|
||||||
it.value as T
|
it.value as T
|
||||||
@@ -48,4 +46,4 @@ fun box(): String {
|
|||||||
if (res != "OK") return "FAIL 3: $res"
|
if (res != "OK") return "FAIL 3: $res"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
// !LANGUAGE: +InlineClasses
|
// !LANGUAGE: +InlineClasses
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
|
|
||||||
fun <T> underlying(a: IC): T = bar(a) {
|
fun <T> underlying(a: IC): T = bar(a) {
|
||||||
it.value as T
|
it.value as T
|
||||||
@@ -44,4 +42,4 @@ fun box(): String {
|
|||||||
if (res != "OK") return "FAIL 3: $res"
|
if (res != "OK") return "FAIL 3: $res"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
// !LANGUAGE: +InlineClasses
|
// !LANGUAGE: +InlineClasses
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
|
|
||||||
fun <T> underlying(a: IC): T = bar(a, object : IFace<IC, T> {
|
fun <T> underlying(a: IC): T = bar(a, object : IFace<IC, T> {
|
||||||
override fun call(ic: IC): T = ic.value as T
|
override fun call(ic: IC): T = ic.value as T
|
||||||
@@ -48,4 +46,4 @@ fun box(): String {
|
|||||||
if (res != "OK") return "FAIL 3: $res"
|
if (res != "OK") return "FAIL 3: $res"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-5
@@ -1,8 +1,4 @@
|
|||||||
// !LANGUAGE: +InlineClasses
|
// !LANGUAGE: +InlineClasses
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR, JVM_MULTI_MODULE_IR_AGAINST_OLD
|
|
||||||
|
|
||||||
// FILE: inline.kt
|
// FILE: inline.kt
|
||||||
|
|
||||||
inline class IC(val value: String) {
|
inline class IC(val value: String) {
|
||||||
@@ -47,4 +43,4 @@ fun box(): String {
|
|||||||
if (res != "OK") return "FAIL 3: $res"
|
if (res != "OK") return "FAIL 3: $res"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-5
@@ -1,8 +1,4 @@
|
|||||||
// !LANGUAGE: +InlineClasses
|
// !LANGUAGE: +InlineClasses
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR, JVM_MULTI_MODULE_IR_AGAINST_OLD
|
|
||||||
|
|
||||||
// FILE: inline.kt
|
// FILE: inline.kt
|
||||||
|
|
||||||
inline class IC(val value: String) {
|
inline class IC(val value: String) {
|
||||||
@@ -43,4 +39,4 @@ fun box(): String {
|
|||||||
if (res != "OK") return "FAIL 3: $res"
|
if (res != "OK") return "FAIL 3: $res"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-5
@@ -1,8 +1,4 @@
|
|||||||
// !LANGUAGE: +InlineClasses
|
// !LANGUAGE: +InlineClasses
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR, JVM_MULTI_MODULE_IR_AGAINST_OLD
|
|
||||||
|
|
||||||
// FILE: inline.kt
|
// FILE: inline.kt
|
||||||
|
|
||||||
inline class IC(val value: String) {
|
inline class IC(val value: String) {
|
||||||
@@ -47,4 +43,4 @@ fun box(): String {
|
|||||||
if (res != "OK") return "FAIL 3: $res"
|
if (res != "OK") return "FAIL 3: $res"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -15517,6 +15517,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
+5
@@ -15522,6 +15522,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
+5
@@ -14117,6 +14117,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
Generated
+5
@@ -12122,6 +12122,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
Generated
+5
@@ -12122,6 +12122,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
+5
@@ -12187,6 +12187,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/smartCastOnThisOfInlineClassType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stringPlus.kt")
|
||||||
|
public void testStringPlus() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/inlineClasses/stringPlus.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toStringCallingPrivateFun.kt")
|
@TestMetadata("toStringCallingPrivateFun.kt")
|
||||||
public void testToStringCallingPrivateFun() throws Exception {
|
public void testToStringCallingPrivateFun() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user