JVM_IR: refactor ToArrayLowering and make matching more precise
* If `toArray` is inherited from Java, it may take an argument or
return a value of a flexible type, which looks nullable in IR;
* The returned array may also have `out` variance of the type
argument;
* `Array<T>` is not the same as `Array<Any?>` if `T` is neither `Any`
nor `in Something`, so presence of `toArray(): Array<T>` does not
mean we don't need to generate a new `toArray`.
This commit is contained in:
Generated
+5
@@ -26683,6 +26683,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt");
|
runTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("toArrayFromJava.kt")
|
||||||
|
public void testToArrayFromJava() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/toArray/toArrayFromJava.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toArrayShouldBePublic.kt")
|
@TestMetadata("toArrayShouldBePublic.kt")
|
||||||
public void testToArrayShouldBePublic() throws Exception {
|
public void testToArrayShouldBePublic() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt");
|
runTest("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt");
|
||||||
|
|||||||
+2
-3
@@ -559,9 +559,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
|||||||
if (!withSuper && !declaration.visibility.isPrivate && !declaration.visibility.isProtected) return true
|
if (!withSuper && !declaration.visibility.isPrivate && !declaration.visibility.isProtected) return true
|
||||||
|
|
||||||
//`toArray` is always accessible cause mapped to public functions
|
//`toArray` is always accessible cause mapped to public functions
|
||||||
if (symbolOwner is IrSimpleFunction && (symbolOwner.isNonGenericToArray() || symbolOwner.isGenericToArray(context))) {
|
if (symbolOwner is IrSimpleFunction && (symbolOwner.isNonGenericToArray(context) || symbolOwner.isGenericToArray(context))) {
|
||||||
val parent = symbolOwner.parent
|
if (symbolOwner.parentAsClass.isCollectionSubClass) {
|
||||||
if (parent is IrClass && parent.isCollectionSubClass()) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-177
@@ -11,33 +11,27 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
|||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.builders.declarations.addDispatchReceiver
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
import org.jetbrains.kotlin.ir.builders.irCall
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irGet
|
||||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
|
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedTypeParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
|
import org.jetbrains.kotlin.ir.util.functions
|
||||||
import org.jetbrains.kotlin.ir.util.isClass
|
import org.jetbrains.kotlin.ir.util.isClass
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
|
|
||||||
internal val toArrayPhase = makeIrFilePhase(
|
internal val toArrayPhase = makeIrFilePhase(
|
||||||
@@ -47,188 +41,99 @@ internal val toArrayPhase = makeIrFilePhase(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private class ToArrayLowering(private val context: JvmBackendContext) : ClassLoweringPass {
|
private class ToArrayLowering(private val context: JvmBackendContext) : ClassLoweringPass {
|
||||||
|
private val symbols = context.ir.symbols
|
||||||
|
|
||||||
override fun lower(irClass: IrClass) {
|
override fun lower(irClass: IrClass) {
|
||||||
if (irClass.isJvmInterface || !irClass.isCollectionSubClass()) return
|
if (irClass.isJvmInterface || !irClass.isCollectionSubClass) return
|
||||||
|
|
||||||
val irBuiltIns = context.irBuiltIns
|
val indirectCollectionSubClass = generateSequence(irClass.superClass, IrClass::superClass).firstOrNull {
|
||||||
val symbols = context.ir.symbols
|
it.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
|
||||||
|
}?.isCollectionSubClass == true
|
||||||
|
|
||||||
val toArrayName = Name.identifier("toArray")
|
irClass.findOrCreate(indirectCollectionSubClass, { it.isGenericToArray(context) }) {
|
||||||
val genericToArray = irClass.declarations.filterIsInstance<IrSimpleFunction>().find { it.isGenericToArray(context) }
|
irClass.addFunction {
|
||||||
val nonGenericToArray = irClass.declarations.filterIsInstance<IrSimpleFunction>().find { it.isNonGenericToArray() }
|
name = Name.identifier("toArray")
|
||||||
val isDirectCollectionSubClass = irClass.isDirectCollectionSubClass()
|
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||||
if (genericToArray == null) {
|
modality = Modality.OPEN
|
||||||
if (isDirectCollectionSubClass) {
|
}.apply {
|
||||||
val typeParameterDescriptor = WrappedTypeParameterDescriptor()
|
val elementType = addTypeParameter {
|
||||||
val typeParameter = IrTypeParameterImpl(
|
name = Name.identifier("T")
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
superTypes.add(context.irBuiltIns.anyNType)
|
||||||
IrTypeParameterSymbolImpl(typeParameterDescriptor),
|
|
||||||
Name.identifier("T"),
|
|
||||||
index = 0,
|
|
||||||
variance = Variance.INVARIANT,
|
|
||||||
isReified = false
|
|
||||||
).apply {
|
|
||||||
typeParameterDescriptor.bind(this)
|
|
||||||
superTypes.add(irBuiltIns.anyNType)
|
|
||||||
}
|
}
|
||||||
|
returnType = context.irBuiltIns.arrayClass.typeWith(elementType.defaultType)
|
||||||
val substitutedArrayType = irBuiltIns.arrayClass.typeWith(typeParameter.defaultType)
|
val receiver = addDispatchReceiver {
|
||||||
val functionDescriptor = WrappedSimpleFunctionDescriptor()
|
type = irClass.defaultType
|
||||||
val irFunction = IrFunctionImpl(
|
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
|
||||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
|
||||||
IrSimpleFunctionSymbolImpl(functionDescriptor),
|
|
||||||
toArrayName,
|
|
||||||
Visibilities.PUBLIC,
|
|
||||||
Modality.OPEN,
|
|
||||||
returnType = substitutedArrayType,
|
|
||||||
isInline = false,
|
|
||||||
isExternal = false,
|
|
||||||
isTailrec = false,
|
|
||||||
isSuspend = false,
|
|
||||||
isExpect = false,
|
|
||||||
isFakeOverride = false,
|
|
||||||
isOperator = false
|
|
||||||
)
|
|
||||||
functionDescriptor.bind(irFunction)
|
|
||||||
irFunction.parent = irClass
|
|
||||||
|
|
||||||
typeParameter.parent = irFunction
|
|
||||||
irFunction.typeParameters += typeParameter
|
|
||||||
|
|
||||||
val dispatchReceiverParameterDescriptor = WrappedValueParameterDescriptor()
|
|
||||||
irFunction.dispatchReceiverParameter = IrValueParameterImpl(
|
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
|
||||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
|
||||||
IrValueParameterSymbolImpl(dispatchReceiverParameterDescriptor),
|
|
||||||
Name.special("<this>"),
|
|
||||||
index = -1,
|
|
||||||
type = irClass.defaultType,
|
|
||||||
varargElementType = null,
|
|
||||||
isCrossinline = false,
|
|
||||||
isNoinline = false
|
|
||||||
).apply {
|
|
||||||
parent = irFunction
|
|
||||||
}
|
}
|
||||||
val valueParameterDescriptor = WrappedValueParameterDescriptor()
|
val prototype = addValueParameter("array", returnType, JvmLoweredDeclarationOrigin.TO_ARRAY)
|
||||||
irFunction.valueParameters +=
|
body = context.createIrBuilder(symbol).irBlockBody {
|
||||||
IrValueParameterImpl(
|
+irReturn(irCall(symbols.genericToArray, symbols.genericToArray.owner.returnType).apply {
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
putValueArgument(0, irGet(receiver))
|
||||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
putValueArgument(1, irGet(prototype))
|
||||||
IrValueParameterSymbolImpl(valueParameterDescriptor),
|
})
|
||||||
Name.identifier("array"),
|
|
||||||
index = 0,
|
|
||||||
varargElementType = null,
|
|
||||||
type = substitutedArrayType,
|
|
||||||
isCrossinline = false,
|
|
||||||
isNoinline = false
|
|
||||||
).apply {
|
|
||||||
valueParameterDescriptor.bind(this)
|
|
||||||
parent = irFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody {
|
|
||||||
+irReturn(
|
|
||||||
irCall(symbols.genericToArray, symbols.genericToArray.owner.returnType).apply {
|
|
||||||
putValueArgument(
|
|
||||||
0,
|
|
||||||
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irFunction.dispatchReceiverParameter!!.symbol)
|
|
||||||
)
|
|
||||||
putValueArgument(1, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irFunction.valueParameters[0].symbol))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irClass.declarations.add(irFunction)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
(genericToArray as IrFunctionImpl).visibility = Visibilities.PUBLIC
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonGenericToArray == null) {
|
irClass.findOrCreate(indirectCollectionSubClass, { it.isNonGenericToArray(context) }) {
|
||||||
if (isDirectCollectionSubClass) {
|
irClass.addFunction {
|
||||||
val functionDescriptor = WrappedSimpleFunctionDescriptor()
|
name = Name.identifier("toArray")
|
||||||
val irFunction = IrFunctionImpl(
|
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
modality = Modality.OPEN
|
||||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
returnType = context.irBuiltIns.arrayClass.typeWith(context.irBuiltIns.anyNType)
|
||||||
IrSimpleFunctionSymbolImpl(functionDescriptor),
|
}.apply {
|
||||||
toArrayName,
|
val receiver = addDispatchReceiver {
|
||||||
Visibilities.PUBLIC,
|
type = irClass.defaultType
|
||||||
Modality.OPEN,
|
origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||||
returnType = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyNType),
|
|
||||||
isInline = false,
|
|
||||||
isExternal = false,
|
|
||||||
isTailrec = false,
|
|
||||||
isSuspend = false,
|
|
||||||
isExpect = false,
|
|
||||||
isFakeOverride = false,
|
|
||||||
isOperator = false
|
|
||||||
)
|
|
||||||
functionDescriptor.bind(irFunction)
|
|
||||||
irFunction.parent = irClass
|
|
||||||
|
|
||||||
val dispatchReceiverParameterDescriptor = WrappedValueParameterDescriptor()
|
|
||||||
irFunction.dispatchReceiverParameter = IrValueParameterImpl(
|
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
|
||||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
|
||||||
IrValueParameterSymbolImpl(dispatchReceiverParameterDescriptor),
|
|
||||||
Name.special("<this>"),
|
|
||||||
index = -1,
|
|
||||||
type = irClass.defaultType,
|
|
||||||
varargElementType = null,
|
|
||||||
isCrossinline = false,
|
|
||||||
isNoinline = false
|
|
||||||
).apply {
|
|
||||||
parent = irFunction
|
|
||||||
}
|
}
|
||||||
|
body = context.createIrBuilder(symbol).irBlockBody {
|
||||||
irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody {
|
+irReturn(irCall(symbols.nonGenericToArray, symbols.nonGenericToArray.owner.returnType).apply {
|
||||||
+irReturn(
|
putValueArgument(0, irGet(receiver))
|
||||||
irCall(symbols.nonGenericToArray, symbols.nonGenericToArray.owner.returnType).apply {
|
})
|
||||||
putValueArgument(
|
|
||||||
0,
|
|
||||||
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irFunction.dispatchReceiverParameter!!.symbol)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irClass.declarations.add(irFunction)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
(nonGenericToArray as IrFunctionImpl).visibility = Visibilities.PUBLIC
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IrClass.findOrCreate(indirectSubclass: Boolean, matcher: (IrSimpleFunction) -> Boolean, fallback: () -> IrSimpleFunction) {
|
||||||
|
val existing = functions.find(matcher) as? IrFunctionImpl
|
||||||
|
if (existing != null) {
|
||||||
|
// This is an explicit override of a method defined in `kotlin.collections.AbstractCollection`
|
||||||
|
// or `java.util.Collection`. From here on, the frontend will check the existence of implementations;
|
||||||
|
// we just need to match visibility in the former case to the latter.
|
||||||
|
existing.visibility = Visibilities.PUBLIC
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (indirectSubclass) {
|
||||||
|
// There's a Kotlin class up the hierarchy that should already have `toArray`.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrClass.superClasses
|
private val IrClass.superClass: IrClass?
|
||||||
get() = superTypes.mapNotNull { it.getClass() }
|
get() = superTypes.mapNotNull { it.getClass()?.takeIf { superClass -> superClass.isClass } }.singleOrNull()
|
||||||
|
|
||||||
// Have to check by name, since irBuiltins is unreliable.
|
internal val IrClass.isCollectionSubClass: Boolean
|
||||||
internal fun IrClass.isCollectionSubClass() =
|
get() = DFS.ifAny(superTypes, { it.getClass()?.superTypes ?: listOf() }) { it.isCollection() }
|
||||||
DFS.ifAny(listOf(this), IrClass::superClasses) { it.defaultType.isCollection() }
|
|
||||||
|
|
||||||
// If this class inherits from another Kotlin class that implements Collection, it already has toArray.
|
private fun IrType.isArrayOrNullableArrayOf(context: JvmBackendContext, element: IrClassifierSymbol): Boolean =
|
||||||
private fun IrClass.isDirectCollectionSubClass() =
|
this is IrSimpleType && (isArray() || isNullableArray()) && arguments.size == 1 && element == when (val it = arguments[0]) {
|
||||||
isCollectionSubClass() && !superClasses.any {
|
is IrStarProjection -> context.irBuiltIns.anyClass
|
||||||
it.isClass && it.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && it.isCollectionSubClass()
|
is IrTypeProjection -> if (it.variance == Variance.IN_VARIANCE) context.irBuiltIns.anyClass else it.type.classifierOrNull
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match `fun <T> toArray(prototype: Array<T>): Array<T>`
|
||||||
|
internal fun IrSimpleFunction.isGenericToArray(context: JvmBackendContext): Boolean =
|
||||||
|
name.asString() == "toArray" && typeParameters.size == 1 && valueParameters.size == 1 &&
|
||||||
|
extensionReceiverParameter == null &&
|
||||||
|
returnType.isArrayOrNullableArrayOf(context, typeParameters[0].symbol) &&
|
||||||
|
valueParameters[0].type.isArrayOrNullableArrayOf(context, typeParameters[0].symbol)
|
||||||
|
|
||||||
internal fun IrSimpleFunction.isGenericToArray(context: JvmBackendContext): Boolean {
|
// Match `fun toArray(): Array<Any?>`
|
||||||
if (name.asString() != "toArray") return false
|
internal fun IrSimpleFunction.isNonGenericToArray(context: JvmBackendContext): Boolean =
|
||||||
if (typeParameters.size != 1 || valueParameters.size != 1 || extensionReceiverParameter != null) return false
|
name.asString() == "toArray" && typeParameters.isEmpty() && valueParameters.isEmpty() &&
|
||||||
|
extensionReceiverParameter == null && returnType.isArrayOrNullableArrayOf(context, context.irBuiltIns.anyClass)
|
||||||
val paramType = valueParameters[0].type
|
|
||||||
|
|
||||||
if (!returnType.isArray() || !paramType.isArray()) return false
|
|
||||||
|
|
||||||
val elementType = typeParameters[0].defaultType
|
|
||||||
val expectedType = context.ir.symbols.array.typeWith(elementType)
|
|
||||||
return expectedType == paramType && expectedType == returnType
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun IrSimpleFunction.isNonGenericToArray(): Boolean {
|
|
||||||
if (name.asString() != "toArray") return false
|
|
||||||
if (typeParameters.isNotEmpty() || valueParameters.isNotEmpty() || extensionReceiverParameter != null) return false
|
|
||||||
return returnType.isArray()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// The old backend thinks `toArray(): Array<Int?>` is the same as `toArray(): Array<Any?>`
|
||||||
|
// IGNORE_BACKEND: JVM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// FILE: MyListWithCustomToArray.java
|
||||||
|
|
||||||
|
public abstract class MyListWithCustomToArray<E> extends java.util.AbstractList<E> {
|
||||||
|
public Object[] toArray() {
|
||||||
|
return new Object[]{null};
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T[] toArray(T[] a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: a.kt
|
||||||
|
class MyList<T>(val list: List<T>): java.util.AbstractList<T>() {
|
||||||
|
override fun get(index: Int): T = list[index]
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = list.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyListSubclass<T>(val list: List<T>): MyListWithCustomToArray<T>() {
|
||||||
|
override fun get(index: Int): T = list[index]
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = list.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyCollection<T>(val list: List<T>) : Collection<T> by list {
|
||||||
|
fun toArray(): Array<Int?> =
|
||||||
|
arrayOfNulls<Int>(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val list1 = MyList(listOf(2, 3, 9)) as java.util.Collection<*>
|
||||||
|
list1.toArray().contentToString().let { if (it != "[2, 3, 9]") return "fail 1: $it" }
|
||||||
|
list1.toArray(arrayOfNulls<Int>(0)).contentToString().let { if (it != "[2, 3, 9]") return "fail 2: $it" }
|
||||||
|
|
||||||
|
val list2 = MyListSubclass(listOf(2, 3, 9)) as java.util.Collection<*>
|
||||||
|
list2.toArray().contentToString().let { if (it != "[null]") return "fail 3: $it" }
|
||||||
|
list2.toArray(arrayOfNulls<Int>(1)).contentToString().let { if (it != "[null]") return "fail 4: $it" }
|
||||||
|
|
||||||
|
val list3 = MyCollection(listOf(2, 3, 9)) as java.util.Collection<*>
|
||||||
|
list3.toArray().contentToString().let { if (it != "[2, 3, 9]") return "fail 5: $it" }
|
||||||
|
list3.toArray(arrayOfNulls<Int>(0)).contentToString().let { if (it != "[2, 3, 9]") return "fail 6: $it" }
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
// FILE: SingletonCollection.kt
|
// FILE: SingletonCollection.kt
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// IGNORE_LIGHT_ANALYSIS
|
// IGNORE_LIGHT_ANALYSIS
|
||||||
|
|
||||||
|
|||||||
+5
@@ -28199,6 +28199,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt");
|
runTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("toArrayFromJava.kt")
|
||||||
|
public void testToArrayFromJava() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/toArray/toArrayFromJava.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toArrayShouldBePublic.kt")
|
@TestMetadata("toArrayShouldBePublic.kt")
|
||||||
public void testToArrayShouldBePublic() throws Exception {
|
public void testToArrayShouldBePublic() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt");
|
runTest("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt");
|
||||||
|
|||||||
+5
@@ -26983,6 +26983,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class ToArray extends AbstractLightAnalysisModeTest {
|
public static class ToArray extends AbstractLightAnalysisModeTest {
|
||||||
|
@TestMetadata("toArrayFromJava.kt")
|
||||||
|
public void ignoreToArrayFromJava() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/toArray/toArrayFromJava.kt");
|
||||||
|
}
|
||||||
|
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -26683,6 +26683,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt");
|
runTest("compiler/testData/codegen/box/toArray/toArrayAlreadyPresent.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("toArrayFromJava.kt")
|
||||||
|
public void testToArrayFromJava() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/toArray/toArrayFromJava.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("toArrayShouldBePublic.kt")
|
@TestMetadata("toArrayShouldBePublic.kt")
|
||||||
public void testToArrayShouldBePublic() throws Exception {
|
public void testToArrayShouldBePublic() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt");
|
runTest("compiler/testData/codegen/box/toArray/toArrayShouldBePublic.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user