diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 919fe493617..7008756a055 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -3401,16 +3401,10 @@ public class ExpressionCodegen extends JetVisitor implem if (isArray) { gen(args.get(0), Type.INT_TYPE); - TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull( - arrayType.getArguments().get(0).getType() + putReifierMarkerIfTypeIsReifiedParameter( + arrayType.getArguments().get(0).getType(), + ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME ); - if (parameterDescriptor != null && parameterDescriptor.isReified()) { - v.iconst(parameterDescriptor.getIndex()); - v.invokestatic( - IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME, - Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false - ); - } v.newarray(boxType(asmType(arrayType.getArguments().get(0).getType()))); } else { @@ -3702,7 +3696,6 @@ The "returned" value of try expression with no finally is either the last expres JetTypeReference typeReference = expression.getRight(); JetType rightType = bindingContext.get(TYPE, typeReference); assert rightType != null; - Type rightTypeAsm = boxType(asmType(rightType)); JetExpression left = expression.getLeft(); DeclarationDescriptor descriptor = rightType.getConstructor().getDeclarationDescriptor(); if (descriptor instanceof ClassDescriptor || descriptor instanceof TypeParameterDescriptor) { @@ -3724,7 +3717,7 @@ The "returned" value of try expression with no finally is either the last expres } else { v.dup(); - v.instanceOf(rightTypeAsm); + generateInstanceOfInstruction(rightType); Label ok = new Label(); v.ifne(ok); v.pop(); @@ -3732,8 +3725,7 @@ The "returned" value of try expression with no finally is either the last expres v.mark(ok); } - v.checkcast(rightTypeAsm); - return StackValue.onStack(rightTypeAsm); + return generateCheckCastInstruction(rightType); } else { throw new UnsupportedOperationException("Don't know how to handle non-class types in as/as? : " + descriptor); @@ -3786,14 +3778,13 @@ The "returned" value of try expression with no finally is either the last expres if (leaveExpressionOnStack) { v.dup(); } - Type type = boxType(asmType(jetType)); if (jetType.isNullable()) { Label nope = new Label(); Label end = new Label(); v.dup(); v.ifnull(nope); - v.instanceOf(type); + generateInstanceOfInstruction(jetType); v.goTo(end); v.mark(nope); v.pop(); @@ -3801,7 +3792,32 @@ The "returned" value of try expression with no finally is either the last expres v.mark(end); } else { - v.instanceOf(type); + generateInstanceOfInstruction(jetType); + } + } + + private void generateInstanceOfInstruction(@NotNull JetType jetType) { + Type type = boxType(asmType(jetType)); + putReifierMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.INSTANCEOF_MARKER_METHOD_NAME); + v.instanceOf(type); + } + + @NotNull + private StackValue generateCheckCastInstruction(@NotNull JetType jetType) { + Type type = boxType(asmType(jetType)); + putReifierMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.CHECKCAST_MARKER_METHOD_NAME); + v.checkcast(type); + return StackValue.onStack(type); + } + + private void putReifierMarkerIfTypeIsReifiedParameter(@NotNull JetType type, @NotNull String markerMethodName) { + TypeParameterDescriptor typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type); + if (typeParameterDescriptor != null && typeParameterDescriptor.isReified()) { + v.iconst(typeParameterDescriptor.getIndex()); + v.invokestatic( + IntrinsicMethods.INTRINSICS_CLASS_NAME, markerMethodName, + Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false + ); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/ReifiedTypeInliner.kt b/compiler/backend/src/org/jetbrains/jet/codegen/inline/ReifiedTypeInliner.kt index a14ab3c73d0..7b08345834d 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/ReifiedTypeInliner.kt +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/ReifiedTypeInliner.kt @@ -35,6 +35,8 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame class object { public val NEW_ARRAY_MARKER_METHOD_NAME: String = "reifyNewArray" + public val CHECKCAST_MARKER_METHOD_NAME: String = "reifyCheckcast" + public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof" } public fun reifyInstructions(instructions: InsnList) { @@ -54,9 +56,12 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList) { val mapping = getTypeParameterMapping(insn) ?: return - if (mapping.asmType != null) { + val asmType = mapping.asmType + if (asmType != null) { if (!when (insn.name) { - NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, mapping.asmType) + NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, asmType) + CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, asmType) + INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, asmType) else -> false }) { return @@ -68,10 +73,18 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame } } - private fun processNewArray(insn: MethodInsnNode, parameter: Type): Boolean { - if (insn.getNext()?.getOpcode() != Opcodes.ANEWARRAY) return false - val next = insn.getNext() as TypeInsnNode - next.desc = parameter.getInternalName() + private fun processNewArray(insn: MethodInsnNode, parameter: Type) = + processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY) + + private fun processCheckcast(insn: MethodInsnNode, parameter: Type) = + processNextTypeInsn(insn, parameter, Opcodes.CHECKCAST) + + private fun processInstanceof(insn: MethodInsnNode, parameter: Type) = + processNextTypeInsn(insn, parameter, Opcodes.INSTANCEOF) + + private fun processNextTypeInsn(insn: MethodInsnNode, parameter: Type, expectedNextOpcode: Int): Boolean { + if (insn.getNext()?.getOpcode() != expectedNextOpcode) return false + (insn.getNext() as TypeInsnNode).desc = parameter.getInternalName() return true } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java index 2a461139d1a..6057c4472b7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/CastDiagnosticsUtil.java @@ -119,8 +119,8 @@ public class CastDiagnosticsUtil { // if it is a upcast, it's never erased if (typeChecker.isSubtypeOf(supertype, subtype)) return false; - // downcasting to a type parameter is always erased - if (TypeUtils.isTypeParameter(subtype)) return true; + // downcasting to a non-reified type parameter is always erased + if (TypeUtils.isNonReifiedTypeParemeter(subtype)) return true; // Check that we are actually casting to a generic type // NOTE: this does not account for 'as Array>' diff --git a/compiler/testData/codegen/boxWithStdlib/reified/checkcast.kt b/compiler/testData/codegen/boxWithStdlib/reified/checkcast.kt new file mode 100644 index 00000000000..833a6a0db65 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reified/checkcast.kt @@ -0,0 +1,20 @@ +import kotlin.test.assertEquals + +inline fun checkcast(x: Any?): T { + return x as T +} + +fun box(): String { + val x = checkcast("abc") + assertEquals("abc", x) + val y = checkcast(1) + assertEquals(1, y) + + try { + val z = checkcast("abc") + } catch (e: Exception) { + return "OK" + } + + return "Fail" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reified/filterIsInstance.kt b/compiler/testData/codegen/boxWithStdlib/reified/filterIsInstance.kt new file mode 100644 index 00000000000..f7e2f9a5c45 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reified/filterIsInstance.kt @@ -0,0 +1,16 @@ +import kotlin.test.assertEquals + +inline fun Array.filterIsInstance(): List { + return this.filter { it is T }.map { it as T } +} + +fun box(): String { + val src: Array = array(1,2,3.toDouble(), "abc", "cde") + + assertEquals(arrayListOf(1,2), src.filterIsInstance()) + assertEquals(arrayListOf(3.0), src.filterIsInstance()) + assertEquals(arrayListOf("abc", "cde"), src.filterIsInstance()) + assertEquals(src.toList(), src.filterIsInstance()) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reified/instanceof.kt b/compiler/testData/codegen/boxWithStdlib/reified/instanceof.kt new file mode 100644 index 00000000000..4d20512d772 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reified/instanceof.kt @@ -0,0 +1,11 @@ +inline fun isinstance(x: Any?): Boolean { + return x is T +} + +fun box(): String { + assert(isinstance("abc")) + assert(isinstance(1)) + assert(!isinstance("abc")) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reified/safecast.kt b/compiler/testData/codegen/boxWithStdlib/reified/safecast.kt new file mode 100644 index 00000000000..cb31cc60793 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reified/safecast.kt @@ -0,0 +1,17 @@ +import kotlin.test.assertEquals + +inline fun safecast(x: Any?): T? { + return x as? T +} + +fun box(): String { + val x = safecast("abc") + assertEquals("abc", x) + val y = safecast(1) + assertEquals(1, y) + + val z = safecast("abc") + assertEquals(null, z) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index ac0def4157a..9fe1df46888 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2420,6 +2420,24 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reified"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("checkcast.kt") + public void testCheckcast() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/checkcast.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("filterIsInstance.kt") + public void testFilterIsInstance() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/filterIsInstance.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("instanceof.kt") + public void testInstanceof() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/instanceof.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("newArrayInt.kt") public void testNewArrayInt() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/newArrayInt.kt"); @@ -2432,6 +2450,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("safecast.kt") + public void testSafecast() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/safecast.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("sameIndexRecursive.kt") public void testSameIndexRecursive() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/sameIndexRecursive.kt"); diff --git a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java index 3e4066c35fe..6abcc84cc97 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -724,6 +724,11 @@ public class TypeUtils { return getTypeParameterDescriptorOrNull(type) != null; } + public static boolean isNonReifiedTypeParemeter(@NotNull JetType type) { + TypeParameterDescriptor typeParameterDescriptor = getTypeParameterDescriptorOrNull(type); + return typeParameterDescriptor != null && !typeParameterDescriptor.isReified(); + } + @Nullable public static TypeParameterDescriptor getTypeParameterDescriptorOrNull(@NotNull JetType type) { if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java b/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java index f8494eb6373..cc2801d4865 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/Intrinsics.java @@ -110,6 +110,14 @@ public class Intrinsics { throwUndefinedForReified(); } + public static void reifyCheckcast(int parameterTypeIndex) { + throwUndefinedForReified(); + } + + public static void reifyInstanceof(int parameterTypeIndex) { + throwUndefinedForReified(); + } + public static T sanitizeStackTrace(T throwable) { StackTraceElement[] stackTrace = throwable.getStackTrace(); ArrayList list = new ArrayList(stackTrace.length);