diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java index fc1071d5cca..d28650edfd6 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java @@ -53,6 +53,12 @@ public class CastTestGenerated extends AbstractCastTest { doTest(fileName); } + @TestMetadata("castToGenericTypeWithUpperBound.kt") + public void testCastToGenericTypeWithUpperBound() throws Exception { + String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToGenericTypeWithUpperBound.kt"); + doTest(fileName); + } + @TestMetadata("castToNotNull.kt") public void testCastToNotNull() throws Exception { String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToNotNull.kt"); @@ -77,6 +83,12 @@ public class CastTestGenerated extends AbstractCastTest { doTest(fileName); } + @TestMetadata("safeCastToGenericTypeWithUpperBound.kt") + public void testSafeCastToGenericTypeWithUpperBound() throws Exception { + String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/safeCastToGenericTypeWithUpperBound.kt"); + doTest(fileName); + } + @TestMetadata("safeCastToNotNull.kt") public void testSafeCastToNotNull() throws Exception { String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/safeCastToNotNull.kt"); 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 663430e39db..c29e98ed15b 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 @@ -42,6 +42,8 @@ import org.jetbrains.kotlin.psi.KtTypeReference; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.types.KotlinType; +import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.*; +import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.getNameIfStandardType; import static org.jetbrains.kotlin.builtins.FunctionTypesKt.isFunctionTypeOrSubtype; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isAnyOrNullableAny; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isArray; @@ -106,8 +108,11 @@ public final class PatternTranslator extends AbstractTranslator { public JsExpression translateIsCheck(@NotNull JsExpression subject, @NotNull KtTypeReference typeReference) { KotlinType type = BindingUtils.getTypeByReference(bindingContext(), typeReference); JsExpression checkFunReference = doGetIsTypeCheckCallable(type); + boolean isReifiedType = isReifiedTypeParameter(type); - if (isReifiedTypeParameter(type) && findChildByType(typeReference, KtNodeTypes.NULLABLE_TYPE) != null) { + if (!isReifiedType && isNullableType(type) || + isReifiedType && findChildByType(typeReference, KtNodeTypes.NULLABLE_TYPE) != null + ) { checkFunReference = namer().orNull(checkFunReference); } @@ -142,7 +147,7 @@ public final class PatternTranslator extends AbstractTranslator { return getIsTypeCheckCallableForReifiedType(typeParameterDescriptor); } - return namer().isAny(); + return doGetIsTypeCheckCallable(typeParameterDescriptor.getUpperBoundsAsType()); } JsNameRef typeName = getClassNameReference(type); diff --git a/js/js.translator/testData/expression/cast/_commonFiles/asserts.kt b/js/js.translator/testData/expression/cast/_commonFiles/asserts.kt index cf42464c974..9518032ee28 100644 --- a/js/js.translator/testData/expression/cast/_commonFiles/asserts.kt +++ b/js/js.translator/testData/expression/cast/_commonFiles/asserts.kt @@ -4,6 +4,15 @@ package kotlin // CHECK_NOT_REFERENCED: Kotlin.isTypeOf // CHECK_NOT_REFERENCED: Kotlin.orNull +fun success(message: String, fn: ()->Unit) { + try { + fn() + } + catch (e: Exception) { + throw Exception("Exception was thrown: message=$message, exception=$e") + } +} + fun failsClassCast(message: String, fn: ()->Unit) { try { fn() diff --git a/js/js.translator/testData/expression/cast/cases/castToGenericType.kt b/js/js.translator/testData/expression/cast/cases/castToGenericType.kt index 6ee035c61be..28dc11f08c3 100644 --- a/js/js.translator/testData/expression/cast/cases/castToGenericType.kt +++ b/js/js.translator/testData/expression/cast/cases/castToGenericType.kt @@ -37,13 +37,13 @@ fun castsNullableToNotNullT(a: Any?) { fun box(): String { val a = A("OK") - castsNotNullToNullableT(a) - castsNullableToNullableT(a) - castsNullableToNullableT(null) - castsNotNullToNotNullT(a) - castsNullableToNotNullT(a) - castsNullableToNotNullT(null) - castNullableToNotNullT(a) + success("castsNotNullToNullableT(a)") { castsNotNullToNullableT(a) } + success("castsNullableToNullableT(a)") { castsNullableToNullableT(a) } + success("castsNullableToNullableT(null)") { castsNullableToNullableT(null) } + success("castsNotNullToNotNullT(a)") { castsNotNullToNotNullT(a) } + success("castsNullableToNotNullT(a)") { castsNullableToNotNullT(a) } + success("castsNullableToNotNullT(null)") { castsNullableToNotNullT(null) } + success("castNullableToNotNullT(a)") { castNullableToNotNullT(a) } failsClassCast("castNullableToNotNullT(null)") { castNullableToNotNullT(null) } return "OK" diff --git a/js/js.translator/testData/expression/cast/cases/castToGenericTypeWithUpperBound.kt b/js/js.translator/testData/expression/cast/cases/castToGenericTypeWithUpperBound.kt new file mode 100644 index 00000000000..027b4a39eaf --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castToGenericTypeWithUpperBound.kt @@ -0,0 +1,39 @@ +package foo + +open class A +class B : A() +class C + +fun notNullToNotNullT(a: Any): T = a as T + +fun nullableToNotNullT(a: Any?): T = a as T + +fun notNullToNullableT(a: Any): T? = a as T? + +fun nullableToNullableT(a: Any?): T? = a as T? + +fun box(): String { + val a = A() + val b = B() + val c = C() + + success("notNullToNotNullT(a)") { assertEquals(a, notNullToNotNullT(a)) } + success("notNullToNotNullT(b)") { assertEquals(b, notNullToNotNullT(b)) } + failsClassCast("notNullToNotNullT(c)") { notNullToNotNullT(c) } + + success("nullableToNotNullT(a)") {assertEquals(a, nullableToNotNullT(a)) } + success("nullableToNotNullT(b)") {assertEquals(b, nullableToNotNullT(b)) } + failsClassCast("nullableToNotNullT(c)") { nullableToNotNullT(c) } + failsClassCast("nullableToNotNullT(null)") { nullableToNotNullT(null) } + + success("notNullToNullableT(a)") { assertEquals(a, notNullToNullableT(a))} + success("notNullToNullableT(b)") { assertEquals(b, notNullToNullableT(b))} + failsClassCast("notNullToNullableT(c)") { notNullToNullableT(c) } + + success("nullableToNullableT(a)") { assertEquals(a, nullableToNullableT(a)) } + success("nullableToNullableT(b)") { assertEquals(b, nullableToNullableT(b)) } + success("nullableToNullableT(null)") { assertEquals(null, nullableToNullableT(null)) } + failsClassCast("nullableToNullableT(c)") { nullableToNullableT(c) } + + return "OK" +} diff --git a/js/js.translator/testData/expression/cast/cases/safeCastToGenericTypeWithUpperBound.kt b/js/js.translator/testData/expression/cast/cases/safeCastToGenericTypeWithUpperBound.kt new file mode 100644 index 00000000000..f8232ac2751 --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/safeCastToGenericTypeWithUpperBound.kt @@ -0,0 +1,26 @@ +package foo + +open class A +class B : A() +class C + +fun notNullToNullableT(a: Any): T? = a as? T? + +fun nullableToNullableT(a: Any?): T? = a as? T? + +fun box(): String { + val a = A() + val b = B() + val c = C() + + success("notNullToNullableT(a)") { assertEquals(a, notNullToNullableT(a)) } + success("notNullToNullableT(b)") { assertEquals(b, notNullToNullableT(b)) } + success("notNullToNullableT(c)") { assertEquals(null, notNullToNullableT(c)) } + + success("nullableToNullableT(a)") { assertEquals(a, nullableToNullableT(a)) } + success("nullableToNullableT(b)") { assertEquals(b, nullableToNullableT(b)) } + success("nullableToNullableT(null)") { assertEquals(null, nullableToNullableT(null)) } + success("nullableToNullableT(c)") { assertEquals(null, nullableToNullableT(c)) } + + return "OK" +}