diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt index 11c18844433..5f1549fc79d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt @@ -7,15 +7,13 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder -import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.name.Name class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransformer { @@ -47,10 +45,12 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo } irBuiltIns.booleanType.let { - add(it, OperatorNames.AND, intrinsics.jsBitAnd) - add(it, OperatorNames.OR, intrinsics.jsBitOr) + // These operators are not short-circuit -- using bitwise operators '&', '|', '^' followed by coercion to boolean + add(it, OperatorNames.AND) { call -> toBoolean(irCall(call, intrinsics.jsBitAnd, dispatchReceiverAsFirstArgument = true)) } + add(it, OperatorNames.OR) { call -> toBoolean(irCall(call, intrinsics.jsBitOr, dispatchReceiverAsFirstArgument = true)) } + add(it, OperatorNames.XOR) { call -> toBoolean(irCall(call, intrinsics.jsBitXor, dispatchReceiverAsFirstArgument = true)) } + add(it, OperatorNames.NOT, intrinsics.jsNot) - add(it, OperatorNames.XOR, intrinsics.jsBitXor) } for (type in primitiveNumbers) { @@ -231,6 +231,14 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo } } + private fun booleanNegate(e: IrExpression) = + JsIrBuilder.buildCall(intrinsics.jsNot, irBuiltIns.booleanType).apply { + putValueArgument(0, e) + } + + private fun toBoolean(e: IrExpression) = + booleanNegate(booleanNegate(e)) + private fun toInt32(e: IrExpression) = JsIrBuilder.buildCall(intrinsics.jsBitOr, irBuiltIns.intType).apply { putValueArgument(0, e) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index d96ba2ace56..4f63c9f7117 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -6791,6 +6791,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/typeChecks"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("booleanOperatorsTypes.kt") + public void testBooleanOperatorsTypes() throws Exception { + runTest("js/js.translator/testData/box/regression/typeChecks/booleanOperatorsTypes.kt"); + } + @TestMetadata("emptyVarargInConstructorCall.kt") public void testEmptyVarargInConstructorCall() throws Exception { runTest("js/js.translator/testData/box/regression/typeChecks/emptyVarargInConstructorCall.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 329eef01a9d..f0420b053d5 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -6826,6 +6826,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/typeChecks"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("booleanOperatorsTypes.kt") + public void testBooleanOperatorsTypes() throws Exception { + runTest("js/js.translator/testData/box/regression/typeChecks/booleanOperatorsTypes.kt"); + } + @TestMetadata("emptyVarargInConstructorCall.kt") public void testEmptyVarargInConstructorCall() throws Exception { runTest("js/js.translator/testData/box/regression/typeChecks/emptyVarargInConstructorCall.kt"); diff --git a/js/js.translator/testData/box/regression/typeChecks/booleanOperatorsTypes.kt b/js/js.translator/testData/box/regression/typeChecks/booleanOperatorsTypes.kt new file mode 100644 index 00000000000..8e0ace4bafd --- /dev/null +++ b/js/js.translator/testData/box/regression/typeChecks/booleanOperatorsTypes.kt @@ -0,0 +1,28 @@ +// IGNORE_BACKEND: JS +// EXPECTED_REACHABLE_NODES: 1281 + +package foo + +fun check(x: Any?) { + x as Boolean +} + +fun tests(x: Boolean, y: Boolean) { + check(x) + check(!x) + check(x or y) + check(x and y) + check(x xor y) + check(x.not()) + check(x || y) + check(x && y) +} + +fun box(): String { + tests(false, false) + tests(false, true) + tests(true, false) + tests(true, true) + + return "OK" +} \ No newline at end of file