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.
This commit is contained in:
Alexander Udalov
2023-01-04 23:44:09 +01:00
parent 4a91957ff0
commit 6d0628900e
8 changed files with 72 additions and 18 deletions
@@ -64,7 +64,10 @@ class Psi2IrTranslator(
extensions: GeneratorExtensions = GeneratorExtensions(), extensions: GeneratorExtensions = GeneratorExtensions(),
fragmentContext: FragmentContext? = null fragmentContext: FragmentContext? = null
): GeneratorContext { ): GeneratorContext {
val typeTranslator = TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions) val typeTranslator = TypeTranslatorImpl(
symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions,
allowErrorTypeInAnnotations = configuration.skipBodies,
)
return GeneratorContext( return GeneratorContext(
configuration, configuration,
moduleDescriptor, moduleDescriptor,
@@ -22,7 +22,8 @@ class ConstantValueGeneratorImpl(
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
symbolTable: ReferenceSymbolTable, symbolTable: ReferenceSymbolTable,
typeTranslator: TypeTranslator, typeTranslator: TypeTranslator,
) : ConstantValueGenerator(moduleDescriptor, symbolTable, typeTranslator) { allowErrorTypeInAnnotations: Boolean,
) : ConstantValueGenerator(moduleDescriptor, symbolTable, typeTranslator, allowErrorTypeInAnnotations) {
override fun extractAnnotationOffsets(annotationDescriptor: AnnotationDescriptor): Pair<Int, Int> = override fun extractAnnotationOffsets(annotationDescriptor: AnnotationDescriptor): Pair<Int, Int> =
extractOffsets(annotationDescriptor.source) extractOffsets(annotationDescriptor.source)
@@ -9,15 +9,11 @@ import org.jetbrains.kotlin.backend.common.SamTypeApproximator
import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.IrGeneratorContext 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.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.SymbolTable
import org.jetbrains.kotlin.ir.util.TypeTranslator import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -83,7 +79,10 @@ class GeneratorContext private constructor(
languageVersionSettings, languageVersionSettings,
symbolTable, symbolTable,
extensions, extensions,
TypeTranslatorImpl(symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile), TypeTranslatorImpl(
symbolTable, languageVersionSettings, moduleDescriptor, extensions = extensions, ktFile = ktFile,
allowErrorTypeInAnnotations = configuration.skipBodies,
),
irBuiltIns, irBuiltIns,
callToSubstitutedDescriptorMap, callToSubstitutedDescriptorMap,
fragmentContext, fragmentContext,
@@ -18,7 +18,11 @@ package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.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.DescriptorMetadataSource
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl 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.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile
import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
open class ModuleGenerator( open class ModuleGenerator(
@@ -75,6 +81,13 @@ open class ModuleGenerator(
IrSyntheticDeclarationGenerator(context).generateSyntheticDeclarations(irFile) 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) insertImplicitCasts(irFile, context)
context.callToSubstitutedDescriptorMap.clear() context.callToSubstitutedDescriptorMap.clear()
@@ -85,11 +98,32 @@ open class ModuleGenerator(
return irFile return irFile
} }
fun createEmptyIrFile(ktFile: KtFile, module: IrModuleFragment): IrFileImpl { private fun createEmptyIrFile(ktFile: KtFile, module: IrModuleFragment): IrFileImpl {
val fileEntry = PsiIrFileEntry(ktFile) val fileEntry = PsiIrFileEntry(ktFile)
val packageFragmentDescriptor = context.moduleDescriptor.findPackageFragmentForFile(ktFile)!! val packageFragmentDescriptor = context.moduleDescriptor.findPackageFragmentForFile(ktFile)!!
return IrFileImpl(fileEntry, packageFragmentDescriptor, module).apply { return IrFileImpl(fileEntry, packageFragmentDescriptor, module).apply {
metadata = DescriptorMetadataSource.File(CodegenUtil.getMemberDescriptorsToGenerate(ktFile, context.bindingContext)) metadata = DescriptorMetadataSource.File(CodegenUtil.getMemberDescriptorsToGenerate(ktFile, context.bindingContext))
} }
} }
private fun createStubIrForErrorClass() {
val fakeFileEntry = object : IrFileEntry {
override val name: String = "<error-class>"
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,
)
}
} }
@@ -14,16 +14,18 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
open class TypeTranslatorImpl( class TypeTranslatorImpl(
symbolTable: ReferenceSymbolTable, symbolTable: ReferenceSymbolTable,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
typeParametersResolverBuilder: () -> TypeParametersResolver = { ScopedTypeParametersResolver() }, typeParametersResolverBuilder: () -> TypeParametersResolver = { ScopedTypeParametersResolver() },
enterTableScope: Boolean = false, enterTableScope: Boolean = false,
extensions: StubGeneratorExtensions = StubGeneratorExtensions.EMPTY, extensions: StubGeneratorExtensions = StubGeneratorExtensions.EMPTY,
private val ktFile: KtFile? = null private val ktFile: KtFile? = null,
allowErrorTypeInAnnotations: Boolean = false,
) : TypeTranslator(symbolTable, languageVersionSettings, typeParametersResolverBuilder, enterTableScope, extensions) { ) : 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) private val typeApproximatorForNI = TypeApproximator(moduleDescriptor.builtIns, languageVersionSettings)
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.error.ErrorUtils
/* Descriptors that serve purely as a view into IR structures. /* 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). Created each time at the borderline between IR-based and descriptor-based code (such as inliner).
@@ -44,11 +45,21 @@ abstract class IrBasedDeclarationDescriptor<T : IrDeclaration>(val owner: T) : D
} }
private fun IrConstructorCall.toAnnotationDescriptor(): AnnotationDescriptor { 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()}" "Expected call to constructor of annotation class but was: ${this.dump()}"
} }
return AnnotationDescriptorImpl( return AnnotationDescriptorImpl(
symbol.owner.parentAsClass.defaultType.toIrBasedKotlinType(), annotationClass.defaultType.toIrBasedKotlinType(),
symbol.owner.valueParameters.map { it.name to getValueArgument(it.index) } symbol.owner.valueParameters.map { it.name to getValueArgument(it.index) }
.filter { it.second != null } .filter { it.second != null }
.associate { it.first to it.second!!.toConstantValue() }, .associate { it.first to it.second!!.toConstantValue() },
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeConstructorSubstitution import org.jetbrains.kotlin.types.TypeConstructorSubstitution
import org.jetbrains.kotlin.types.error.ErrorClassDescriptor
import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.types.typeUtil.builtIns
@@ -28,6 +29,7 @@ abstract class ConstantValueGenerator(
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val symbolTable: ReferenceSymbolTable, private val symbolTable: ReferenceSymbolTable,
private val typeTranslator: TypeTranslator, private val typeTranslator: TypeTranslator,
private val allowErrorTypeInAnnotations: Boolean,
) { ) {
protected abstract fun extractAnnotationOffsets(annotationDescriptor: AnnotationDescriptor): Pair<Int, Int> protected abstract fun extractAnnotationOffsets(annotationDescriptor: AnnotationDescriptor): Pair<Int, Int>
@@ -158,7 +160,10 @@ abstract class ConstantValueGenerator(
if (annotationClassDescriptor !is ClassDescriptor) return null if (annotationClassDescriptor !is ClassDescriptor) return null
if (annotationClassDescriptor is NotFoundClasses.MockClassDescriptor) 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" "Annotation class expected: $annotationClassDescriptor"
} }
+3 -4
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// CORRECT_ERROR_TYPES // CORRECT_ERROR_TYPES
@file:Suppress("UNRESOLVED_REFERENCE", "ANNOTATION_ARGUMENT_MUST_BE_CONST") @file:Suppress("UNRESOLVED_REFERENCE", "ANNOTATION_ARGUMENT_MUST_BE_CONST")
@@ -15,6 +14,6 @@ class ErrorSomeMissingAnnotations
annotation class Anno(val klass: KClass<*>) annotation class Anno(val klass: KClass<*>)
// EXPECTED_ERROR: (kotlin:10:1) cannot find symbol // EXPECTED_ERROR: (kotlin:9:1) cannot find symbol
// EXPECTED_ERROR: (kotlin:13:1) cannot find symbol // EXPECTED_ERROR: (kotlin:12:1) cannot find symbol
// EXPECTED_ERROR: (kotlin:7:1) cannot find symbol // EXPECTED_ERROR: (kotlin:6:1) cannot find symbol