diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index a55ea132cde..ac1a84865c1 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -895,6 +895,24 @@ public abstract class KotlinBuiltIns { return isNotNullConstructedFromGivenClass(type, FqNames.unit); } + /** + * Returns true if the descriptor's return type is not Unit, + * or it overrides a function with a non-Unit return type. + */ + public static boolean mayReturnNonUnitValue(@NotNull FunctionDescriptor descriptor) { + KotlinType functionReturnType = descriptor.getReturnType(); + assert functionReturnType != null : "Function return typed type must be resolved."; + boolean mayReturnNonUnitValue = !isUnit(functionReturnType); + for (FunctionDescriptor overriddenDescriptor : descriptor.getOriginal().getOverriddenDescriptors()) { + if (mayReturnNonUnitValue) + break; + KotlinType overriddenFunctionReturnType = overriddenDescriptor.getReturnType(); + assert overriddenFunctionReturnType != null : "Function return typed type must be resolved."; + mayReturnNonUnitValue = !isUnit(overriddenFunctionReturnType); + } + return mayReturnNonUnitValue; + } + public static boolean isUnitOrNullableUnit(@NotNull KotlinType type) { return isConstructedFromGivenClass(type, FqNames.unit); } diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index 5066e07a241..bbf563280f3 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -750,6 +750,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/coercion/unitIsAs.kt"); } + @Test + @TestMetadata("unitMaterializationInOverriddenMethod.kt") + public void testUnitMaterializationInOverriddenMethod() throws Exception { + runTest("js/js.translator/testData/box/coercion/unitMaterializationInOverriddenMethod.kt"); + } + @Test @TestMetadata("unitNullCheck.kt") public void testUnitNullCheck() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index 9ca146b551f..e53705715d0 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -750,6 +750,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/coercion/unitIsAs.kt"); } + @Test + @TestMetadata("unitMaterializationInOverriddenMethod.kt") + public void testUnitMaterializationInOverriddenMethod() throws Exception { + runTest("js/js.translator/testData/box/coercion/unitMaterializationInOverriddenMethod.kt"); + } + @Test @TestMetadata("unitNullCheck.kt") public void testUnitNullCheck() throws Exception { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 7d26e0574b3..e2408bd3e72 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -160,7 +160,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { JsReturn jsReturn; if (returned == null) { - jsReturn = new JsReturn(null); + JsExpression returnExpression = null; + if (returnTarget != null && KotlinBuiltIns.mayReturnNonUnitValue(returnTarget)) { + ClassDescriptor unit = context.getCurrentModule().getBuiltIns().getUnit(); + returnExpression = ReferenceTranslator.translateAsValueReference(unit, context); + } + jsReturn = new JsReturn(returnExpression); } else { JsExpression jsReturnExpression = translateAsExpression(returned, context); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java index 1ce21614af7..76c2d64f64b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java @@ -161,9 +161,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator { } private boolean mustAddReturnToGeneratedFunctionBody() { - KotlinType functionReturnType = descriptor.getReturnType(); - assert functionReturnType != null : "Function return typed type must be resolved."; - return (!declaration.hasBlockBody()) && !(KotlinBuiltIns.isUnit(functionReturnType) && !descriptor.isSuspend()); + return !declaration.hasBlockBody() && (KotlinBuiltIns.mayReturnNonUnitValue(descriptor) || descriptor.isSuspend()); } @NotNull diff --git a/js/js.translator/testData/box/coercion/unitMaterializationInOverriddenMethod.kt b/js/js.translator/testData/box/coercion/unitMaterializationInOverriddenMethod.kt new file mode 100644 index 00000000000..3d515de50ff --- /dev/null +++ b/js/js.translator/testData/box/coercion/unitMaterializationInOverriddenMethod.kt @@ -0,0 +1,24 @@ +// EXPECTED_REACHABLE_NODES: 1279 +// KT-51878 + +open class A { + open fun foo(): Any = "string" +} + +var condition = true + +class B: A() { + override fun foo(): Unit { + if (condition) { + return + } + condition = !condition + } +} + +fun box(): String { + val b: A = B() + if (b.foo() !is Unit) return "fail1" + if (b.foo() !is Unit) return "fail2" + return "OK" +}