KT-6646, KT-10482:
when a method (or a property getter) returns Nothing, emit ACONST_NULL ATHROW after a call so that class files verifier knows that this is an exit point in a method. Note that if an inline method returning Nothing throws an exception explicitly (or via a chain of inline methods), this code will be deleted by DCE.
This commit is contained in:
@@ -2420,6 +2420,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
||||||
|
|
||||||
|
KotlinType returnType = resolvedCall.getResultingDescriptor().getReturnType();
|
||||||
|
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||||
|
v.aconst(null);
|
||||||
|
v.athrow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import kotlin.jvm.functions.Function1;
|
|||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty;
|
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty;
|
||||||
@@ -45,6 +46,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||||
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor;
|
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor;
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
import org.jetbrains.org.objectweb.asm.Label;
|
import org.jetbrains.org.objectweb.asm.Label;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||||
@@ -1086,6 +1088,12 @@ public abstract class StackValue {
|
|||||||
else {
|
else {
|
||||||
getter.genInvokeInstruction(v);
|
getter.genInvokeInstruction(v);
|
||||||
coerce(getter.getReturnType(), type, v);
|
coerce(getter.getReturnType(), type, v);
|
||||||
|
|
||||||
|
KotlinType returnType = descriptor.getReturnType();
|
||||||
|
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||||
|
v.aconst(null);
|
||||||
|
v.athrow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
var flag = true
|
||||||
|
|
||||||
|
fun exit(): Nothing = null!!
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
if (flag) {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
inline fun exit(): Nothing = null!!
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
try {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
var flag = true
|
||||||
|
|
||||||
|
object Test {
|
||||||
|
val magic: Nothing get() = null!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
if (flag) {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Test.magic
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fun exit(): Nothing = null!!
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
try {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
fun exit(): Nothing = null!!
|
||||||
|
|
||||||
|
var x = 0
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
when (x) {
|
||||||
|
0 -> a = "OK"
|
||||||
|
1 -> a = "???"
|
||||||
|
2 -> exit()
|
||||||
|
else -> exit()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// NB '!!' uses Intrinsics.throwNpe()
|
||||||
|
inline fun exit(): Nothing = null!!
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
try {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
exit()
|
||||||
|
// ATHROW
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 ATHROW
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
inline fun exit(): Nothing =
|
||||||
|
throw RuntimeException() // ATHROW
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
try {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
exit() // ATHROW inlined
|
||||||
|
// no ATHROW (removed as dead code)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 ATHROW
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
inline fun exit(): Nothing = null!!
|
||||||
|
inline fun exita(): Nothing = exit() // ATHROW
|
||||||
|
inline fun exitb(): Nothing = exita() // ATHROW
|
||||||
|
inline fun exitc(): Nothing = exitb() // ATHROW
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val a: String
|
||||||
|
try {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
exitc() // ATHROW
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 ATHROW
|
||||||
@@ -706,6 +706,24 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/inline"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/inline"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineReturnsNothing1.kt")
|
||||||
|
public void testInlineReturnsNothing1() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing1.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineReturnsNothing2.kt")
|
||||||
|
public void testInlineReturnsNothing2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineReturnsNothing3.kt")
|
||||||
|
public void testInlineReturnsNothing3() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/inlineReturnsNothing3.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("noSynAccessor.kt")
|
@TestMetadata("noSynAccessor.kt")
|
||||||
public void testNoSynAccessor() throws Exception {
|
public void testNoSynAccessor() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/noSynAccessor.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/noSynAccessor.kt");
|
||||||
|
|||||||
+39
@@ -2422,6 +2422,45 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/controlStructures/returnsNothing")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ReturnsNothing extends AbstractBlackBoxCodegenTest {
|
||||||
|
public void testAllFilesPresentInReturnsNothing() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/controlStructures/returnsNothing"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifElse.kt")
|
||||||
|
public void testIfElse() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/returnsNothing/ifElse.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineMethod.kt")
|
||||||
|
public void testInlineMethod() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/returnsNothing/inlineMethod.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyGetter.kt")
|
||||||
|
public void testPropertyGetter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/returnsNothing/propertyGetter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("tryCatch.kt")
|
||||||
|
public void testTryCatch() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/returnsNothing/tryCatch.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("when.kt")
|
||||||
|
public void testWhen() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/returnsNothing/when.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
|
@TestMetadata("compiler/testData/codegen/box/controlStructures/tryCatchInExpressions")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user