Check expected type of annotation parameter correctly in AnnotationDeserializer

The main idea of this refactoring is to separate two usages of
`AnnotationDeserializer.resolveValue`: the one where we load annotation
argument values, and the one where we load constant values of properties
for JS/Native/Common
(`AnnotationAndConstantLoaderImpl.loadPropertyConstant`).

In the latter case, `expectedType` is the type of the property and it
can be a supertype of the actual value (e.g. see `arrayConst` in
compiler/testData/serialization/builtinsSerializer/compileTimeConstants.kt).
But in the former case, we need to check that the value conforms to the
expected type and disregard it if it's not the case, which is possible
if the annotation was recompiled separately.

 #KT-28927
This commit is contained in:
Alexander Udalov
2019-05-29 18:38:56 +02:00
parent c666b60ca3
commit 43cb17cdd5
6 changed files with 84 additions and 95 deletions
@@ -142,29 +142,7 @@ abstract class AbstractAnnotationDeserializer(
)
}
// ARRAY -> {
// val expectedIsArray = KotlinBuiltIns.isArray(expectedType) || KotlinBuiltIns.isPrimitiveArray(expectedType)
// val arrayElements = value.arrayElementList
//
// val actualArrayType =
// if (arrayElements.isNotEmpty()) {
// val actualElementType = resolveArrayElementType(arrayElements.first(), nameResolver)
// builtIns.getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(actualElementType)
// ?: builtIns.getArrayType(Variance.INVARIANT, actualElementType)
// } else {
// // In the case of empty array, no element has the element type, so we fall back to the expected type, if any.
// // This is not very accurate when annotation class has been changed without recompiling clients,
// // but should not in fact matter because the value is empty anyway
// if (expectedIsArray) expectedType else builtIns.getArrayType(Variance.INVARIANT, builtIns.anyType)
// }
//
// val expectedElementType = builtIns.getArrayElementType(if (expectedIsArray) expectedType else actualArrayType)
//
// ConstantValueFactory.createArrayValue(
// arrayElements.map {
// resolveValue(expectedElementType, it, nameResolver)
// },
// actualArrayType
// )
// TODO: see AnnotationDeserializer
// }
// else -> error("Unsupported annotation argument type: ${value.type} (expected $expectedType)")
else -> return null
@@ -173,4 +151,4 @@ abstract class AbstractAnnotationDeserializer(
}
private fun <T> const(kind: IrConstKind<T>, value: T) = FirConstExpressionImpl(session, null, kind, value)
}
}
@@ -0,0 +1,24 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.KClass
import kotlin.test.assertEquals
@Target(AnnotationTarget.TYPE)
annotation class Anno(
val k1: KClass<out CharSequence>,
val k2: KClass<in String>,
val ka: Array<KClass<out Number>>
)
fun f(): @Anno(String::class, CharSequence::class, [Double::class, Long::class, Int::class]) Unit {}
fun box(): String {
assertEquals(
"[@Anno(k1=class java.lang.String, k2=interface java.lang.CharSequence, " +
"ka=[class java.lang.Double, class java.lang.Long, class java.lang.Integer])]",
::f.returnType.annotations.toString()
)
return "OK"
}
@@ -20008,6 +20008,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/annotations/onTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("classLiteralWithExpectedType.kt")
public void testClassLiteralWithExpectedType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/classLiteralWithExpectedType.kt");
}
@TestMetadata("differentArgumentTypes.kt")
public void testDifferentArgumentTypes() throws Exception {
runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt");
@@ -20008,6 +20008,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/annotations/onTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("classLiteralWithExpectedType.kt")
public void testClassLiteralWithExpectedType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/classLiteralWithExpectedType.kt");
}
@TestMetadata("differentArgumentTypes.kt")
public void testDifferentArgumentTypes() throws Exception {
runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt");
@@ -20013,6 +20013,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/annotations/onTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
}
@TestMetadata("classLiteralWithExpectedType.kt")
public void testClassLiteralWithExpectedType() throws Exception {
runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/classLiteralWithExpectedType.kt");
}
@TestMetadata("differentArgumentTypes.kt")
public void testDifferentArgumentTypes() throws Exception {
runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt");