From 412f07c55b03d9a13774ae3c890d4c3bcac6e2dd Mon Sep 17 00:00:00 2001 From: Vladimir Sukharev Date: Thu, 28 Jul 2022 14:57:28 +0000 Subject: [PATCH] Call toString() within known class, during string concatenation type narrowing ^KT-53119 Improved Merge-request: KT-MR-6755 Merged-by: Vladimir Sukharev --- .../lower/StringConcatenationTypeNarrowing.kt | 13 ++++-- .../kt53119_append_generated.kt | 38 ++++++++++++++++++ .../kt53119_append_generated.out | 4 ++ .../kt53119_append_manual.kt | 16 ++++++++ .../kt53119_append_manual.out | 1 + .../kt53119_plus_extension.kt | 40 +++++++++++++++++++ .../kt53119_plus_extension.out | 3 ++ .../kt53119_plus_member.kt | 30 ++++++++++++++ .../kt53119_plus_member.out | 3 ++ 9 files changed, 145 insertions(+), 3 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationTypeNarrowing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationTypeNarrowing.kt index 1447dc73052..203f314d873 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationTypeNarrowing.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/StringConcatenationTypeNarrowing.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.util.shallowCopy import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions /** * This pass replaces calls to: @@ -134,9 +135,15 @@ internal class StringConcatenationTypeNarrowing(val context: Context) : FileLowe private fun buildNonNullableArgToString(argument: IrExpression): IrExpression { return if (argument.type.isString() || argument.type.isNullableString()) argument - else builder - .irCall(context.ir.symbols.memberToString, context.irBuiltIns.stringType, valueArgumentsCount = 1, typeArgumentsCount = 0) - .apply { dispatchReceiver = argument } + else { + val calleeOrNull = argument.type.classOrNull?.owner?.functions?.singleOrNull { + it.name == OperatorNameConventions.TO_STRING && it.valueParameters.isEmpty() + }?.symbol + val callee = calleeOrNull ?: context.ir.symbols.memberToString // defaults to `Any.toString()` + builder + .irCall(callee, callee.owner.returnType, valueArgumentsCount = 0, typeArgumentsCount = 0) + .apply { dispatchReceiver = argument } + } } /** diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.kt b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.kt index 402f65c9136..ae6006b64b5 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.kt +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.kt @@ -59,6 +59,40 @@ fun maybeAnyString(maybeAny1: Any?, string: String): String { data class Foo(val bar: Int) +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyFoo +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?) +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String) +// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append +// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String" +// CHECK-NOT: Foo#toString(){}kotlin.String +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?) +// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append + +// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String" +// CHECK-NOT: Foo#toString(){}kotlin.String" + +// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append +// CHECK: ret %struct.ObjHeader* + +fun maybeAnyFoo(maybeAny: Any?, foo: Foo): String { + return "$maybeAny,$foo" +} +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyMaybeFoo +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?) +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String) +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?) +// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append + +// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String" +// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated.Foo#toString(){}kotlin.String" +// CHECK-NOT: Foo#toString(){}kotlin.String" + +// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append +// CHECK: ret %struct.ObjHeader* + +fun maybeAnyMaybeFoo(maybeAny: Any?, foo: Foo?): String { + return "$maybeAny,$foo" +} @Test fun runTest() { val foo = Foo(42) @@ -68,4 +102,8 @@ fun runTest() { println(maybeAnyMaybeString(null, null)) println(maybeAnyString(foo, "bar")) println(maybeAnyString(null, "bar")) + println(maybeAnyFoo(foo, foo)) + println(maybeAnyFoo(null, foo)) + println(maybeAnyMaybeFoo(foo, foo)) + println(maybeAnyMaybeFoo(foo, null)) } diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.out b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.out index 55ccd76a57a..dee9b413891 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.out +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_generated.out @@ -4,3 +4,7 @@ Foo(bar=42),bar null,null Foo(bar=42),bar null,bar +Foo(bar=42),Foo(bar=42) +null,Foo(bar=42) +Foo(bar=42),Foo(bar=42) +Foo(bar=42),null diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.kt b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.kt index 6eac2fda323..8eafefc39ad 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.kt +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.kt @@ -67,6 +67,21 @@ fun appendString(str: String): String { data class Foo(val bar: Int) +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendFoo(codegen.stringConcatenationTypeNarrowing.kt53119_append_manual.Foo) +// CHECK: %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual.Foo#toString(){}kotlin.String" +// CHECK-NOT: Foo#toString(){}kotlin.String" +// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?) + +// CHECK-NOT: Foo#toString(){}kotlin.String" +// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append +// CHECK: ret %struct.ObjHeader* + +fun appendFoo(foo: Foo): String { + val sb = kotlin.text.StringBuilder() + sb.append(foo) + return sb.toString() +} + @Test fun runTest() { val foo = Foo(42) @@ -76,4 +91,5 @@ fun runTest() { println(appendMaybeString("foo")) println(appendMaybeString(null)) println(appendString("foo")) + println(appendFoo(foo)) } diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.out b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.out index 1441fc26086..1d7e5b40704 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.out +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_append_manual.out @@ -4,3 +4,4 @@ Foo(bar=42) foo null foo +Foo(bar=42) diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.kt b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.kt index 908d18038d8..725cd0abbc9 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.kt +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.kt @@ -77,6 +77,43 @@ fun generatedPlusExtensionString(maybeStr: String?, str: String): String { data class Foo(val bar: Int) +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#generatedPlusExtensionFoo +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) + +// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension.Foo#toString(){}kotlin.String" +// CHECK-NOT: Foo#toString(){}kotlin.String + +// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl +// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl + +// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String" +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) + +// CHECK: ret %struct.ObjHeader* + +fun generatedPlusExtensionFoo(maybeStr: String?, foo: Foo): String { + return "$maybeStr$foo" +} + +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#generatedPlusExtensionMaybeFoo +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) + +// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl +// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl +// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String" + +// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension.Foo#toString(){}kotlin.String" +// CHECK-NOT: Foo#toString(){}kotlin.String + +// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String" +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) + +// CHECK: ret %struct.ObjHeader* + +fun generatedPlusExtensionMaybeFoo(maybeStr: String?, foo: Foo?): String { + return "$maybeStr$foo" +} + @Test fun runTest() { val foo = Foo(42) @@ -88,4 +125,7 @@ fun runTest() { println(generatedPlusExtensionAny(null, null)) println(generatedPlusExtensionString("foo", "bar")) println(generatedPlusExtensionString(null, "bar")) + println(generatedPlusExtensionFoo(null, Foo(42))) + println(generatedPlusExtensionMaybeFoo("foo", Foo(42))) + println(generatedPlusExtensionMaybeFoo("foo", null)) } diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.out b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.out index 3ef641e3920..160fb91a0e9 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.out +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_extension.out @@ -6,3 +6,6 @@ fooFoo(bar=42) nullnull foobar nullbar +nullFoo(bar=42) +fooFoo(bar=42) +foonull diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.kt b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.kt index 088b41145fa..e915062191c 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.kt +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.kt @@ -68,6 +68,33 @@ fun generatedPlusMemberString(str1: String, str2: String): String { data class Foo(val bar: Int) +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#manualPlusMemberFoo +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) +// CHECK call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member.Foo#toString(){}kotlin.String" +// CHECK-NOT Foo#toString(){}kotlin.String + +// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) +// CHECK-NOT: Foo#toString(){}kotlin.String" + +// CHECK: ret %struct.ObjHeader* +fun manualPlusMemberFoo(str1: String, foo: Foo): kotlin.String = + str1 + foo + +// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#manualPlusMemberMaybeFoo +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) +// CHECK call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member.Foo#toString(){}kotlin.String" +// CHECK-NOT Foo#toString(){}kotlin.String + +// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl +// CHECK: call %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member.Foo#toString +// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?) +// CHECK-NOT: Foo#toString(){}kotlin.String" + +// CHECK: ret %struct.ObjHeader* +fun manualPlusMemberMaybeFoo(str1: String, foo: Foo?): kotlin.String = + str1 + foo + @Test fun runTest() { val foo = Foo(42) @@ -77,4 +104,7 @@ fun runTest() { println(generatedPlusMemberAny("foo", null)) println(generatedPlusMemberAny("foo", foo)) println(generatedPlusMemberString("foo", "bar")) + println(manualPlusMemberFoo("foo", Foo(42))) + println(manualPlusMemberMaybeFoo("foo", Foo(42))) + println(manualPlusMemberMaybeFoo("foo", null)) } diff --git a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.out b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.out index c4e143efd35..3c25d8a8c10 100644 --- a/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.out +++ b/kotlin-native/backend.native/tests/codegen/stringConcatenationTypeNarrowing/kt53119_plus_member.out @@ -4,3 +4,6 @@ foobar foonull fooFoo(bar=42) foobar +fooFoo(bar=42) +fooFoo(bar=42) +foonull