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(),
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,
@@ -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<Int, Int> =
extractOffsets(annotationDescriptor.source)
@@ -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,
@@ -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 = "<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.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)
@@ -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<T : IrDeclaration>(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() },
@@ -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<Int, Int>
@@ -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"
}
+3 -4
View File
@@ -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