Typecheck instructions are supported for reified T
This commit is contained in:
committed by
Andrey Breslav
parent
416ac7917b
commit
f3c49c605f
@@ -3401,16 +3401,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
gen(args.get(0), Type.INT_TYPE);
|
gen(args.get(0), Type.INT_TYPE);
|
||||||
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(
|
putReifierMarkerIfTypeIsReifiedParameter(
|
||||||
arrayType.getArguments().get(0).getType()
|
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())));
|
v.newarray(boxType(asmType(arrayType.getArguments().get(0).getType())));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -3702,7 +3696,6 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
JetTypeReference typeReference = expression.getRight();
|
JetTypeReference typeReference = expression.getRight();
|
||||||
JetType rightType = bindingContext.get(TYPE, typeReference);
|
JetType rightType = bindingContext.get(TYPE, typeReference);
|
||||||
assert rightType != null;
|
assert rightType != null;
|
||||||
Type rightTypeAsm = boxType(asmType(rightType));
|
|
||||||
JetExpression left = expression.getLeft();
|
JetExpression left = expression.getLeft();
|
||||||
DeclarationDescriptor descriptor = rightType.getConstructor().getDeclarationDescriptor();
|
DeclarationDescriptor descriptor = rightType.getConstructor().getDeclarationDescriptor();
|
||||||
if (descriptor instanceof ClassDescriptor || descriptor instanceof TypeParameterDescriptor) {
|
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 {
|
else {
|
||||||
v.dup();
|
v.dup();
|
||||||
v.instanceOf(rightTypeAsm);
|
generateInstanceOfInstruction(rightType);
|
||||||
Label ok = new Label();
|
Label ok = new Label();
|
||||||
v.ifne(ok);
|
v.ifne(ok);
|
||||||
v.pop();
|
v.pop();
|
||||||
@@ -3732,8 +3725,7 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
v.mark(ok);
|
v.mark(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
v.checkcast(rightTypeAsm);
|
return generateCheckCastInstruction(rightType);
|
||||||
return StackValue.onStack(rightTypeAsm);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new UnsupportedOperationException("Don't know how to handle non-class types in as/as? : " + descriptor);
|
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) {
|
if (leaveExpressionOnStack) {
|
||||||
v.dup();
|
v.dup();
|
||||||
}
|
}
|
||||||
Type type = boxType(asmType(jetType));
|
|
||||||
if (jetType.isNullable()) {
|
if (jetType.isNullable()) {
|
||||||
Label nope = new Label();
|
Label nope = new Label();
|
||||||
Label end = new Label();
|
Label end = new Label();
|
||||||
|
|
||||||
v.dup();
|
v.dup();
|
||||||
v.ifnull(nope);
|
v.ifnull(nope);
|
||||||
v.instanceOf(type);
|
generateInstanceOfInstruction(jetType);
|
||||||
v.goTo(end);
|
v.goTo(end);
|
||||||
v.mark(nope);
|
v.mark(nope);
|
||||||
v.pop();
|
v.pop();
|
||||||
@@ -3801,7 +3792,32 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
v.mark(end);
|
v.mark(end);
|
||||||
}
|
}
|
||||||
else {
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
|
|
||||||
class object {
|
class object {
|
||||||
public val NEW_ARRAY_MARKER_METHOD_NAME: String = "reifyNewArray"
|
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) {
|
public fun reifyInstructions(instructions: InsnList) {
|
||||||
@@ -54,9 +56,12 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList) {
|
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList) {
|
||||||
val mapping = getTypeParameterMapping(insn) ?: return
|
val mapping = getTypeParameterMapping(insn) ?: return
|
||||||
|
|
||||||
if (mapping.asmType != null) {
|
val asmType = mapping.asmType
|
||||||
|
if (asmType != null) {
|
||||||
if (!when (insn.name) {
|
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
|
else -> false
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@@ -68,10 +73,18 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processNewArray(insn: MethodInsnNode, parameter: Type): Boolean {
|
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
|
||||||
if (insn.getNext()?.getOpcode() != Opcodes.ANEWARRAY) return false
|
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
|
||||||
val next = insn.getNext() as TypeInsnNode
|
|
||||||
next.desc = parameter.getInternalName()
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ public class CastDiagnosticsUtil {
|
|||||||
// if it is a upcast, it's never erased
|
// if it is a upcast, it's never erased
|
||||||
if (typeChecker.isSubtypeOf(supertype, subtype)) return false;
|
if (typeChecker.isSubtypeOf(supertype, subtype)) return false;
|
||||||
|
|
||||||
// downcasting to a type parameter is always erased
|
// downcasting to a non-reified type parameter is always erased
|
||||||
if (TypeUtils.isTypeParameter(subtype)) return true;
|
if (TypeUtils.isNonReifiedTypeParemeter(subtype)) return true;
|
||||||
|
|
||||||
// Check that we are actually casting to a generic type
|
// Check that we are actually casting to a generic type
|
||||||
// NOTE: this does not account for 'as Array<List<T>>'
|
// NOTE: this does not account for 'as Array<List<T>>'
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
inline fun<reified T> checkcast(x: Any?): T {
|
||||||
|
return x as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val x = checkcast<String>("abc")
|
||||||
|
assertEquals("abc", x)
|
||||||
|
val y = checkcast<Int>(1)
|
||||||
|
assertEquals(1, y)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val z = checkcast<Int>("abc")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Fail"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
inline fun<reified T> Array<Any>.filterIsInstance(): List<T> {
|
||||||
|
return this.filter { it is T }.map { it as T }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val src: Array<Any> = array(1,2,3.toDouble(), "abc", "cde")
|
||||||
|
|
||||||
|
assertEquals(arrayListOf(1,2), src.filterIsInstance<Int>())
|
||||||
|
assertEquals(arrayListOf(3.0), src.filterIsInstance<Double>())
|
||||||
|
assertEquals(arrayListOf("abc", "cde"), src.filterIsInstance<String>())
|
||||||
|
assertEquals(src.toList(), src.filterIsInstance<Any>())
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
inline fun<reified T> isinstance(x: Any?): Boolean {
|
||||||
|
return x is T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assert(isinstance<String>("abc"))
|
||||||
|
assert(isinstance<Int>(1))
|
||||||
|
assert(!isinstance<Int>("abc"))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
inline fun<reified T> safecast(x: Any?): T? {
|
||||||
|
return x as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val x = safecast<String>("abc")
|
||||||
|
assertEquals("abc", x)
|
||||||
|
val y = safecast<Int>(1)
|
||||||
|
assertEquals(1, y)
|
||||||
|
|
||||||
|
val z = safecast<Int>("abc")
|
||||||
|
assertEquals(null, z)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+24
@@ -2420,6 +2420,24 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reified"), Pattern.compile("^(.+)\\.kt$"), true);
|
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")
|
@TestMetadata("newArrayInt.kt")
|
||||||
public void testNewArrayInt() throws Exception {
|
public void testNewArrayInt() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/newArrayInt.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/newArrayInt.kt");
|
||||||
@@ -2432,6 +2450,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
doTestWithStdlib(fileName);
|
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")
|
@TestMetadata("sameIndexRecursive.kt")
|
||||||
public void testSameIndexRecursive() throws Exception {
|
public void testSameIndexRecursive() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/sameIndexRecursive.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/sameIndexRecursive.kt");
|
||||||
|
|||||||
@@ -724,6 +724,11 @@ public class TypeUtils {
|
|||||||
return getTypeParameterDescriptorOrNull(type) != null;
|
return getTypeParameterDescriptorOrNull(type) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isNonReifiedTypeParemeter(@NotNull JetType type) {
|
||||||
|
TypeParameterDescriptor typeParameterDescriptor = getTypeParameterDescriptorOrNull(type);
|
||||||
|
return typeParameterDescriptor != null && !typeParameterDescriptor.isReified();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static TypeParameterDescriptor getTypeParameterDescriptorOrNull(@NotNull JetType type) {
|
public static TypeParameterDescriptor getTypeParameterDescriptorOrNull(@NotNull JetType type) {
|
||||||
if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
|
if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
|
||||||
|
|||||||
@@ -110,6 +110,14 @@ public class Intrinsics {
|
|||||||
throwUndefinedForReified();
|
throwUndefinedForReified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void reifyCheckcast(int parameterTypeIndex) {
|
||||||
|
throwUndefinedForReified();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reifyInstanceof(int parameterTypeIndex) {
|
||||||
|
throwUndefinedForReified();
|
||||||
|
}
|
||||||
|
|
||||||
public static <T extends Throwable> T sanitizeStackTrace(T throwable) {
|
public static <T extends Throwable> T sanitizeStackTrace(T throwable) {
|
||||||
StackTraceElement[] stackTrace = throwable.getStackTrace();
|
StackTraceElement[] stackTrace = throwable.getStackTrace();
|
||||||
ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>(stackTrace.length);
|
ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>(stackTrace.length);
|
||||||
|
|||||||
Reference in New Issue
Block a user