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"
+}