KT53119: Implement String Concatenation Type Narrowing to improve escape analysis
Merge-request: KT-MR-6661 Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package codegen.stringConcatenationTypeNarrowing.kt53119_append_generated
|
||||
import kotlin.test.*
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyMaybeAny
|
||||
// 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-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 maybeAnyMaybeAny(maybeAny1: Any?, maybeAny2: Any?): String {
|
||||
return "$maybeAny1,$maybeAny2"
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyMaybeString
|
||||
// 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-NOT: Foo#toString(){}kotlin.String"
|
||||
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun maybeAnyMaybeString(maybeAny1: Any?, maybeString2: String?): String {
|
||||
return "$maybeAny1,$maybeString2"
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_generated#maybeAnyString
|
||||
// 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-NOT: Foo#toString(){}kotlin.String"
|
||||
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun maybeAnyString(maybeAny1: Any?, string: String): String {
|
||||
return "$maybeAny1,$string"
|
||||
}
|
||||
|
||||
data class Foo(val bar: Int)
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
val foo = Foo(42)
|
||||
println(maybeAnyMaybeAny(foo, foo))
|
||||
println(maybeAnyMaybeAny(null, null))
|
||||
println(maybeAnyMaybeString(foo, "bar"))
|
||||
println(maybeAnyMaybeString(null, null))
|
||||
println(maybeAnyString(foo, "bar"))
|
||||
println(maybeAnyString(null, "bar"))
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
Foo(bar=42),Foo(bar=42)
|
||||
null,null
|
||||
Foo(bar=42),bar
|
||||
null,null
|
||||
Foo(bar=42),bar
|
||||
null,bar
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package codegen.stringConcatenationTypeNarrowing.kt53119_append_manual
|
||||
import kotlin.test.*
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendMaybeAny(kotlin.Any?)
|
||||
// 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_manual.Foo#toString(){}kotlin.String"
|
||||
// CHECK-NOT: Foo#toString(){}kotlin.String"
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun appendMaybeAny(maybeAny: Any?): String {
|
||||
val sb = kotlin.text.StringBuilder()
|
||||
sb.append(maybeAny)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendAny(kotlin.Any)
|
||||
// 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 appendAny(any: Any): String {
|
||||
val sb = kotlin.text.StringBuilder()
|
||||
sb.append(any)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendMaybeString(kotlin.String?)
|
||||
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
|
||||
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun appendMaybeString(maybeStr: String?): String {
|
||||
val sb = kotlin.text.StringBuilder()
|
||||
sb.append(maybeStr)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_append_manual#appendString(kotlin.String)
|
||||
// CHECK: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append(kotlin.String?)
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.text.StringBuilder#append
|
||||
|
||||
// CHECK-NOT: %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun appendString(str: String): String {
|
||||
val sb = kotlin.text.StringBuilder()
|
||||
sb.append(str)
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
data class Foo(val bar: Int)
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
val foo = Foo(42)
|
||||
println(appendMaybeAny(foo))
|
||||
println(appendMaybeAny(null))
|
||||
println(appendAny(foo))
|
||||
println(appendMaybeString("foo"))
|
||||
println(appendMaybeString(null))
|
||||
println(appendString("foo"))
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
Foo(bar=42)
|
||||
null
|
||||
Foo(bar=42)
|
||||
foo
|
||||
null
|
||||
foo
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension
|
||||
import kotlin.test.*
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#manualPlusExtensionAny
|
||||
// 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: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
// 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: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
|
||||
// CHECK-NOT: Foo#toString(){}kotlin.String"
|
||||
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun manualPlusExtensionAny(maybeStr: String?, maybeAny: kotlin.Any?): kotlin.String =
|
||||
maybeStr + maybeAny
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#manualPlusExtensionString
|
||||
// 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-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun manualPlusExtensionString(maybeStr: String?, str: String): kotlin.String =
|
||||
maybeStr + str
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#generatedPlusExtensionAny
|
||||
// CHECK-NOT: kfun:kotlin#plus__at__kotlin.String?(kotlin.Any?)
|
||||
|
||||
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
// CHECK-NOT: kfun:kotlin#plus__at__kotlin.String?(kotlin.Any?)
|
||||
|
||||
// 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#plus__at__kotlin.String?(kotlin.Any?)
|
||||
// CHECK-NOT: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun generatedPlusExtensionAny(maybeStr: String?, maybeAny: Any?): String {
|
||||
return "$maybeStr$maybeAny"
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_extension#generatedPlusExtensionString
|
||||
// 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-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun generatedPlusExtensionString(maybeStr: String?, str: String): String {
|
||||
return "$maybeStr$str"
|
||||
}
|
||||
|
||||
data class Foo(val bar: Int)
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
val foo = Foo(42)
|
||||
println(manualPlusExtensionAny("foo", foo))
|
||||
println(manualPlusExtensionAny(null, null))
|
||||
println(manualPlusExtensionString("foo", "bar"))
|
||||
println(manualPlusExtensionString(null, "bar"))
|
||||
println(generatedPlusExtensionAny("foo", foo))
|
||||
println(generatedPlusExtensionAny(null, null))
|
||||
println(generatedPlusExtensionString("foo", "bar"))
|
||||
println(generatedPlusExtensionString(null, "bar"))
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fooFoo(bar=42)
|
||||
nullnull
|
||||
foobar
|
||||
nullbar
|
||||
fooFoo(bar=42)
|
||||
nullnull
|
||||
foobar
|
||||
nullbar
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package codegen.stringConcatenationTypeNarrowing.kt53119_plus_generated_noescape
|
||||
import kotlin.test.*
|
||||
import kotlin.random.*
|
||||
|
||||
// Without "string concatenation type narrowing", pointer to IntRange is conservatively considered to escape,
|
||||
// since within String.plus(Any?) (within Random.nextInt), the pointer is passed as a receiver of virtual call Any.toString()
|
||||
// As a result, no stack allocation is possible
|
||||
|
||||
// With "string concatenation type narrowing", IntRange.toString() is explicitly called, that does not escape the receiver.
|
||||
// Hence, escape analysis can find out the pointer to IntRange does not escape anywhere, and stack allocation is possible.
|
||||
|
||||
// CHECK-LABEL: define i32 @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_generated_noescape#getNextInt(){}kotlin.Int
|
||||
// CHECK-NOT ret i32
|
||||
// CHECK alloca %"kclassbody:kotlin.ranges.IntRange#internal"
|
||||
|
||||
// CHECK ret i32
|
||||
|
||||
fun getNextInt(): Int {
|
||||
return Random.nextInt(IntRange(42153, 42153))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
println(getNextInt())
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
42153
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package codegen.stringConcatenationTypeNarrowing.kt53119_plus_member
|
||||
import kotlin.test.*
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#manualPlusMemberAny
|
||||
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
// 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-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun manualPlusMemberAny(str: String, maybeAny: kotlin.Any?): kotlin.String =
|
||||
str + maybeAny
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#manualPlusMemberString
|
||||
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK-NOT: call %struct.ObjHeader* @"kfun:kotlin.String#toString(){}kotlin.String"
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun manualPlusMemberString(str1: String, str2: String): kotlin.String =
|
||||
str1 + str2
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#generatedPlusMemberAny
|
||||
// CHECK-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: call %struct.ObjHeader* @Kotlin_String_plusImpl
|
||||
// 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-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun generatedPlusMemberAny(str: String, maybeAny: Any?): String {
|
||||
return "$str$maybeAny"
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define %struct.ObjHeader* @"kfun:codegen.stringConcatenationTypeNarrowing.kt53119_plus_member#generatedPlusMemberString
|
||||
// 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-NOT: kfun:kotlin.String#plus(kotlin.Any?)
|
||||
|
||||
// CHECK: ret %struct.ObjHeader*
|
||||
|
||||
fun generatedPlusMemberString(str1: String, str2: String): String {
|
||||
return "$str1$str2"
|
||||
}
|
||||
|
||||
data class Foo(val bar: Int)
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
val foo = Foo(42)
|
||||
println(manualPlusMemberAny("foo", foo))
|
||||
println(manualPlusMemberAny("foo", null))
|
||||
println(manualPlusMemberString("foo", "bar"))
|
||||
println(generatedPlusMemberAny("foo", null))
|
||||
println(generatedPlusMemberAny("foo", foo))
|
||||
println(generatedPlusMemberString("foo", "bar"))
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fooFoo(bar=42)
|
||||
foonull
|
||||
foobar
|
||||
foonull
|
||||
fooFoo(bar=42)
|
||||
foobar
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
// The same tests are also tested with FileCheck in folder ../../filecheck/kt53119_*.kt
|
||||
// Please keep them in sync
|
||||
|
||||
package codegen.stringConcatenationTypeNarrowing.kt53119_side_effect
|
||||
import kotlin.test.*
|
||||
|
||||
fun withSideEffect(): String? {
|
||||
println("Side effect")
|
||||
return "Result."
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
println("Retval is: " + withSideEffect())
|
||||
println("Retval is: " + withSideEffect() + "!")
|
||||
println(withSideEffect() + withSideEffect())
|
||||
println(withSideEffect() + withSideEffect() + withSideEffect())
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
Side effect
|
||||
Retval is: Result.
|
||||
Side effect
|
||||
Retval is: Result.!
|
||||
Side effect
|
||||
Side effect
|
||||
Result.Result.
|
||||
Side effect
|
||||
Side effect
|
||||
Side effect
|
||||
Result.Result.Result.
|
||||
Reference in New Issue
Block a user