From 8dbfd85d265d51d823291bc3b6996b9da481a679 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 22 May 2018 12:48:06 +0300 Subject: [PATCH] psi2ir: Support class literals in annotations --- .../generators/ConstantValueGenerator.kt | 28 +++- .../transformations/AnnotationGenerator.kt | 11 +- .../annotations/classLiteralInAnnotation.kt | 14 ++ .../annotations/classLiteralInAnnotation.txt | 157 ++++++++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 5 + 5 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.kt create mode 100644 compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGenerator.kt index 451b88570ff..9c683bc5fbc 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGenerator.kt @@ -7,27 +7,32 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.psi2ir.transformations.AnnotationGenerator +import org.jetbrains.kotlin.psi2ir.transformations.ScopedTypeParametersResolver import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class ConstantValueGenerator( private val moduleDescriptor: ModuleDescriptor, private val symbolTable: SymbolTable, - private val annotationGenerator: AnnotationGenerator? + private val annotationGenerator: AnnotationGenerator?, + private val scopedTypeParameterResolver: ScopedTypeParametersResolver? ) { constructor( context: GeneratorContext, annotationGenerator: AnnotationGenerator? = null - ) : this(context.moduleDescriptor, context.symbolTable, annotationGenerator) + ) : this(context.moduleDescriptor, context.symbolTable, annotationGenerator, null) fun generateConstantValueAsExpression( startOffset: Int, @@ -80,7 +85,24 @@ class ConstantValueGenerator( annotationGenerator.generateAnnotationConstructorCall(constantValue.value) } - else -> TODO("handle other literal values: ${constantValue::class.simpleName}") + is KClassValue -> { + val classifierType = constantValue.value + val classifierDescriptor = classifierType.constructor.declarationDescriptor + ?: throw AssertionError("Unexpected KClassValue: $classifierType") + + val typeParameterSymbol = classifierDescriptor.safeAs()?.let { + scopedTypeParameterResolver?.resolveScopedTypeParameter(it) + } + + IrClassReferenceImpl( + startOffset, endOffset, + constantValue.getType(moduleDescriptor), + typeParameterSymbol ?: symbolTable.referenceClassifier(classifierDescriptor), + classifierType + ) + } + + else -> TODO("Unexpected constant value: ${constantValue.javaClass.simpleName} $constantValue") } } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/AnnotationGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/AnnotationGenerator.kt index e6ab5cc06e3..aa67448e6b9 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/AnnotationGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/AnnotationGenerator.kt @@ -39,15 +39,22 @@ class AnnotationGenerator( constructor(context: GeneratorContext) : this(context.moduleDescriptor, context.symbolTable) - private val constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable, this) + private val scopedTypeParameterResolver = ScopedTypeParametersResolver() + private val constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable, this, scopedTypeParameterResolver) override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } override fun visitDeclaration(declaration: IrDeclaration) { - visitElement(declaration) + if (declaration is IrTypeParametersContainer) { + scopedTypeParameterResolver.enterTypeParameterScope(declaration) + } generateAnnotationsForDeclaration(declaration) + visitElement(declaration) + if (declaration is IrTypeParametersContainer) { + scopedTypeParameterResolver.leaveTypeParameterScope() + } } override fun visitValueParameter(declaration: IrValueParameter) { diff --git a/compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.kt b/compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.kt new file mode 100644 index 00000000000..432f962eac9 --- /dev/null +++ b/compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.kt @@ -0,0 +1,14 @@ +import kotlin.reflect.KClass + +annotation class A(val klass: KClass<*>) + +class C + +@A(C::class) fun test1() {} + +inline fun test2() = + @A(T::class) object {} + +inline var T.test3 + get() = @A(T::class) object {} + set(v) { @A(T::class) object {} } \ No newline at end of file diff --git a/compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.txt b/compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.txt new file mode 100644 index 00000000000..f99fc8d029b --- /dev/null +++ b/compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.txt @@ -0,0 +1,157 @@ +FILE fqName: fileName:/classLiteralInAnnotation.kt + CLASS ANNOTATION_CLASS name:A modality:FINAL visibility:public flags: + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:A flags: + superClasses: + CLASS IR_EXTERNAL_DECLARATION_STUB INTERFACE name:Annotation modality:ABSTRACT visibility:public flags: + CONSTRUCTOR visibility:public <> (klass:kotlin.reflect.KClass<*>) returnType:A flags:primary + VALUE_PARAMETER name:klass index:0 type:kotlin.reflect.KClass<*> flags: + PROPERTY name:klass type:kotlin.reflect.KClass<*> visibility:public modality:FINAL flags:val + FIELD PROPERTY_BACKING_FIELD name:klass type:kotlin.reflect.KClass<*> visibility:public flags:final + EXPRESSION_BODY + GET_VAR 'value-parameter klass: KClass<*>' type=kotlin.reflect.KClass<*> origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:A) returnType:KClass<*> flags: + $this: VALUE_PARAMETER name: type:A flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): KClass<*>' + GET_FIELD 'klass: KClass<*>' type=kotlin.reflect.KClass<*> origin=null + receiver: GET_VAR 'this@A: A' type=A origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + overridden: + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + overridden: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + overridden: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + CLASS CLASS name:C modality:FINAL visibility:public flags: + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:C flags: + superClasses: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + CONSTRUCTOR visibility:public <> () returnType:C flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='C' + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN name:test1 visibility:public modality:FINAL <> () returnType:Unit flags: + annotations: + CALL 'constructor A(KClass<*>)' type=A origin=null + klass: CLASS_REFERENCE 'C' type=kotlin.reflect.KClass + BLOCK_BODY + FUN name:test2 visibility:public modality:FINAL () returnType:Any flags:inline + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Any?] + superClassifiers: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='test2(): Any' + BLOCK type=test2. origin=OBJECT_LITERAL + CLASS CLASS name: modality:FINAL visibility:local flags: + annotations: + CALL 'constructor A(KClass<*>)' type=A origin=null + klass: CLASS_REFERENCE '' type=kotlin.reflect.KClass + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:test2. flags: + superClasses: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + CONSTRUCTOR visibility:public <> () returnType:test2. flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='' + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + CALL 'constructor ()' type=test2. origin=OBJECT_LITERAL + PROPERTY name:test3 type:kotlin.Any visibility:public modality:FINAL flags:var + FUN name: visibility:public modality:FINAL ($receiver:T) returnType:Any flags:inline + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Any?] + superClassifiers: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + $receiver: VALUE_PARAMETER name: type:T flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='() on T: Any' + BLOCK type=. origin=OBJECT_LITERAL + CLASS CLASS name: modality:FINAL visibility:local flags: + annotations: + CALL 'constructor A(KClass<*>)' type=A origin=null + klass: CLASS_REFERENCE '' type=kotlin.reflect.KClass + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:. flags: + superClasses: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + CONSTRUCTOR visibility:public <> () returnType:. flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='' + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + CALL 'constructor ()' type=. origin=OBJECT_LITERAL + FUN name: visibility:public modality:FINAL ($receiver:T, v:kotlin.Any) returnType:Unit flags:inline + TYPE_PARAMETER name:T index:0 variance: upperBounds:[kotlin.Any?] + superClassifiers: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + $receiver: VALUE_PARAMETER name: type:T flags: + VALUE_PARAMETER name:v index:0 type:kotlin.Any flags: + BLOCK_BODY + TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Unit modality:FINAL visibility:public flags: + BLOCK type=. origin=OBJECT_LITERAL + CLASS CLASS name: modality:FINAL visibility:local flags: + annotations: + CALL 'constructor A(KClass<*>)' type=A origin=null + klass: CLASS_REFERENCE '' type=kotlin.reflect.KClass + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:. flags: + superClasses: + CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: + CONSTRUCTOR visibility:public <> () returnType:. flags:primary + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='' + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:Boolean flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags: + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:Int flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + overridden: + FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:String flags: + $this: VALUE_PARAMETER name: type:kotlin.Any flags: + CALL 'constructor ()' type=. origin=OBJECT_LITERAL diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 068c7c71de7..54ebc6a4365 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -326,6 +326,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { runTest("compiler/testData/ir/irText/declarations/annotations/arrayInAnnotationArguments.kt"); } + @TestMetadata("classLiteralInAnnotation.kt") + public void testClassLiteralInAnnotation() throws Exception { + runTest("compiler/testData/ir/irText/declarations/annotations/classLiteralInAnnotation.kt"); + } + @TestMetadata("classesWithAnnotations.kt") public void testClassesWithAnnotations() throws Exception { runTest("compiler/testData/ir/irText/declarations/annotations/classesWithAnnotations.kt");