Use non-local return target instead of inline site in suspend function

return type coercion.
 #KT-43226 Fixed
This commit is contained in:
Ilmir Usmanov
2020-11-09 21:19:18 +01:00
parent d4f08018ce
commit fa42a6ba58
12 changed files with 165 additions and 33 deletions
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.config.ApiVersion;
import org.jetbrains.kotlin.config.JVMAssertionsMode;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor;
@@ -1705,26 +1706,38 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Type returnType;
KotlinType returnKotlinType;
if (isNonLocalReturn) {
// This is inline lambda. Find inline-site and check, whether it is suspend functions returning unboxed inline class
CodegenContext<?> inlineSiteContext = this.context.getFirstCrossInlineOrNonInlineContext();
KotlinType originalInlineClass = null;
boolean invokeSuspendOfLambda = false;
FunctionDescriptor inlineSiteDescriptor = null;
if (inlineSiteContext instanceof MethodContext) {
inlineSiteDescriptor = ((MethodContext) inlineSiteContext).getFunctionDescriptor();
originalInlineClass = CoroutineCodegenUtilKt
.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass(inlineSiteDescriptor, typeMapper);
invokeSuspendOfLambda = CoroutineCodegenUtilKt.isInvokeSuspendOfLambda(inlineSiteDescriptor);
}
if (originalInlineClass != null) {
returnType = typeMapper.mapType(originalInlineClass);
returnKotlinType = originalInlineClass;
} else if (!invokeSuspendOfLambda) {
returnType = nonLocalReturn.returnType.getType();
returnKotlinType = nonLocalReturn.returnType.getKotlinType();
} else {
FunctionDescriptor returnTarget =
nonLocalReturn.descriptor instanceof FunctionDescriptor
? (FunctionDescriptor) nonLocalReturn.descriptor
: null;
if (returnTarget == null || !returnTarget.isSuspend()) {
JvmKotlinType jvmKotlinType = nonLocalReturn.getJvmKotlinType(typeMapper);
returnType = jvmKotlinType.getType();
returnKotlinType = jvmKotlinType.getKotlinType();
} else if (returnTarget instanceof AnonymousFunctionDescriptor) {
// Suspend lambdas always return Any?
returnType = OBJECT_TYPE;
returnKotlinType = inlineSiteDescriptor.getReturnType();
returnKotlinType = state.getModule().getBuiltIns().getNullableAnyType();
} else {
// This is inline lambda, but return target is ordinary, yet suspend, function.
// Find inline-site and check, whether it is suspend functions returning unboxed inline class
CodegenContext<?> inlineSiteContext = this.context.getFirstCrossInlineOrNonInlineContext();
KotlinType originalInlineClass = null;
if (inlineSiteContext instanceof MethodContext) {
FunctionDescriptor view = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(returnTarget, state);
originalInlineClass =
CoroutineCodegenUtilKt.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass(view, typeMapper);
}
if (originalInlineClass != null) {
// As an optimization, suspend functions, returning inline classes with reference underlying
// type return unboxed inline class. Save the type so the coercer will not box it.
returnType = typeMapper.mapType(originalInlineClass);
returnKotlinType = originalInlineClass;
} else {
JvmKotlinType jvmKotlinType = nonLocalReturn.getJvmKotlinType(typeMapper);
returnType = jvmKotlinType.getType();
returnKotlinType = jvmKotlinType.getKotlinType();
}
}
}
else {
@@ -1788,13 +1801,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
FunctionDescriptor containingFunction =
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
//FIRST_FUN_LABEL to prevent clashing with existing labels
return new NonLocalReturnInfo(
new JvmKotlinType(
typeMapper.mapReturnType(containingFunction),
containingFunction.getReturnType()
),
FIRST_FUN_LABEL
);
return new NonLocalReturnInfo(containingFunction, FIRST_FUN_LABEL);
} else {
//local
return null;
@@ -1807,10 +1814,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
assert element != null : "Expression should be not null " + expression.getText();
assert elementDescriptor != null : "Descriptor should be not null: " + element.getText();
CallableDescriptor function = (CallableDescriptor) elementDescriptor;
return new NonLocalReturnInfo(
new JvmKotlinType(typeMapper.mapReturnType(function), function.getReturnType()),
expression.getLabelName()
);
return new NonLocalReturnInfo(function, expression.getLabelName());
}
}
return null;
@@ -5427,14 +5431,18 @@ The "returned" value of try expression with no finally is either the last expres
private static class NonLocalReturnInfo {
private final JvmKotlinType returnType;
private final CallableDescriptor descriptor;
private final String labelName;
private NonLocalReturnInfo(@NotNull JvmKotlinType type, @NotNull String name) {
returnType = type;
private NonLocalReturnInfo(@NotNull CallableDescriptor descriptor, @NotNull String name) {
this.descriptor = descriptor;
labelName = name;
}
private JvmKotlinType getJvmKotlinType(@NotNull KotlinTypeMapper typeMapper) {
return new JvmKotlinType(typeMapper.mapReturnType(descriptor), descriptor.getReturnType());
}
}
@NotNull
@@ -6883,6 +6883,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines");
@@ -60,6 +60,18 @@ class Test3 {
suspend fun test() = bar().s
}
class Test4 {
suspend fun <T> foo(value: T): T = value
suspend fun bar(): IC? {
run {
return foo(IC("OK"))
}
}
suspend fun test() = bar()!!.s
}
fun box(): String {
var result = "FAIL"
@@ -83,5 +95,13 @@ fun box(): String {
result = Test3().test()
}
if (result != "OK") return "FAIL 3 $result"
result = "FAIL 4"
builder {
result = Test4().test()
}
return result
}
@@ -67,6 +67,18 @@ class Test3 {
suspend fun test() = bar().s
}
class Test4 {
suspend fun <T> foo(value: T): T = value
suspend fun bar(): IC? {
run {
return foo(suspendMe())
}
}
suspend fun test() = bar()!!.s
}
fun box(): String {
var result = "FAIL"
@@ -93,5 +105,14 @@ fun box(): String {
}
c?.resume(IC("OK"))
if (result != "OK") return "FAIL 3 $result"
result = "FAIL 4"
builder {
result = Test4().test()
}
c?.resume(IC("OK"))
return result
}
@@ -71,6 +71,18 @@ class Test3 {
suspend fun test() = bar().s
}
class Test4 {
suspend fun <T> foo(value: T): T = value
suspend fun bar(): IC? {
run {
return foo(suspendMe())
}
}
suspend fun test() = bar()!!.s
}
fun box(): String {
builder {
Test1().test()
@@ -95,5 +107,12 @@ fun box(): String {
}
c?.resumeWithException(IllegalStateException("OK"))
if (result != "OK") return "FAIL 3 $result"
builder {
Test4().test()
}
c?.resumeWithException(IllegalStateException("OK"))
return result
}
@@ -0,0 +1,29 @@
// KJS_WITH_FULL_RUNTIME
// WITH_RUNTIME
import kotlin.coroutines.*
suspend fun coroutineScope(c: suspend () -> Unit) {
c()
}
var counter = 0
suspend fun whatever() = coroutineScope {
repeat(10) { // repeat hides a loop, that plays a part in the compiler crash
run {
counter++
return@repeat // required to reproduce the crash
}
}
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(Continuation(EmptyCoroutineContext) {})
}
fun box(): String {
builder {
whatever()
}
return if (counter != 10) "FAIL $counter" else "OK"
}
@@ -7208,6 +7208,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines.experimental");
@@ -7208,6 +7208,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines.experimental");
@@ -6883,6 +6883,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines");
@@ -5723,6 +5723,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines");
@@ -5723,6 +5723,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines");
@@ -5723,6 +5723,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt", "kotlin.coroutines");
}
@TestMetadata("nonLocalReturn.kt")
public void testNonLocalReturn() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/nonLocalReturn.kt");
}
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
public void testNonLocalReturnFromInlineLambdaDeep_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt", "kotlin.coroutines");