diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/captureSubjectVariable.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/captureSubjectVariable.kt index 8cf748b3574..e49a259c80d 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/captureSubjectVariable.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/captureSubjectVariable.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject -// IGNORE_BACKEND: JS fun box(): String { var y: String = "OK" diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/denseIntSwitchWithSubjectVariable.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/denseIntSwitchWithSubjectVariable.kt index 8b161b99c9d..c5ed7e0eab6 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/denseIntSwitchWithSubjectVariable.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/denseIntSwitchWithSubjectVariable.kt @@ -1,6 +1,6 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject // WITH_RUNTIME -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS_IR fun dense(x: Int): Int { return when (val xx = x) { diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/equalityWithSubjectVariable.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/equalityWithSubjectVariable.kt index ffaea5b65a4..a1ac6d5b8ba 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/equalityWithSubjectVariable.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/equalityWithSubjectVariable.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject -// IGNORE_BACKEND: JS val x = 1 diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754Equality.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754Equality.kt index 860fbf9aa41..70dcdbabe43 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754Equality.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754Equality.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject -// IGNORE_BACKEND: JS val dz = -0.0 val fz = -0.0f @@ -29,5 +28,14 @@ fun box(): String { } } + testDoubleAsUpperBound(-0.0) + return "OK" -} \ No newline at end of file +} + +fun testDoubleAsUpperBound(v: T): Boolean { + return when (val a = v*v) { + 0.0 -> true + else -> throw AssertionError() + } +} diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754EqualityWithSmartCast.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754EqualityWithSmartCast.kt index a213c827604..71015f020aa 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754EqualityWithSmartCast.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/ieee754EqualityWithSmartCast.kt @@ -1,22 +1,32 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject +ProperIeee754Comparisons // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS_IR val az: Any = -0.0 val afz: Any = -0.0f fun box(): String { - when (val y = az) { + val y = az + when (y) { !is Double -> throw AssertionError() 0.0 -> {} else -> throw AssertionError() } - - when (val y = afz) { + val yy = afz + when (yy) { !is Float -> throw AssertionError() 0.0 -> {} else -> throw AssertionError() } + testDoubleAsUpperBound(-0.0) + return "OK" -} \ No newline at end of file +} + +fun testDoubleAsUpperBound(v: T): Boolean { + return when (val a = v*v) { + 0.0 -> true + else -> throw AssertionError() + } +} diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/isCheckOnSubjectVariable.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/isCheckOnSubjectVariable.kt index 0d25c5f3d56..762b9a79f0a 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/isCheckOnSubjectVariable.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/isCheckOnSubjectVariable.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject -// IGNORE_BACKEND: JS val x: Any = 1 diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/sparseIntSwitchWithSubjectVariable.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/sparseIntSwitchWithSubjectVariable.kt index 4bc4aaa5bb0..5e8be00bc84 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/sparseIntSwitchWithSubjectVariable.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/sparseIntSwitchWithSubjectVariable.kt @@ -1,6 +1,6 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject // WITH_RUNTIME -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS_IR fun sparse(x: Int): Int { return when (val xx = (x % 4) * 100) { diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/subjectExpressionIsEvaluatedOnce.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/subjectExpressionIsEvaluatedOnce.kt index 0da0c54b834..60c515dbf22 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/subjectExpressionIsEvaluatedOnce.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/subjectExpressionIsEvaluatedOnce.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject -// IGNORE_BACKEND: JS var effectCount = 0 diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/whenByEnum.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/whenByEnum.kt index cad3744fcbb..7320f4dc6a8 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/whenByEnum.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/whenByEnum.kt @@ -1,6 +1,6 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject // WITH_RUNTIME -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS_IR import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/whenByNullableEnum.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/whenByNullableEnum.kt index 7777a4888e2..f684ddc4bc6 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/whenByNullableEnum.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/whenByNullableEnum.kt @@ -1,6 +1,6 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject // WITH_RUNTIME -// IGNORE_BACKEND: JS, JS_IR +// IGNORE_BACKEND: JS_IR import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/when/whenSubjectVariable/whenByString.kt b/compiler/testData/codegen/box/when/whenSubjectVariable/whenByString.kt index 5d1a1a14044..c0755fafa3a 100644 --- a/compiler/testData/codegen/box/when/whenSubjectVariable/whenByString.kt +++ b/compiler/testData/codegen/box/when/whenSubjectVariable/whenByString.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +VariableDeclarationInWhenSubject // WITH_RUNTIME -// IGNORE_BACKEND: JS import kotlin.test.assertEquals 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 13b84fe8b81..e8c480f141d 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 @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.js.translate.expression; @@ -36,10 +25,7 @@ import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelF import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; import org.jetbrains.kotlin.js.translate.utils.*; import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS; -import org.jetbrains.kotlin.psi.KtExpression; -import org.jetbrains.kotlin.psi.KtIsExpression; -import org.jetbrains.kotlin.psi.KtTypeReference; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo; import org.jetbrains.kotlin.types.KotlinType; @@ -270,31 +256,32 @@ public final class PatternTranslator extends AbstractTranslator { @NotNull public JsExpression translateExpressionPattern( - @NotNull KtExpression subjectExpression, + @NotNull KotlinType subjectType, @NotNull JsExpression expressionToMatch, @NotNull KtExpression patternExpression ) { PrimitiveNumericComparisonInfo ieeeInfo = UtilsKt.getPrimitiveNumericComparisonInfo(context(), patternExpression); - KotlinType subjectType, patternType; + KotlinType actualSubjectType; + KotlinType patternType; if (ieeeInfo != null) { - subjectType = ieeeInfo.getLeftType(); + actualSubjectType = ieeeInfo.getLeftType(); patternType = ieeeInfo.getRightType(); } else { - subjectType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), subjectExpression); + actualSubjectType = UtilsKt.refineType(subjectType); patternType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), patternExpression); } - EqualityType matchEquality = equalityType(subjectType); + EqualityType matchEquality = equalityType(actualSubjectType); EqualityType patternEquality = equalityType(patternType); - JsExpression expressionToMatchAgainst = TranslationUtils.coerce(context(), translateExpressionForExpressionPattern(patternExpression), subjectType); + JsExpression expressionToMatchAgainst = TranslationUtils.coerce(context(), translateExpressionForExpressionPattern(patternExpression), actualSubjectType); if (matchEquality == EqualityType.PRIMITIVE && patternEquality == EqualityType.PRIMITIVE) { return equality(expressionToMatch, expressionToMatchAgainst); } else if (expressionToMatchAgainst instanceof JsNullLiteral) { - return TranslationUtils.nullCheck(subjectExpression, expressionToMatch, context(), false); + return TranslationUtils.nullCheck(actualSubjectType, 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/expression/WhenTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.kt index b969b76438f..6dadc750488 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.js.translate.expression @@ -19,6 +8,7 @@ package org.jetbrains.kotlin.js.translate.expression import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.js.backend.ast.* @@ -27,6 +17,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.operation.InOperationTranslator +import org.jetbrains.kotlin.js.translate.utils.BindingUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator @@ -49,6 +40,7 @@ private typealias EntryWithConstants = Pair, KtWhenEntry> class WhenTranslator private constructor(private val whenExpression: KtWhenExpression, context: TranslationContext) : AbstractTranslator(context) { + private val subjectType: KotlinType? private val expressionToMatch: JsExpression? private val type: KotlinType? private val uniqueConstants = mutableSetOf() @@ -63,8 +55,27 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans } init { - val subject = whenExpression.subjectExpression - expressionToMatch = if (subject != null) context.defineTemporary(Translation.translateAsExpression(subject, context)) else null + val subjectVariable = whenExpression.subjectVariable + val subjectExpression = whenExpression.subjectExpression + + when { + subjectVariable != null -> { + val variable = Translation.translateAsStatement(subjectVariable, context) as JsVars + context.addStatementToCurrentBlock(variable) + + val descriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), subjectVariable) as? CallableDescriptor + subjectType = descriptor?.returnType + expressionToMatch = variable.vars.first().name.makeRef() + } + subjectExpression != null -> { + subjectType = bindingContext().getType(subjectExpression) + expressionToMatch = context.defineTemporary(Translation.translateAsExpression(subjectExpression, context)) + } + else -> { + subjectType = null + expressionToMatch = null + } + } type = bindingContext().getType(whenExpression) } @@ -123,8 +134,8 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans } private fun translateAsSwitch(fromIndex: Int): Pair? { + val subjectType = subjectType ?: return null val ktSubject = whenExpression.subjectExpression ?: return null - val subjectType = bindingContext().getType(ktSubject) ?: return null val dataFlow = dataFlowValueFactory.createDataFlowValue( ktSubject, subjectType, bindingContext(), context().declarationDescriptor ?: context().currentModule) @@ -322,12 +333,11 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans private fun translateExpressionCondition(condition: KtWhenConditionWithExpression, context: TranslationContext): JsExpression { val patternExpression = condition.expression ?: error("Expression pattern should have an expression.") - val expressionToMatch = expressionToMatch val patternTranslator = Translation.patternTranslator(context) return if (expressionToMatch == null) { patternTranslator.translateExpressionForExpressionPattern(patternExpression) } else { - patternTranslator.translateExpressionPattern(whenExpression.subjectExpression!!, expressionToMatch, patternExpression) + patternTranslator.translateExpressionPattern(subjectType!!, expressionToMatch, patternExpression) } } 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 c855e000bbe..6ca3b7c87ab 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 @@ -173,6 +173,16 @@ public final class TranslationUtils { return nullCheck(prepareForNullCheck(ktSubject, expressionToCheck, context), isNegated); } + @NotNull + public static JsBinaryOperation nullCheck( + @NotNull KotlinType expressionType, + @NotNull JsExpression expressionToCheck, + @NotNull TranslationContext context, + boolean isNegated + ) { + return nullCheck(coerce(context, expressionToCheck, TypeUtils.makeNullable(expressionType)), isNegated); + } + @NotNull public static JsConditional notNullConditional( @NotNull JsExpression expression, diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index 0141cbe2ab2..9b6ae899adc 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -242,6 +242,9 @@ fun TranslationContext.createCoroutineResult(resolvedCall: ResolvedCall<*>): JsE } } +fun KotlinType.refineType() = + TypeUtils.getAllSupertypes(this).find(KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType) ?: this + /** * Tries to get precise statically known primitive type. Takes generic supertypes into account. Doesn't handle smart-casts. * This is needed to be compatible with JVM NaN behaviour: @@ -256,7 +259,7 @@ fun TranslationContext.getPrecisePrimitiveType(expression: KtExpression): Kotlin val bindingContext = bindingContext() val ktType = bindingContext.getType(expression) ?: return null - return TypeUtils.getAllSupertypes(ktType).find(KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType) ?: ktType + return ktType.refineType() } fun TranslationContext.getPrecisePrimitiveTypeNotNull(expression: KtExpression): KotlinType {