[JS BE] Support val in when subject

#KT-25014 Fixed
This commit is contained in:
Zalim Bashorov
2018-08-02 13:28:53 +03:00
parent 289ff845c4
commit 3c765e3625
15 changed files with 82 additions and 59 deletions
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
fun box(): String { fun box(): String {
var y: String = "OK" var y: String = "OK"
@@ -1,6 +1,6 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME // WITH_RUNTIME
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR
fun dense(x: Int): Int { fun dense(x: Int): Int {
return when (val xx = x) { return when (val xx = x) {
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
val x = 1 val x = 1
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
val dz = -0.0 val dz = -0.0
val fz = -0.0f val fz = -0.0f
@@ -29,5 +28,14 @@ fun box(): String {
} }
} }
testDoubleAsUpperBound(-0.0)
return "OK" return "OK"
} }
fun <T: Double> testDoubleAsUpperBound(v: T): Boolean {
return when (val a = v*v) {
0.0 -> true
else -> throw AssertionError()
}
}
@@ -1,22 +1,32 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject +ProperIeee754Comparisons // !LANGUAGE: +VariableDeclarationInWhenSubject +ProperIeee754Comparisons
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR
val az: Any = -0.0 val az: Any = -0.0
val afz: Any = -0.0f val afz: Any = -0.0f
fun box(): String { fun box(): String {
when (val y = az) { val y = az
when (y) {
!is Double -> throw AssertionError() !is Double -> throw AssertionError()
0.0 -> {} 0.0 -> {}
else -> throw AssertionError() else -> throw AssertionError()
} }
val yy = afz
when (val y = afz) { when (yy) {
!is Float -> throw AssertionError() !is Float -> throw AssertionError()
0.0 -> {} 0.0 -> {}
else -> throw AssertionError() else -> throw AssertionError()
} }
testDoubleAsUpperBound(-0.0)
return "OK" return "OK"
} }
fun <T: Double> testDoubleAsUpperBound(v: T): Boolean {
return when (val a = v*v) {
0.0 -> true
else -> throw AssertionError()
}
}
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
val x: Any = 1 val x: Any = 1
@@ -1,6 +1,6 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME // WITH_RUNTIME
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR
fun sparse(x: Int): Int { fun sparse(x: Int): Int {
return when (val xx = (x % 4) * 100) { return when (val xx = (x % 4) * 100) {
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
var effectCount = 0 var effectCount = 0
@@ -1,6 +1,6 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME // WITH_RUNTIME
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,6 +1,6 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME // WITH_RUNTIME
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS_IR
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,6 +1,5 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject // !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME // WITH_RUNTIME
// IGNORE_BACKEND: JS
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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.
*/ */
package org.jetbrains.kotlin.js.translate.expression; 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.reference.ReferenceTranslator;
import org.jetbrains.kotlin.js.translate.utils.*; import org.jetbrains.kotlin.js.translate.utils.*;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtIsExpression;
import org.jetbrains.kotlin.psi.KtTypeReference;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo; import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
@@ -270,31 +256,32 @@ public final class PatternTranslator extends AbstractTranslator {
@NotNull @NotNull
public JsExpression translateExpressionPattern( public JsExpression translateExpressionPattern(
@NotNull KtExpression subjectExpression, @NotNull KotlinType subjectType,
@NotNull JsExpression expressionToMatch, @NotNull JsExpression expressionToMatch,
@NotNull KtExpression patternExpression @NotNull KtExpression patternExpression
) { ) {
PrimitiveNumericComparisonInfo ieeeInfo = UtilsKt.getPrimitiveNumericComparisonInfo(context(), patternExpression); PrimitiveNumericComparisonInfo ieeeInfo = UtilsKt.getPrimitiveNumericComparisonInfo(context(), patternExpression);
KotlinType subjectType, patternType; KotlinType actualSubjectType;
KotlinType patternType;
if (ieeeInfo != null) { if (ieeeInfo != null) {
subjectType = ieeeInfo.getLeftType(); actualSubjectType = ieeeInfo.getLeftType();
patternType = ieeeInfo.getRightType(); patternType = ieeeInfo.getRightType();
} else { } else {
subjectType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), subjectExpression); actualSubjectType = UtilsKt.refineType(subjectType);
patternType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), patternExpression); patternType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), patternExpression);
} }
EqualityType matchEquality = equalityType(subjectType); EqualityType matchEquality = equalityType(actualSubjectType);
EqualityType patternEquality = equalityType(patternType); 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) { if (matchEquality == EqualityType.PRIMITIVE && patternEquality == EqualityType.PRIMITIVE) {
return equality(expressionToMatch, expressionToMatchAgainst); return equality(expressionToMatch, expressionToMatchAgainst);
} }
else if (expressionToMatchAgainst instanceof JsNullLiteral) { else if (expressionToMatchAgainst instanceof JsNullLiteral) {
return TranslationUtils.nullCheck(subjectExpression, expressionToMatch, context(), false); return TranslationUtils.nullCheck(actualSubjectType, expressionToMatch, context(), false);
} }
else { else {
return TopLevelFIF.KOTLIN_EQUALS.apply(expressionToMatch, Collections.singletonList(expressionToMatchAgainst), context()); return TopLevelFIF.KOTLIN_EQUALS.apply(expressionToMatch, Collections.singletonList(expressionToMatchAgainst), context());
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * 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.
* 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.
*/ */
package org.jetbrains.kotlin.js.translate.expression 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.backend.common.CodegenUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.js.backend.ast.* 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.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.operation.InOperationTranslator 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
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not
import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator
@@ -49,6 +40,7 @@ private typealias EntryWithConstants = Pair<List<JsExpression>, KtWhenEntry>
class WhenTranslator class WhenTranslator
private constructor(private val whenExpression: KtWhenExpression, context: TranslationContext) : AbstractTranslator(context) { private constructor(private val whenExpression: KtWhenExpression, context: TranslationContext) : AbstractTranslator(context) {
private val subjectType: KotlinType?
private val expressionToMatch: JsExpression? private val expressionToMatch: JsExpression?
private val type: KotlinType? private val type: KotlinType?
private val uniqueConstants = mutableSetOf<Any>() private val uniqueConstants = mutableSetOf<Any>()
@@ -63,8 +55,27 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans
} }
init { init {
val subject = whenExpression.subjectExpression val subjectVariable = whenExpression.subjectVariable
expressionToMatch = if (subject != null) context.defineTemporary(Translation.translateAsExpression(subject, context)) else null 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) type = bindingContext().getType(whenExpression)
} }
@@ -123,8 +134,8 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans
} }
private fun translateAsSwitch(fromIndex: Int): Pair<JsSwitch, Int>? { private fun translateAsSwitch(fromIndex: Int): Pair<JsSwitch, Int>? {
val subjectType = subjectType ?: return null
val ktSubject = whenExpression.subjectExpression ?: return null val ktSubject = whenExpression.subjectExpression ?: return null
val subjectType = bindingContext().getType(ktSubject) ?: return null
val dataFlow = dataFlowValueFactory.createDataFlowValue( val dataFlow = dataFlowValueFactory.createDataFlowValue(
ktSubject, subjectType, bindingContext(), context().declarationDescriptor ?: context().currentModule) 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 { private fun translateExpressionCondition(condition: KtWhenConditionWithExpression, context: TranslationContext): JsExpression {
val patternExpression = condition.expression ?: error("Expression pattern should have an expression.") val patternExpression = condition.expression ?: error("Expression pattern should have an expression.")
val expressionToMatch = expressionToMatch
val patternTranslator = Translation.patternTranslator(context) val patternTranslator = Translation.patternTranslator(context)
return if (expressionToMatch == null) { return if (expressionToMatch == null) {
patternTranslator.translateExpressionForExpressionPattern(patternExpression) patternTranslator.translateExpressionForExpressionPattern(patternExpression)
} else { } else {
patternTranslator.translateExpressionPattern(whenExpression.subjectExpression!!, expressionToMatch, patternExpression) patternTranslator.translateExpressionPattern(subjectType!!, expressionToMatch, patternExpression)
} }
} }
@@ -173,6 +173,16 @@ public final class TranslationUtils {
return nullCheck(prepareForNullCheck(ktSubject, expressionToCheck, context), isNegated); 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 @NotNull
public static JsConditional notNullConditional( public static JsConditional notNullConditional(
@NotNull JsExpression expression, @NotNull JsExpression expression,
@@ -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. * 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: * This is needed to be compatible with JVM NaN behaviour:
@@ -256,7 +259,7 @@ fun TranslationContext.getPrecisePrimitiveType(expression: KtExpression): Kotlin
val bindingContext = bindingContext() val bindingContext = bindingContext()
val ktType = bindingContext.getType(expression) ?: return null val ktType = bindingContext.getType(expression) ?: return null
return TypeUtils.getAllSupertypes(ktType).find(KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType) ?: ktType return ktType.refineType()
} }
fun TranslationContext.getPrecisePrimitiveTypeNotNull(expression: KtExpression): KotlinType { fun TranslationContext.getPrecisePrimitiveTypeNotNull(expression: KtExpression): KotlinType {