diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index 5868555a964..d83b5c8b0c2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -280,13 +280,13 @@ public final class PatternTranslator extends AbstractTranslator { EqualityType matchEquality = equalityType(subjectType); EqualityType patternEquality = equalityType(patternType); - JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression); + JsExpression expressionToMatchAgainst = TranslationUtils.coerce(context(), translateExpressionForExpressionPattern(patternExpression), subjectType); if (matchEquality == EqualityType.PRIMITIVE && patternEquality == EqualityType.PRIMITIVE) { return equality(expressionToMatch, expressionToMatchAgainst); } else if (expressionToMatchAgainst instanceof JsNullLiteral) { - return TranslationUtils.nullCheck(expressionToMatch, false); + return TranslationUtils.nullCheck(subjectExpression, expressionToMatch, context(), false); } else { return TopLevelFIF.KOTLIN_EQUALS.apply(expressionToMatch, Collections.singletonList(expressionToMatchAgainst), context()); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt index 91d6ec8988b..aff6f626bfc 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken +import org.jetbrains.kotlin.js.translate.utils.PsiUtils.isNegatedOperation import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBinaryExpression @@ -36,7 +37,6 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.isDynamic -import org.jetbrains.kotlin.types.typeUtil.makeNullable import java.util.* object EqualsBOIF : BinaryOperationIntrinsicFactory { @@ -49,9 +49,7 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory { val anyType = context.currentModule.builtIns.anyType if (right is JsNullLiteral || left is JsNullLiteral) { val (subject, ktSubject) = if (right is JsNullLiteral) Pair(left, expression.left!!) else Pair(right, expression.right!!) - val type = context.bindingContext().getType(ktSubject) ?: anyType - val coercedSubject = TranslationUtils.coerce(context, subject, type.makeNullable()) - return TranslationUtils.nullCheck(coercedSubject, isNegated) + return TranslationUtils.nullCheck(ktSubject, subject, context, isNegatedOperation(expression)) } val (leftKotlinType, rightKotlinType) = binaryOperationTypes(expression, context) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java index 41718b774dd..deda273fb6e 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.isAssignment; +import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.coerce; import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.isSimpleNameExpressionNotDelegatedLocalVar; public final class IntrinsicAssignmentTranslator extends AssignmentTranslator { @@ -62,6 +63,7 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator { JsExpression result = TranslationUtils.translateRightExpression(context, expression, rightBlock); KotlinType leftType = context.bindingContext().getType(expression.getLeft()); KotlinType rightType = context.bindingContext().getType(expression.getRight()); + if (rightType != null && KotlinBuiltIns.isCharOrNullableChar(rightType)) { if (leftType != null && KotlinBuiltIns.isStringOrNullableString(leftType)) { result = JsAstUtils.charToString(result); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java index 7ce30ab393d..2ae6350a8e2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java @@ -54,7 +54,7 @@ public final class UnaryOperationTranslator { if (operationToken == KtTokens.EXCLEXCL) { KtExpression baseExpression = getBaseExpression(expression); JsExpression translatedExpression = translateAsExpression(baseExpression, context); - return sure(translatedExpression, context); + return sure(baseExpression, translatedExpression, context); } if (operationToken == KtTokens.MINUS) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index 647346869be..12380331e5a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -50,6 +50,7 @@ import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; import org.jetbrains.kotlin.types.DynamicTypesKt; import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.kotlin.types.TypeUtils; import java.util.*; import java.util.stream.Collectors; @@ -157,6 +158,30 @@ public final class TranslationUtils { return new JsBinaryOperation(operator, expressionToCheck, new JsNullLiteral()); } + @NotNull + private static JsExpression prepareForNullCheck( + @NotNull KtExpression ktSubject, + @NotNull JsExpression expression, + @NotNull TranslationContext context + ) { + KotlinType type = context.bindingContext().getType(ktSubject); + if (type == null) { + type = context.getCurrentModule().getBuiltIns().getAnyType(); + } + + return coerce(context, expression, TypeUtils.makeNullable(type)); + } + + @NotNull + public static JsBinaryOperation nullCheck( + @NotNull KtExpression ktSubject, + @NotNull JsExpression expressionToCheck, + @NotNull TranslationContext context, + boolean isNegated + ) { + return nullCheck(prepareForNullCheck(ktSubject, expressionToCheck, context), isNegated); + } + @NotNull public static JsConditional notNullConditional( @NotNull JsExpression expression, @@ -310,8 +335,9 @@ public final class TranslationUtils { } @NotNull - public static JsExpression sure(@NotNull JsExpression expression, @NotNull TranslationContext context) { - return new JsInvocation(context.getReferenceToIntrinsic(Namer.NULL_CHECK_INTRINSIC_NAME), expression); + public static JsExpression sure(@NotNull KtExpression ktExpression, @NotNull JsExpression expression, @NotNull TranslationContext context) { + return new JsInvocation(context.getReferenceToIntrinsic(Namer.NULL_CHECK_INTRINSIC_NAME), + prepareForNullCheck(ktExpression, expression, context)); } public static boolean isSimpleNameExpressionNotDelegatedLocalVar(@Nullable KtExpression expression, @NotNull TranslationContext context) { @@ -491,9 +517,7 @@ public final class TranslationUtils { } else if (KotlinBuiltIns.isUnit(from)) { if (!KotlinBuiltIns.isUnit(to) && !MetadataProperties.isUnit(value)) { - ClassDescriptor unit = context.getCurrentModule().getBuiltIns().getUnit(); - JsExpression unitRef = ReferenceTranslator.translateAsValueReference(unit, context); - value = JsAstUtils.newSequence(Arrays.asList(value, unitRef)); + value = voidToUnit(context, value); } } @@ -501,6 +525,13 @@ public final class TranslationUtils { return value; } + @NotNull + public static JsExpression voidToUnit(@NotNull TranslationContext context, @NotNull JsExpression expression) { + ClassDescriptor unit = context.getCurrentModule().getBuiltIns().getUnit(); + JsExpression unitRef = ReferenceTranslator.translateAsValueReference(unit, context); + return JsAstUtils.newSequence(Arrays.asList(expression, unitRef)); + } + @NotNull private static JsExpression unitToVoid(@NotNull JsExpression expression) { if (expression instanceof JsBinaryOperation) { diff --git a/js/js.translator/testData/box/coercion/unitNullCheck.kt b/js/js.translator/testData/box/coercion/unitNullCheck.kt index d2b184af71a..2b18e9c0885 100644 --- a/js/js.translator/testData/box/coercion/unitNullCheck.kt +++ b/js/js.translator/testData/box/coercion/unitNullCheck.kt @@ -1,18 +1,26 @@ // EXPECTED_REACHABLE_NODES: 1132 var log = "" -fun box(): String { - foo() ?: bar() - if (foo() == null) bar() +fun log(msg: String) { + log += "$msg;" +} + +fun box(): String { + log("!!")!! + + log("elvis-left") ?: log("elvis-right") + + if (log("if") == null) log("then") + + when (null) { + log("when-pattern") -> log("when-body-1") + } + + when (log("when-subject")) { + null -> log("when-body-2") + } + + if (log != "!!;elvis-left;if;when-pattern;when-subject;") return "fail: $log" - if (log != "foo;foo;") return "fail: $log" return "OK" } - -fun foo() { - log += "foo;" -} - -fun bar() { - log += "bar;" -}