From 6d0628900e3ec8e39dc23c0fcc8ceb9ec880bea3 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 4 Jan 2023 23:44:09 +0100 Subject: [PATCH] Kapt+JVM_IR: support annotations with error types This is a hack to implement KT-32596 in the JVM IR version of kapt. Basically we allow psi2ir to generate annotations whose classifier is error class, which happens when it's unresolved. Because there's no physical IR for an error class, we create stub IR for it via SyntheticDeclarationsGenerator in case we'll need it. With this hack, annotations with unresolved classifiers magically survive all the way until the codegen (with a minor change in IrBasedDescriptors) where they are generated as `@error.NonExistentClass`, which then gets corrected by the kapt's "correct error types" mode as in all other cases of error types. --- .../kotlin/psi2ir/Psi2IrTranslator.kt | 5 ++- .../generators/ConstantValueGeneratorImpl.kt | 3 +- .../psi2ir/generators/GeneratorContext.kt | 9 +++-- .../psi2ir/generators/ModuleGenerator.kt | 36 ++++++++++++++++++- .../psi2ir/generators/TypeTranslatorImpl.kt | 8 +++-- .../ir/descriptors/IrBasedDescriptors.kt | 15 ++++++-- .../kotlin/ir/util/ConstantValueGenerator.kt | 7 +++- .../testData/converter/kt32596.kt | 7 ++-- 8 files changed, 72 insertions(+), 18 deletions(-) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt index 3f3dbdb5d46..028986d0b9a 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt @@ -64,7 +64,10 @@ class Psi2IrTranslator( extensions: GeneratorExtensions = GeneratorExtensions(), fragmentContext: FragmentContext? = null ): GeneratorContext { - val typeTranslator = TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions) + val typeTranslator = TypeTranslatorImpl( + symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, + allowErrorTypeInAnnotations = configuration.skipBodies, + ) return GeneratorContext( configuration, moduleDescriptor, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGeneratorImpl.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGeneratorImpl.kt index ff81e3fdca9..ecfedc6d69b 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGeneratorImpl.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ConstantValueGeneratorImpl.kt @@ -22,7 +22,8 @@ class ConstantValueGeneratorImpl( moduleDescriptor: ModuleDescriptor, symbolTable: ReferenceSymbolTable, typeTranslator: TypeTranslator, -) : ConstantValueGenerator(moduleDescriptor, symbolTable, typeTranslator) { + allowErrorTypeInAnnotations: Boolean, +) : ConstantValueGenerator(moduleDescriptor, symbolTable, typeTranslator, allowErrorTypeInAnnotations) { override fun extractAnnotationOffsets(annotationDescriptor: AnnotationDescriptor): Pair = extractOffsets(annotationDescriptor.source) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt index 842fcd89e5c..be54f54971d 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt @@ -9,15 +9,11 @@ import org.jetbrains.kotlin.backend.common.SamTypeApproximator import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.NotFoundClasses import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.builders.IrGeneratorContext -import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.psi.KtFile @@ -83,7 +79,10 @@ class GeneratorContext private constructor( languageVersionSettings, symbolTable, extensions, - TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile), + TypeTranslatorImpl( + symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile, + allowErrorTypeInAnnotations = configuration.skipBodies, + ), irBuiltIns, callToSubstitutedDescriptorMap, fragmentContext, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt index 0c5eef87824..9ab45ca2b82 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt @@ -18,7 +18,11 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor +import org.jetbrains.kotlin.ir.IrFileEntry import org.jetbrains.kotlin.ir.PsiIrFileEntry +import org.jetbrains.kotlin.ir.SourceRangeInfo +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.DescriptorMetadataSource import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl @@ -28,10 +32,12 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile +import org.jetbrains.kotlin.types.error.ErrorUtils import org.jetbrains.kotlin.utils.addIfNotNull open class ModuleGenerator( @@ -75,6 +81,13 @@ open class ModuleGenerator( IrSyntheticDeclarationGenerator(context).generateSyntheticDeclarations(irFile) + if (context.configuration.skipBodies) { + // In KAPT3 mode (skipBodies = true), we create stub IR for error class, so that it would be possible to have annotations + // with unresolved types in the IR and they could survive all transformations up until the codegen. + // This is a hack which should preferably be removed as soon as KAPT3 is no longer used. + createStubIrForErrorClass() + } + insertImplicitCasts(irFile, context) context.callToSubstitutedDescriptorMap.clear() @@ -85,11 +98,32 @@ open class ModuleGenerator( return irFile } - fun createEmptyIrFile(ktFile: KtFile, module: IrModuleFragment): IrFileImpl { + private fun createEmptyIrFile(ktFile: KtFile, module: IrModuleFragment): IrFileImpl { val fileEntry = PsiIrFileEntry(ktFile) val packageFragmentDescriptor = context.moduleDescriptor.findPackageFragmentForFile(ktFile)!! return IrFileImpl(fileEntry, packageFragmentDescriptor, module).apply { metadata = DescriptorMetadataSource.File(CodegenUtil.getMemberDescriptorsToGenerate(ktFile, context.bindingContext)) } } + + private fun createStubIrForErrorClass() { + val fakeFileEntry = object : IrFileEntry { + override val name: String = "" + override val maxOffset: Int = UNDEFINED_OFFSET + + override fun getSourceRangeInfo(beginOffset: Int, endOffset: Int): SourceRangeInfo = TODO("Not yet implemented") + override fun getLineNumber(offset: Int): Int = TODO("Not yet implemented") + override fun getColumnNumber(offset: Int): Int = TODO("Not yet implemented") + } + val fakeFile = IrFileImpl( + fakeFileEntry, + EmptyPackageFragmentDescriptor(context.moduleDescriptor, FqName(fakeFileEntry.name)), + ) + val gen = SyntheticDeclarationsGenerator(context) + gen.visitClassDescriptor(ErrorUtils.errorClass, fakeFile) + gen.visitConstructorDescriptor( + ErrorUtils.errorClass.unsubstitutedPrimaryConstructor!!, + context.symbolTable.referenceClass(ErrorUtils.errorClass).owner, + ) + } } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TypeTranslatorImpl.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TypeTranslatorImpl.kt index 4cfd4dd0b42..b57e4aac693 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TypeTranslatorImpl.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TypeTranslatorImpl.kt @@ -14,16 +14,18 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.* -open class TypeTranslatorImpl( +class TypeTranslatorImpl( symbolTable: ReferenceSymbolTable, languageVersionSettings: LanguageVersionSettings, moduleDescriptor: ModuleDescriptor, typeParametersResolverBuilder: () -> TypeParametersResolver = { ScopedTypeParametersResolver() }, enterTableScope: Boolean = false, extensions: StubGeneratorExtensions = StubGeneratorExtensions.EMPTY, - private val ktFile: KtFile? = null + private val ktFile: KtFile? = null, + allowErrorTypeInAnnotations: Boolean = false, ) : TypeTranslator(symbolTable, languageVersionSettings, typeParametersResolverBuilder, enterTableScope, extensions) { - override val constantValueGenerator: ConstantValueGenerator = ConstantValueGeneratorImpl(moduleDescriptor, symbolTable, this) + override val constantValueGenerator: ConstantValueGenerator = + ConstantValueGeneratorImpl(moduleDescriptor, symbolTable, this, allowErrorTypeInAnnotations) private val typeApproximatorForNI = TypeApproximator(moduleDescriptor.builtIns, languageVersionSettings) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt index d8b529bba88..02837768a2a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrBasedDescriptors.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.error.ErrorUtils /* Descriptors that serve purely as a view into IR structures. Created each time at the borderline between IR-based and descriptor-based code (such as inliner). @@ -44,11 +45,21 @@ abstract class IrBasedDeclarationDescriptor(val owner: T) : D } private fun IrConstructorCall.toAnnotationDescriptor(): AnnotationDescriptor { - assert(symbol.owner.parentAsClass.isAnnotationClass) { + val annotationClass = symbol.owner.parentAsClass + + @OptIn(ObsoleteDescriptorBasedAPI::class) + if (annotationClass.symbol.descriptor == ErrorUtils.errorClass) { + // This should be possible only in case of KAPT3 where IR generated by psi2ir can have annotations with unresolved types. + // Apparently annotations with unresolved types is a useful feature for KAPT3 in the "correct error types mode", see kt32596.kt. + // This is a hack which should preferably be removed as soon as KAPT3 is no longer used. + return AnnotationDescriptorImpl(type.toKotlinType(), emptyMap(), source) + } + + assert(annotationClass.isAnnotationClass) { "Expected call to constructor of annotation class but was: ${this.dump()}" } return AnnotationDescriptorImpl( - symbol.owner.parentAsClass.defaultType.toIrBasedKotlinType(), + annotationClass.defaultType.toIrBasedKotlinType(), symbol.owner.valueParameters.map { it.name to getValueArgument(it.index) } .filter { it.second != null } .associate { it.first to it.second!!.toConstantValue() }, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt index a735434b8ba..d13d551a903 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ConstantValueGenerator.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructorSubstitution +import org.jetbrains.kotlin.types.error.ErrorClassDescriptor import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.typeUtil.builtIns @@ -28,6 +29,7 @@ abstract class ConstantValueGenerator( private val moduleDescriptor: ModuleDescriptor, private val symbolTable: ReferenceSymbolTable, private val typeTranslator: TypeTranslator, + private val allowErrorTypeInAnnotations: Boolean, ) { protected abstract fun extractAnnotationOffsets(annotationDescriptor: AnnotationDescriptor): Pair @@ -158,7 +160,10 @@ abstract class ConstantValueGenerator( if (annotationClassDescriptor !is ClassDescriptor) return null if (annotationClassDescriptor is NotFoundClasses.MockClassDescriptor) return null - assert(DescriptorUtils.isAnnotationClass(annotationClassDescriptor)) { + assert( + DescriptorUtils.isAnnotationClass(annotationClassDescriptor) || + (allowErrorTypeInAnnotations && annotationClassDescriptor is ErrorClassDescriptor) + ) { "Annotation class expected: $annotationClassDescriptor" } diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/kt32596.kt b/plugins/kapt3/kapt3-compiler/testData/converter/kt32596.kt index 6b456a12eab..01f77145bc6 100644 --- a/plugins/kapt3/kapt3-compiler/testData/converter/kt32596.kt +++ b/plugins/kapt3/kapt3-compiler/testData/converter/kt32596.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // CORRECT_ERROR_TYPES @file:Suppress("UNRESOLVED_REFERENCE", "ANNOTATION_ARGUMENT_MUST_BE_CONST") @@ -15,6 +14,6 @@ class ErrorSomeMissingAnnotations annotation class Anno(val klass: KClass<*>) -// EXPECTED_ERROR: (kotlin:10:1) cannot find symbol -// EXPECTED_ERROR: (kotlin:13:1) cannot find symbol -// EXPECTED_ERROR: (kotlin:7:1) cannot find symbol +// EXPECTED_ERROR: (kotlin:9:1) cannot find symbol +// EXPECTED_ERROR: (kotlin:12:1) cannot find symbol +// EXPECTED_ERROR: (kotlin:6:1) cannot find symbol