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:
Alexander Udalov
2020-10-26 19:08:00 +01:00
parent 4a3a2ef72a
commit ad5b6da273
17 changed files with 73 additions and 37 deletions
@@ -14117,6 +14117,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
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")
public void testToStringCallingPrivateFun() throws Exception {
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())
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() }
@@ -277,15 +277,16 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
if (expression.origin == InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE)
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 IrFunctionReferenceImpl(
expression.startOffset, expression.endOffset, expression.type,
function.symbol, function.typeParameters.size,
function.valueParameters.size, expression.reflectionTarget, expression.origin
replacement.symbol, replacement.typeParameters.size,
replacement.valueParameters.size, expression.reflectionTarget, expression.origin
).apply {
buildReplacement(expression.symbol.owner, expression, function)
buildReplacement(function, expression, replacement)
}.copyAttributes(expression)
}
@@ -293,10 +294,14 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
val function = expression.symbol.owner
val replacement = context.inlineClassReplacements.getReplacementFunction(function)
?: return super.visitFunctionAccess(expression)
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
.irCall(replacement, expression.origin, expression.safeAs<IrCall>()?.superQualifierSymbol).apply {
buildReplacement(function, expression, replacement)
}
return IrCallImpl(
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) =
@@ -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.IrTypeOperatorCallImpl
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.SpecialNames
import org.jetbrains.kotlin.utils.DFS
@@ -512,10 +515,12 @@ val IrFunction.allTypeParameters: List<IrTypeParameter>
else
typeParameters
fun IrMemberAccessExpression<*>.getTypeSubstitutionMap(irFunction: IrFunction): Map<IrTypeParameterSymbol, IrType> =
irFunction.allTypeParameters.withIndex().associate {
fun IrMemberAccessExpression<*>.getTypeSubstitutionMap(irFunction: IrFunction): Map<IrTypeParameterSymbol, IrType> {
val typeParameters = irFunction.allTypeParameters
return if (typeParameters.isEmpty()) emptyMap() else typeParameters.withIndex().associate {
it.value.symbol to getTypeArgument(it.index)!!
}
}
val IrFunctionReference.typeSubstitutionMap: Map<IrTypeParameterSymbol, IrType>
get() = getTypeSubstitutionMap(symbol.owner)
@@ -536,4 +541,4 @@ val IrFunction.originalFunction: IrFunction
get() = (this as? IrAttributeContainer)?.attributeOwnerId as? IrFunction ?: this
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,6 +1,4 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_FIR: JVM_IR
fun <T> underlying(a: IC): T = bar(a) {
it.value as T
@@ -48,4 +46,4 @@ fun box(): String {
if (res != "OK") return "FAIL 3: $res"
return "OK"
}
}
@@ -1,6 +1,4 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_FIR: JVM_IR
fun <T> underlying(a: IC): T = bar(a) {
it.value as T
@@ -44,4 +42,4 @@ fun box(): String {
if (res != "OK") return "FAIL 3: $res"
return "OK"
}
}
@@ -1,6 +1,4 @@
// !LANGUAGE: +InlineClasses
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_FIR: JVM_IR
fun <T> underlying(a: IC): T = bar(a, object : IFace<IC, 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"
return "OK"
}
}
@@ -1,8 +1,4 @@
// !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
inline class IC(val value: String) {
@@ -47,4 +43,4 @@ fun box(): String {
if (res != "OK") return "FAIL 3: $res"
return "OK"
}
}
@@ -1,8 +1,4 @@
// !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
inline class IC(val value: String) {
@@ -43,4 +39,4 @@ fun box(): String {
if (res != "OK") return "FAIL 3: $res"
return "OK"
}
}
@@ -1,8 +1,4 @@
// !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
inline class IC(val value: String) {
@@ -47,4 +43,4 @@ fun box(): String {
if (res != "OK") return "FAIL 3: $res"
return "OK"
}
}
@@ -15517,6 +15517,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
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")
public void testToStringCallingPrivateFun() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
@@ -15522,6 +15522,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
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")
public void testToStringCallingPrivateFun() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
@@ -14117,6 +14117,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
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")
public void testToStringCallingPrivateFun() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
@@ -12122,6 +12122,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
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")
public void testToStringCallingPrivateFun() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
@@ -12122,6 +12122,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
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")
public void testToStringCallingPrivateFun() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");
@@ -12187,6 +12187,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
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")
public void testToStringCallingPrivateFun() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/toStringCallingPrivateFun.kt");