[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
// IGNORE_BACKEND: JS
fun box(): String {
var y: String = "OK"
@@ -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) {
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
val x = 1
@@ -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"
}
}
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
// 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"
}
}
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
// IGNORE_BACKEND: JS
val x: Any = 1
@@ -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) {
@@ -1,5 +1,4 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
// IGNORE_BACKEND: JS
var effectCount = 0
@@ -1,6 +1,6 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME
// IGNORE_BACKEND: JS, JS_IR
// IGNORE_BACKEND: JS_IR
import kotlin.test.assertEquals
@@ -1,6 +1,6 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME
// IGNORE_BACKEND: JS, JS_IR
// IGNORE_BACKEND: JS_IR
import kotlin.test.assertEquals
@@ -1,6 +1,5 @@
// !LANGUAGE: +VariableDeclarationInWhenSubject
// WITH_RUNTIME
// IGNORE_BACKEND: JS
import kotlin.test.assertEquals
@@ -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());
@@ -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<List<JsExpression>, 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<Any>()
@@ -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<JsSwitch, Int>? {
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)
}
}
@@ -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,
@@ -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 {