Value classes: Allow unsigned arrays in annotations

including varargs, apparently.
So, we allow unsigned types and unsigned arrays in annotations,
but disallow user-defined inline classes.
 #KT-23816 Fixed
This commit is contained in:
Ilmir Usmanov
2020-12-02 20:14:43 +01:00
parent a9c072f826
commit 516fce37db
16 changed files with 229 additions and 8 deletions
@@ -32129,6 +32129,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/unsignedTypes/equalsImplForInlineClassWrappingNullableInlineClass.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedArrayType.kt")
public void testEvaluateConstructorOfUnsignedArrayType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedArrayType.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedType.kt")
public void testEvaluateConstructorOfUnsignedType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedType.kt");
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.resolve
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -93,12 +94,16 @@ class CollectionLiteralResolver(
}
private fun getArrayFunctionCallName(expectedType: KotlinType): Name {
if (NO_EXPECTED_TYPE === expectedType || !KotlinBuiltIns.isPrimitiveArray(expectedType)) {
if (NO_EXPECTED_TYPE === expectedType ||
!(KotlinBuiltIns.isPrimitiveArray(expectedType) || KotlinBuiltIns.isUnsignedArrayType(expectedType))
) {
return ArrayFqNames.ARRAY_OF_FUNCTION
}
val descriptor = expectedType.constructor.declarationDescriptor ?: return ArrayFqNames.ARRAY_OF_FUNCTION
return ArrayFqNames.PRIMITIVE_TYPE_TO_ARRAY[KotlinBuiltIns.getPrimitiveArrayType(descriptor)] ?: ArrayFqNames.ARRAY_OF_FUNCTION
return ArrayFqNames.PRIMITIVE_TYPE_TO_ARRAY[KotlinBuiltIns.getPrimitiveArrayType(descriptor)]
?: UnsignedTypes.unsignedArrayTypeToArrayCall[UnsignedTypes.toUnsignedArrayType(descriptor)]
?: ArrayFqNames.ARRAY_OF_FUNCTION
}
}
@@ -59,7 +59,11 @@ public class CompileTimeConstantUtils {
"kotlin.shortArrayOf",
"kotlin.byteArrayOf",
"kotlin.booleanArrayOf",
"kotlin.emptyArray"
"kotlin.emptyArray",
"kotlin.ubyteArrayOf",
"kotlin.ushortArrayOf",
"kotlin.uintArrayOf",
"kotlin.ulongArrayOf"
);
public static void checkConstructorParametersType(@NotNull List<KtParameter> parameters, @NotNull BindingTrace trace) {
@@ -91,7 +95,8 @@ public class CompileTimeConstantUtils {
KotlinBuiltIns.isPrimitiveArray(parameterType) ||
KotlinBuiltIns.isPrimitiveType(parameterType) ||
KotlinBuiltIns.isString(parameterType) ||
UnsignedTypes.INSTANCE.isUnsignedType(parameterType)) {
UnsignedTypes.isUnsignedType(parameterType) ||
UnsignedTypes.isUnsignedArrayType(parameterType)) {
return true;
}
@@ -0,0 +1,68 @@
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_REFLECT
// TARGET_BACKEND: JVM
annotation class AnnoUB(val ub: UByteArray)
annotation class AnnoUS(val us: UShortArray)
annotation class AnnoUI(val ui: UIntArray)
annotation class AnnoUL(val ul: ULongArray)
@Suppress("INVISIBLE_MEMBER")
const val ub0 = UByte(1)
@Suppress("INVISIBLE_MEMBER")
const val us0 = UShort(2)
@Suppress("INVISIBLE_MEMBER")
const val ul0 = ULong(3)
@Suppress("INVISIBLE_MEMBER")
const val ui0 = UInt(-1)
@Suppress("INVISIBLE_MEMBER")
const val ui1 = UInt(0)
@Suppress("INVISIBLE_MEMBER")
const val ui2 = UInt(40 + 2)
@Suppress("INVISIBLE_MEMBER")
object Foo {
@AnnoUB([UByte(1), ub0])
fun f0() {}
@AnnoUS([UShort(2 + 5), us0])
fun f1() {}
@AnnoUI([ui0, ui1, ui2, UInt(100)])
fun f2() {}
@AnnoUL([ul0, ULong(5)])
fun f3() {}
}
fun <T> check(ann: Annotation, f: T.() -> Boolean) {
val result = (ann as T).f()
if (!result) throw RuntimeException("fail for $ann")
}
@Suppress("INVISIBLE_MEMBER")
fun box(): String {
if (ub0.toByte() != 1.toByte()) return "fail"
if (us0.toShort() != 2.toShort()) return "fail"
if (ul0.toLong() != 3L) return "fail"
if ((ui0 + ui1 + ui2).toInt() != 41) return "fail"
check<AnnoUB>(Foo::f0.annotations.first()) {
this.ub[0] == UByte(1) && this.ub[1] == UByte(1)
}
check<AnnoUS>(Foo::f1.annotations.first()) {
this.us[0] == UShort(7) && this.us[1] == UShort(2)
}
check<AnnoUI>(Foo::f2.annotations.first()) {
this.ui[0] == UInt.MAX_VALUE && this.ui[1] == UInt(0) && this.ui[2] == UInt(42) && this.ui[3] == UInt(100)
}
check<AnnoUL>(Foo::f3.annotations.first()) {
this.ul[0] == ULong(3) && this.ul[1] == ULong(5)
}
return "OK"
}
@@ -0,0 +1,19 @@
// !LANGUAGE: +InlineClasses
// WITH_RUNTIME
annotation class Ann(
val u: UInt,
val uba: UByteArray,
val usa: UShortArray,
val uia: UIntArray,
val ula: ULongArray
)
@Ann(
1u,
[1u],
ushortArrayOf(),
[1u, 1u],
ulongArrayOf(1u, 1u)
)
fun foo() {}
@@ -0,0 +1,16 @@
@java.lang.annotation.Retention
@kotlin.Metadata
public annotation class Ann {
// source: 'annotationGetters.kt'
public abstract method u(): int
public abstract method uba(): byte[]
public abstract method uia(): int[]
public abstract method ula(): long[]
public abstract method usa(): short[]
}
@kotlin.Metadata
public final class AnnotationGettersKt {
// source: 'annotationGetters.kt'
public final static @Ann method foo(): void
}
@@ -7,6 +7,6 @@ fun ulong(vararg a: ULong) {}
class ValueParam(vararg val a: ULong)
annotation class Ann(vararg val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>UInt<!>)
annotation class Ann(vararg val a: UInt)
fun array(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> a: UIntArray) {}
@@ -33900,6 +33900,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/unsignedTypes/equalsImplForInlineClassWrappingNullableInlineClass.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedArrayType.kt")
public void testEvaluateConstructorOfUnsignedArrayType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedArrayType.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedType.kt")
public void testEvaluateConstructorOfUnsignedType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedType.kt");
@@ -975,6 +975,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
runTest("compiler/testData/codegen/bytecodeListing/inlineClasses/annotatedPropertyWithInlineClassTypeInSignature.kt");
}
@TestMetadata("annotationGetters.kt")
public void testAnnotationGetters() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/inlineClasses/annotationGetters.kt");
}
@TestMetadata("annotationsOnHiddenConstructor.kt")
public void testAnnotationsOnHiddenConstructor() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/inlineClasses/annotationsOnHiddenConstructor.kt");
@@ -31534,6 +31534,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/unsignedTypes/equalsImplForInlineClassWrappingNullableInlineClass.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedArrayType.kt")
public void testEvaluateConstructorOfUnsignedArrayType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedArrayType.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedType.kt")
public void testEvaluateConstructorOfUnsignedType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedType.kt");
@@ -32129,6 +32129,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/unsignedTypes/equalsImplForInlineClassWrappingNullableInlineClass.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedArrayType.kt")
public void testEvaluateConstructorOfUnsignedArrayType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedArrayType.kt");
}
@TestMetadata("evaluateConstructorOfUnsignedType.kt")
public void testEvaluateConstructorOfUnsignedType() throws Exception {
runTest("compiler/testData/codegen/box/unsignedTypes/evaluateConstructorOfUnsignedType.kt");
@@ -945,6 +945,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
runTest("compiler/testData/codegen/bytecodeListing/inlineClasses/annotatedPropertyWithInlineClassTypeInSignature.kt");
}
@TestMetadata("annotationGetters.kt")
public void testAnnotationGetters() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/inlineClasses/annotationGetters.kt");
}
@TestMetadata("annotationsOnHiddenConstructor.kt")
public void testAnnotationsOnHiddenConstructor() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/inlineClasses/annotationsOnHiddenConstructor.kt");
@@ -153,6 +153,10 @@ object StandardNames {
@JvmField val uShort: ClassId = ClassId.topLevel(uShortFqName)
@JvmField val uInt: ClassId = ClassId.topLevel(uIntFqName)
@JvmField val uLong: ClassId = ClassId.topLevel(uLongFqName)
@JvmField val uByteArrayFqName: FqName = fqName("UByteArray")
@JvmField val uShortArrayFqName: FqName = fqName("UShortArray")
@JvmField val uIntArrayFqName: FqName = fqName("UIntArray")
@JvmField val uLongArrayFqName: FqName = fqName("ULongArray")
@JvmField val primitiveTypeShortNames: Set<Name> = newHashSetWithExpectedSize<Name>(PrimitiveType.values().size).apply {
PrimitiveType.values().mapTo(this) { it.typeName }
@@ -819,6 +819,26 @@ public abstract class KotlinBuiltIns {
return isConstructedFromGivenClassAndNotNullable(type, FqNames.uLongFqName.toUnsafe());
}
public static boolean isUByteArray(@NotNull KotlinType type) {
return isConstructedFromGivenClassAndNotNullable(type, FqNames.uByteArrayFqName.toUnsafe());
}
public static boolean isUShortArray(@NotNull KotlinType type) {
return isConstructedFromGivenClassAndNotNullable(type, FqNames.uShortArrayFqName.toUnsafe());
}
public static boolean isUIntArray(@NotNull KotlinType type) {
return isConstructedFromGivenClassAndNotNullable(type, FqNames.uIntArrayFqName.toUnsafe());
}
public static boolean isULongArray(@NotNull KotlinType type) {
return isConstructedFromGivenClassAndNotNullable(type, FqNames.uLongArrayFqName.toUnsafe());
}
public static boolean isUnsignedArrayType(@NotNull KotlinType type) {
return isUByteArray(type) || isUShortArray(type) || isUIntArray(type) || isULongArray(type);
}
public static boolean isDoubleOrNullableDouble(@NotNull KotlinType type) {
return isConstructedFromGivenClass(type, FqNames._double);
}
@@ -23,10 +23,26 @@ enum class UnsignedType(val classId: ClassId) {
val arrayClassId = ClassId(classId.packageFqName, Name.identifier(typeName.asString() + "Array"))
}
enum class UnsignedArrayType(val classId: ClassId) {
UBYTEARRAY(ClassId.fromString("kotlin/UByteArray")),
USHORTARRAY(ClassId.fromString("kotlin/UShortArray")),
UINTARRAY(ClassId.fromString("kotlin/UIntArray")),
ULONGARRAY(ClassId.fromString("kotlin/ULongArray"));
val typeName = classId.shortClassName
}
object UnsignedTypes {
private val unsignedTypeNames = enumValues<UnsignedType>().map { it.typeName }.toSet()
private val unsignedArrayTypeNames = enumValues<UnsignedArrayType>().map { it.typeName }.toSet()
private val arrayClassIdToUnsignedClassId = hashMapOf<ClassId, ClassId>()
private val unsignedClassIdToArrayClassId = hashMapOf<ClassId, ClassId>()
val unsignedArrayTypeToArrayCall = hashMapOf(
UnsignedArrayType.UBYTEARRAY to Name.identifier("ubyteArrayOf"),
UnsignedArrayType.USHORTARRAY to Name.identifier("ushortArrayOf"),
UnsignedArrayType.UINTARRAY to Name.identifier("uintArrayOf"),
UnsignedArrayType.ULONGARRAY to Name.identifier("ulongArrayOf"),
)
private val arrayClassesShortNames: Set<Name> = UnsignedType.values().mapTo(mutableSetOf()) { it.arrayClassId.shortClassName }
@@ -66,4 +82,40 @@ object UnsignedTypes {
container.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME &&
descriptor.name in UnsignedTypes.unsignedTypeNames
}
@JvmStatic
fun isUnsignedArrayType(type: KotlinType): Boolean {
if (TypeUtils.noExpectedType(type)) return false
val descriptor = type.constructor.declarationDescriptor ?: return false
return isUnsignedArrayClass(descriptor)
}
@JvmStatic
fun toUnsignedArrayType(type: KotlinType): UnsignedArrayType? =
when {
KotlinBuiltIns.isUByteArray(type) -> UnsignedArrayType.UBYTEARRAY
KotlinBuiltIns.isUShortArray(type) -> UnsignedArrayType.USHORTARRAY
KotlinBuiltIns.isUIntArray(type) -> UnsignedArrayType.UINTARRAY
KotlinBuiltIns.isULongArray(type) -> UnsignedArrayType.ULONGARRAY
else -> null
}
@JvmStatic
fun toUnsignedArrayType(descriptor: DeclarationDescriptor): UnsignedArrayType? =
if (!isUnsignedArrayClass(descriptor)) null
else when (descriptor.name.asString()) {
"UByteArray" -> UnsignedArrayType.UBYTEARRAY
"UShortArray" -> UnsignedArrayType.USHORTARRAY
"UIntArray" -> UnsignedArrayType.UINTARRAY
"ULongArray" -> UnsignedArrayType.ULONGARRAY
else -> null
}
fun isUnsignedArrayClass(descriptor: DeclarationDescriptor): Boolean {
val container = descriptor.containingDeclaration
return container is PackageFragmentDescriptor &&
container.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME &&
descriptor.name in unsignedArrayTypeNames
}
}
@@ -56,11 +56,13 @@ class AnnotationValue(value: AnnotationDescriptor) : ConstantValue<AnnotationDes
}
class ArrayValue(
value: List<ConstantValue<*>>,
private val computeType: (ModuleDescriptor) -> KotlinType
value: List<ConstantValue<*>>,
private val computeType: (ModuleDescriptor) -> KotlinType
) : ConstantValue<List<ConstantValue<*>>>(value) {
override fun getType(module: ModuleDescriptor): KotlinType = computeType(module).also { type ->
assert(KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) { "Type should be an array, but was $type: $value" }
assert(KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type) || KotlinBuiltIns.isUnsignedArrayType(type)) {
"Type should be an array, but was $type: $value"
}
}
override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitArrayValue(this, data)