Fix for KT-7999: Wrong result when use nullable types as reified parameter, KT-8043: Unsafe cast to reified parameter throws NPE instead of TypeCastException

#KT-7999 Fixed
  #KT-8043 Fixed

~
This commit is contained in:
Michael Bogdanov
2015-11-28 12:32:11 +03:00
parent 3478a6fb6c
commit 4b8017e34b
37 changed files with 756 additions and 40 deletions
@@ -57,7 +57,6 @@ import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
@@ -2483,9 +2482,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
else {
mappings.addParameterMappingToNewParameter(
key.getName().getIdentifier(),
parameterDescriptor.getName().getIdentifier()
);
key.getName().getIdentifier(), type,
parameterDescriptor.getName().getIdentifier());
}
}
return getOrCreateCallGenerator(
@@ -3689,13 +3687,8 @@ The "returned" value of try expression with no finally is either the last expres
}
if (opToken != KtTokens.AS_SAFE) {
if (!TypeUtils.isNullableType(rightType)) {
v.dup();
Label nonnull = new Label();
v.ifnonnull(nonnull);
genThrow(v, "kotlin/TypeCastException", "null cannot be cast to non-null type " +
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(rightType));
v.mark(nonnull);
if (!TypeUtils.isNullableType(rightType) && !TypeUtils.isReifiedTypeParameter(rightType)) {
CodegenUtilKt.generateNullCheckForNonSafeAs(v, rightType);
}
}
else {
@@ -3759,7 +3752,7 @@ The "returned" value of try expression with no finally is either the last expres
if (leaveExpressionOnStack) {
v.dup();
}
CodegenUtilKt.generateIsCheck(v, kotlinType, new Function1<InstructionAdapter, Unit>() {
CodegenUtilKt.generateIsCheck(v, kotlinType.isMarkedNullable() && !TypeUtils.isReifiedTypeParameter(kotlinType), new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter adapter) {
generateInstanceOfInstruction(kotlinType);
@@ -3794,8 +3787,8 @@ The "returned" value of try expression with no finally is either the last expres
parentCodegen.getReifiedTypeParametersUsages().
addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
}
v.visitLdcInsn(typeParameterDescriptor.getName().asString());
boolean putNullableFlag = ReifiedTypeInliner.isNullableMarkerInstruction(markerMethodName) && type.isMarkedNullable();
v.visitLdcInsn(typeParameterDescriptor.getName().asString() + (putNullableFlag ? "?" : ""));
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, markerMethodName,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false
@@ -911,7 +911,7 @@ public class FunctionCodegen {
iv.ifnonnull(afterBarrier);
}
else {
CodegenUtilKt.generateIsCheck(iv, kotlinType, new Function1<InstructionAdapter, Unit>() {
CodegenUtilKt.generateIsCheck(iv, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter adapter) {
TypeIntrinsics.instanceOf(adapter, kotlinType, boxType(delegateParameterType));
@@ -18,16 +18,17 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
fun generateIsCheck(
v: InstructionAdapter,
type: KotlinType,
isNullable: Boolean,
generateInstanceOfInstruction: (InstructionAdapter) -> Unit
) {
if (type.isMarkedNullable) {
if (isNullable) {
val nope = Label()
val end = Label()
@@ -51,6 +52,19 @@ fun generateIsCheck(
}
}
fun generateNullCheckForNonSafeAs(
v: InstructionAdapter,
type: KotlinType
) {
with(v) {
dup()
val nonnull = Label()
ifnonnull(nonnull)
AsmUtil.genThrow(v, "kotlin/TypeCastException", "null cannot be cast to non-null type " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type))
mark(nonnull)
}
}
public fun SpecialSignatureInfo.replaceValueParametersIn(sourceSignature: String?): String?
= valueParametersSignature?.let { sourceSignature?.replace("^\\(.*\\)".toRegex(), "($it)") }
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.codegen.inline;
import com.intellij.openapi.util.Pair;
import com.intellij.util.ArrayUtil;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull;
@@ -237,7 +236,7 @@ public class AnonymousObjectTransformer {
@NotNull ParametersBuilder capturedBuilder,
boolean isConstructor
) {
ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifedTypeInliner.reifyInstructions(sourceNode.instructions);
ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifedTypeInliner.reifyInstructions(sourceNode);
Parameters parameters = isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
RegeneratedLambdaFieldRemapper remapper =
@@ -245,7 +245,7 @@ public class InlineCodegen extends CallGenerator {
private InlineResult inlineCall(SMAPAndMethodNode nodeAndSmap) {
MethodNode node = nodeAndSmap.getNode();
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node.instructions);
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node);
generateClosuresBodies();
//through generation captured parameters will be added to invocationParamBuilder
@@ -311,8 +311,7 @@ public class InlineCodegenUtil {
}
}
public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode beforeNode) {
InsnList instructions = to.instructions;
public static void insertNodeBefore(@NotNull MethodNode from, @NotNull InsnList instructions, @NotNull AbstractInsnNode beforeNode) {
ListIterator<AbstractInsnNode> iterator = from.instructions.iterator();
while (iterator.hasNext()) {
AbstractInsnNode next = iterator.next();
@@ -320,6 +319,10 @@ public class InlineCodegenUtil {
}
}
public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode beforeNode) {
insertNodeBefore(from, to.instructions, beforeNode);
}
public static MethodNode createEmptyMethodNode() {
return new MethodNode(API, 0, "fake", "()V", null, null);
@@ -18,16 +18,22 @@ package org.jetbrains.kotlin.codegen.inline
import com.google.common.collect.ImmutableSet
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.generateIsCheck
import org.jetbrains.kotlin.codegen.generateNullCheckForNonSafeAs
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.signature.SignatureReader
import org.jetbrains.org.objectweb.asm.signature.SignatureWriter
import org.jetbrains.org.objectweb.asm.tree.*
private class ParameterNameAndNullability(val name: String, val nullable: Boolean)
public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParameterMappings?) {
companion object {
@@ -64,15 +70,23 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
Type.getMethodDescriptor(Type.VOID_TYPE), false
);
}
@JvmStatic
public fun isNullableMarkerInstruction(marker: String) = INSTANCEOF_MARKER_METHOD_NAME == marker ||
CHECKCAST_MARKER_METHOD_NAME == marker
}
private var maxStackSize = 0
/**
* @return set of type parameters' identifiers contained in markers that should be reified further
* e.g. when we're generating inline function containing reified T
* and another function containing reifiable parts is inlined into that function
*/
public fun reifyInstructions(instructions: InsnList): ReifiedTypeParametersUsages {
public fun reifyInstructions(node: MethodNode): ReifiedTypeParametersUsages {
if (parametersMapping == null) return ReifiedTypeParametersUsages()
val instructions = node.instructions
maxStackSize = 0
var result = ReifiedTypeParametersUsages()
for (insn in instructions.toArray()) {
if (isParametrisedReifiedMarker(insn)) {
@@ -83,6 +97,7 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
}
}
node.maxStack = node.maxStack + maxStackSize
return result
}
@@ -127,20 +142,25 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
* or null if it shouldn't
*/
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList): String? {
val mapping = getTypeParameterMapping(insn) ?: return null
val parameter = getParameter(insn) ?: return null
val mapping = parametersMapping?.get(parameter.name) ?: return null
val kotlinType =
if (isNullableMarkerInstruction(insn.name) && parameter.nullable)
TypeUtils.makeNullable(mapping.type)
else
mapping.type
val asmType = mapping.asmType
if (asmType != null) {
val jetType = mapping.type ?: return null
// process* methods return false if marker should be reified further
// or it's invalid (may be emitted explicitly in code)
// they return true if instruction is reified and marker can be deleted
if (when (insn.name) {
NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, asmType)
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, jetType, asmType, safe = false)
SAFE_CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, jetType, asmType, safe = true)
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, instructions, jetType, asmType)
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, kotlinType, asmType, safe = false)
SAFE_CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, instructions, kotlinType, asmType, safe = true)
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, instructions, kotlinType, asmType)
JAVA_CLASS_MARKER_METHOD_NAME -> processJavaClass(insn, asmType)
else -> false
}) {
@@ -150,7 +170,8 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
return null
} else {
instructions.set(insn.getPrevious()!!, LdcInsnNode(mapping.newName))
val nullableSuffix = if (isNullableMarkerInstruction(insn.name) && kotlinType.isMarkedNullable) "?" else ""
instructions.set(insn.previous!!, LdcInsnNode(mapping.newName + nullableSuffix))
return mapping.newName
}
}
@@ -158,20 +179,66 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
private fun processCheckcast(insn: MethodInsnNode, instructions: InsnList, jetType: KotlinType, asmType: Type, safe: Boolean) =
private fun processCheckcast(insn: MethodInsnNode,
instructions: InsnList,
jetType: KotlinType,
asmType: Type,
safe: Boolean) =
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode ->
if (instanceofInsn !is TypeInsnNode) return false
addNullCheckForAsIfNeeded(insn.previous!!, instructions, jetType, safe)
TypeIntrinsics.checkcast(instanceofInsn, instructions, jetType, asmType, safe)
return true
}
private fun addNullCheckForAsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, jetType: KotlinType, safe: Boolean) {
if (!safe && !TypeUtils.isNullableType(jetType)) {
val methodNode = MethodNode(InlineCodegenUtil.API)
generateNullCheckForNonSafeAs(InstructionAdapter(methodNode), jetType)
InlineCodegenUtil.insertNodeBefore(methodNode, instructions, insn)
maxStackSize = Math.max(maxStackSize, 4)
}
}
private fun processInstanceof(insn: MethodInsnNode, instructions: InsnList, jetType: KotlinType, asmType: Type) =
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { instanceofInsn: AbstractInsnNode ->
if (instanceofInsn !is TypeInsnNode) return false
addNullCheckForIsIfNeeded(insn, instructions, jetType)
TypeIntrinsics.instanceOf(instanceofInsn, instructions, jetType, asmType)
return true
}
private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) {
if (TypeUtils.isNullableType(type)) {
val instanceOf = insn.next
insertNullCheckAround(instructions, insn.previous!!, instanceOf)
maxStackSize = Math.max(maxStackSize, 2)
}
}
private fun insertNullCheckAround(instructions: InsnList, start: AbstractInsnNode, end: AbstractInsnNode) {
val methodNode = MethodNode(InlineCodegenUtil.API)
var splitIndex: Int = -1
generateIsCheck(InstructionAdapter(methodNode), true) {
splitIndex = methodNode.instructions.size()
}
assert(splitIndex >= 0) {
"Split index should be non-negative, but $splitIndex"
}
val nullCheckInsns = methodNode.instructions.toArray()
nullCheckInsns.take(splitIndex).forEach {
instructions.insertBefore(start, it)
}
nullCheckInsns.drop(splitIndex).reversed().forEach {
instructions.insert(end, it)
}
}
inline private fun rewriteNextTypeInsn(
marker: MethodInsnNode,
expectedNextOpcode: Int,
@@ -195,17 +262,16 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
return true
}
private fun getParameterName(insn: MethodInsnNode): String? {
private fun getParameter(insn: MethodInsnNode): ParameterNameAndNullability? {
val prev = insn.getPrevious()!!
return when (prev.getOpcode()) {
val parameterNameWithFlag = when (prev.getOpcode()) {
Opcodes.LDC -> (prev as LdcInsnNode).cst as String
else -> null
else -> return null
}
}
private fun getTypeParameterMapping(insn: MethodInsnNode): ReifiedTypeParameterMapping? {
return parametersMapping?.get(getParameterName(insn) ?: return null)
val parameterName = if (parameterNameWithFlag.endsWith("?")) parameterNameWithFlag.dropLast(1) else parameterNameWithFlag
return ParameterNameAndNullability(parameterName, parameterName !== parameterNameWithFlag)
}
}
@@ -216,8 +282,8 @@ public class ReifiedTypeParameterMappings() {
mappingsByName[name] = ReifiedTypeParameterMapping(name, type, asmType, newName = null, signature = signature)
}
public fun addParameterMappingToNewParameter(name: String, newName: String) {
mappingsByName[name] = ReifiedTypeParameterMapping(name, type = null, asmType = null, newName = newName, signature = null)
public fun addParameterMappingToNewParameter(name: String, type: KotlinType, newName: String) {
mappingsByName[name] = ReifiedTypeParameterMapping(name, type = type, asmType = null, newName = newName, signature = null)
}
operator fun get(name: String): ReifiedTypeParameterMapping? {
@@ -226,7 +292,7 @@ public class ReifiedTypeParameterMappings() {
}
public class ReifiedTypeParameterMapping(
val name: String, val type: KotlinType?, val asmType: Type?, val newName: String?, val signature: String?
val name: String, val type: KotlinType, val asmType: Type?, val newName: String?, val signature: String?
)
public class ReifiedTypeParametersUsages {
@@ -0,0 +1,36 @@
import test.*
fun box(): String {
if (null.foo3<Any>() != null) return "fail 1"
if (null.foo3<Any?>() != null) return "fail 2"
if (null.foo3<A>() != null) return "fail 3"
if (null.foo3<A?>() != null) return "fail 4"
val a = A()
if (a.foo3<Any>() != a) return "fail 5"
if (a.foo3<Any?>() != a) return "fail 6"
if (a.foo3<A>() != a) return "fail 7"
if (a.foo3<A?>() != a) return "fail 8"
val b = B()
failClassCast { b.foo3<A>(); return "failTypeCast 9" }
failClassCast { b.foo3<A?>(); return "failTypeCast 10" }
return "OK"
}
inline fun failClassCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
throw e
}
catch (e: ClassCastException) {
}
}
@@ -0,0 +1,10 @@
package test
class A
class B
inline fun <reified T> Any?.foo(): T = this as T
inline fun <reified Y> Any?.foo2(): Y? = foo<Y?>()
inline fun <reified Z> Any?.foo3(): Z? = foo2<Z>()
@@ -0,0 +1,18 @@
import test.*
fun case1(): Int =
null.castTo<Int?, Int>()
fun box(): String {
failTypeCast { case1(); return "failTypeCast 9" }
return "OK"
}
inline fun failTypeCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
}
}
@@ -0,0 +1,3 @@
package test
inline fun <reified T, reified R>T.castTo(): R = this as R
@@ -0,0 +1,10 @@
import test.*
fun box(): String {
val a = A()
if (a.foo<Any>() != a) return "failTypeCast 5"
return "OK"
}
@@ -0,0 +1,13 @@
package test
class A
fun call(a: String, b: String, c: String, d: String, e: String, f: Any) {
}
inline fun <reified T: Any> Any?.foo(): T {
call("1", "2", "3", "4", "5", this as T)
return this as T
}
@@ -0,0 +1,45 @@
import test.*
fun box(): String {
if (null.foo<Any>() != null) return "failTypeCast 1"
if (null.foo<Any?>() != null) return "failTypeCast 2"
if (null.foo<A>() != null) return "failTypeCast 3"
if (null.foo<A?>() != null) return "failTypeCast 4"
val a = A()
if (a.foo<Any>() != a) return "failTypeCast 5"
if (a.foo<Any?>() != a) return "failTypeCast 6"
if (a.foo<A>() != a) return "failTypeCast 7"
if (a.foo<A?>() != a) return "failTypeCast 8"
val b = B()
failClassCast { b.foo<A>(); return "failTypeCast 9" }
failClassCast { b.foo<A?>(); return "failTypeCast 10" }
return "OK"
}
inline fun failTypeCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
}
}
inline fun failClassCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
throw e
}
catch (e: ClassCastException) {
}
}
@@ -0,0 +1,6 @@
package test
class A
class B
inline fun <reified T> Any?.foo(): T? = this as T?
@@ -0,0 +1,45 @@
import test.*
fun box(): String {
failTypeCast { null.foo<Any>(); return "failTypeCast 1" }
if (null.foo<Any?>() != null) return "failTypeCast 2"
failTypeCast { null.foo<A>(); return "failTypeCast 3" }
if (null.foo<A?>() != null) return "failTypeCast 4"
val a = A()
if (a.foo<Any>() != a) return "failTypeCast 5"
if (a.foo<Any?>() != a) return "failTypeCast 6"
if (a.foo<A>() != a) return "failTypeCast 7"
if (a.foo<A?>() != a) return "failTypeCast 8"
val b = B()
failClassCast { b.foo<A>(); return "failTypeCast 9" }
failClassCast { b.foo<A?>(); return "failTypeCast 10" }
return "OK"
}
inline fun failTypeCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
}
}
inline fun failClassCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
throw e
}
catch (e: ClassCastException) {
}
}
@@ -0,0 +1,6 @@
package test
class A
class B
inline fun <reified T> Any?.foo(): T = this as T
@@ -0,0 +1,45 @@
import test.*
fun box(): String {
if (null.foo<Any>() != null) return "failTypeCast 1"
if (null.foo<Any?>() != null) return "failTypeCast 2"
if (null.foo<A>() != null) return "failTypeCast 3"
if (null.foo<A?>() != null) return "failTypeCast 4"
val a = A()
if (a.foo<Any>() != a) return "failTypeCast 5"
if (a.foo<Any?>() != a) return "failTypeCast 6"
if (a.foo<A>() != a) return "failTypeCast 7"
if (a.foo<A?>() != a) return "failTypeCast 8"
val b = B()
if (b.foo<A>() != null) return "fail 9"
if (b.foo<A?>() != null) return "fail 10"
return "OK"
}
inline fun failTypeCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
}
}
inline fun failClassCast(s: () -> Unit) {
try {
s()
}
catch (e: TypeCastException) {
throw e
}
catch (e: ClassCastException) {
}
}
@@ -0,0 +1,6 @@
package test
class A
class B
inline fun <reified T> Any?.foo(): T? = this as? T
@@ -0,0 +1,24 @@
import test.*
fun box(): String {
if (null.foo3<Any>() != true) return "fail 1"
if (null.foo3<Any?>() != true) return "fail 2"
if (null.foo3<A>() != true) return "fail 3"
if (null.foo3<A?>() != true) return "fail 4"
val a = A()
if (a.foo3<Any>() != true) return "fail 5"
if (a.foo3<Any?>() != true) return "fail 6"
if (a.foo3<A>() != true) return "fail 7"
if (a.foo3<A?>() != true) return "fail 8"
val b = B()
if (b.foo3<A>() != false) return "fail 9"
if (b.foo3<A?>() != false) return "fail 10"
return "OK"
}
@@ -0,0 +1,10 @@
package test
class A
class B
inline fun <reified T> Any?.foo() = this is T
inline fun <reified Y> Any?.foo2() = foo<Y?>()
inline fun <reified Z> Any?.foo3() = foo2<Z>()
@@ -0,0 +1,24 @@
import test.*
fun box(): String {
if (null.foo<Any>() != true) return "fail 1"
if (null.foo<Any?>() != true) return "fail 2"
if (null.foo<A>() != true) return "fail 3"
if (null.foo<A?>() != true) return "fail 4"
val a = A()
if (a.foo<Any>() != true) return "fail 5"
if (a.foo<Any?>() != true) return "fail 6"
if (a.foo<A>() != true) return "fail 7"
if (a.foo<A?>() != true) return "fail 8"
val b = B()
if (b.foo<A>() != false) return "fail 9"
if (b.foo<A?>() != false) return "fail 10"
return "OK"
}
@@ -0,0 +1,6 @@
package test
class A
class B
inline fun <reified T> Any?.foo() = this is T?
@@ -0,0 +1,24 @@
import test.*
fun box(): String {
if (null.foo<Any>() != false) return "fail 1"
if (null.foo<Any?>() != true) return "fail 2"
if (null.foo<A>() != false) return "fail 3"
if (null.foo<A?>() != true) return "fail 4"
val a = A()
if (a.foo<Any>() != true) return "fail 5"
if (a.foo<Any?>() != true) return "fail 6"
if (a.foo<A>() != true) return "fail 7"
if (a.foo<A?>() != true) return "fail 8"
val b = B()
if (b.foo<A>() != false) return "fail 9"
if (b.foo<A?>() != false) return "fail 10"
return "OK"
}
@@ -0,0 +1,6 @@
package test
class A
class B
inline fun <reified T> Any?.foo() = this is T
@@ -0,0 +1,11 @@
public class JavaClass {
public static String nullString() {
return null;
}
public static String nonnullString() {
return "OK";
}
}
@@ -0,0 +1,16 @@
fun box(): String {
val nullStr = JavaClass.nullString()
val nonnullStr = JavaClass.nonnullString()
if (nullStr.foo() != null) return "fail 1"
if (nonnullStr.foo() != nonnullStr) return "fail 2"
if (nullStr.fooN() != null) return "fail 3"
if (nonnullStr.fooN() != nonnullStr) return "fail 4"
return "OK"
}
inline fun <reified T> T.foo(): T = this as T
inline fun <reified T> T.fooN(): T? = this as T?
@@ -0,0 +1,11 @@
public class JavaClass {
public static String nullString() {
return null;
}
public static String nonnullString() {
return "OK";
}
}
@@ -0,0 +1,16 @@
fun box(): String {
val nullStr = JavaClass.nullString()
val nonnullStr = JavaClass.nonnullString()
if (nullStr.foo() != true) return "fail 1"
if (nonnullStr.foo() != true) return "fail 2"
if (nullStr.fooN() != true) return "fail 3"
if (nonnullStr.fooN() != true) return "fail 4"
return "OK"
}
inline fun <reified T> T.foo(): Boolean = this is T
inline fun <reified T> T.fooN(): Boolean = this is T?
@@ -0,0 +1,15 @@
inline fun <reified T> Any?.foo() = this as T
inline fun <reified Y> Any?.foo2() = foo<Y?>()
inline fun <reified Z> Any?.foo3() = foo2<Z>()
inline fun <reified X> Any?.foo4() = foo2<X?>()
inline fun <reified A> Any?.foo5() = foo<A>()
// 1 LDC "T"
// 1 LDC "Y\?"
// 1 LDC "Z\?"
// 1 LDC "X\?"
// 1 LDC "A"
@@ -0,0 +1,15 @@
inline fun <reified T> Any?.foo() = this as T?
inline fun <reified Y> Any?.foo2() = foo<Y?>()
inline fun <reified Z> Any?.foo3() = foo2<Z>()
inline fun <reified X> Any?.foo4() = foo2<X?>()
inline fun <reified A> Any?.foo5() = foo<A>()
// 1 LDC "T\?"
// 1 LDC "Y\?"
// 1 LDC "Z\?"
// 1 LDC "X\?"
// 1 LDC "A\?"
@@ -0,0 +1,15 @@
inline fun <reified T> Any?.foo() = this is T
inline fun <reified Y> Any?.foo2() = foo<Y?>()
inline fun <reified Z> Any?.foo3() = foo2<Z>()
inline fun <reified X> Any?.foo4() = foo2<X?>()
inline fun <reified A> Any?.foo5() = foo<A>()
// 1 LDC "T"
// 1 LDC "Y\?"
// 1 LDC "Z\?"
// 1 LDC "X\?"
// 1 LDC "A"
@@ -0,0 +1,15 @@
inline fun <reified T> Any?.foo() = this is T?
inline fun <reified Y> Any?.foo2() = foo<Y?>()
inline fun <reified Z> Any?.foo3() = foo2<Z>()
inline fun <reified X> Any?.foo4() = foo2<X?>()
inline fun <reified A> Any?.foo5() = foo<A>()
// 1 LDC "T\?"
// 1 LDC "Y\?"
// 1 LDC "Z\?"
// 1 LDC "X\?"
// 1 LDC "A\?"
@@ -257,6 +257,30 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("reifiedAsCheck.kt")
public void testReifiedAsCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/reifiedAsCheck.kt");
doTest(fileName);
}
@TestMetadata("reifiedAsCheckWithNullable.kt")
public void testReifiedAsCheckWithNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/reifiedAsCheckWithNullable.kt");
doTest(fileName);
}
@TestMetadata("reifiedIsCheck.kt")
public void testReifiedIsCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/reifiedIsCheck.kt");
doTest(fileName);
}
@TestMetadata("reifiedIsCheckWithNullable.kt")
public void testReifiedIsCheckWithNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/reifiedIsCheckWithNullable.kt");
doTest(fileName);
}
@TestMetadata("safeAsWithMutable.kt")
public void testSafeAsWithMutable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/safeAsWithMutable.kt");
@@ -1293,6 +1293,78 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/packages.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CheckCast extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInCheckCast() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/reified/checkCast"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("chain.1.kt")
public void testChain() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/chain.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("kt8043.1.kt")
public void testKt8043() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/kt8043.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("maxStack.1.kt")
public void testMaxStack() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/maxStack.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("nullable.1.kt")
public void testNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/nullable.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("simple.1.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/simple.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("simpleSafe.1.kt")
public void testSimpleSafe() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/simpleSafe.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/isCheck")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class IsCheck extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInIsCheck() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/reified/isCheck"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("chain.1.kt")
public void testChain() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/isCheck/chain.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("nullable.1.kt")
public void testNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/isCheck/nullable.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("simple.1.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/isCheck/simple.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
}
}
@TestMetadata("compiler/testData/codegen/boxInline/simple")
@@ -673,6 +673,28 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
}
@TestMetadata("compiler/testData/codegen/boxWithJava/reified")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Reified extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInReified() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava/reified"), Pattern.compile("^([^\\.]+)$"), true);
}
@TestMetadata("asCheck")
public void testAsCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reified/asCheck/");
doTestWithJava(fileName);
}
@TestMetadata("isCheck")
public void testIsCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reified/isCheck/");
doTestWithJava(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithJava/secondaryConstructors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1293,6 +1293,78 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/packages.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/checkCast")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CheckCast extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInCheckCast() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/reified/checkCast"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("chain.1.kt")
public void testChain() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/chain.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("kt8043.1.kt")
public void testKt8043() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/kt8043.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("maxStack.1.kt")
public void testMaxStack() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/maxStack.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("nullable.1.kt")
public void testNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/nullable.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("simple.1.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/simple.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("simpleSafe.1.kt")
public void testSimpleSafe() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/checkCast/simpleSafe.1.kt");
doBoxTestWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/reified/isCheck")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class IsCheck extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInIsCheck() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/reified/isCheck"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("chain.1.kt")
public void testChain() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/isCheck/chain.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("nullable.1.kt")
public void testNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/isCheck/nullable.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("simple.1.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/reified/isCheck/simple.1.kt");
doBoxTestWithInlineCheck(fileName);
}
}
}
@TestMetadata("compiler/testData/codegen/boxInline/simple")