Fix wrong IrErrorCallExpression gen for annotation in convertToIrConstructorCall In the middle of `KaFirCompilerFacility.compile()`, when the IR gen part accesses the `annotations` of `Fir2IrLazyClass`, it tries to generate the IR annotation calls for annotations of `Fir2IrLazyClass`. In particular, it uses `CallAndReferenceGenerator.convertToIrConstructorCall()`. When we have - file `A` in a module `moduleA` - its dependency file `B` in a module `moduleB` - the class `B` (in file `B`) has an annotation `Foo` in a binary module `lib` - and 'A' is the main target of the CodeGen, the `session` of `CallAndReferenceGenerator` is the session for `moduleA`. Since `moduleA` has only dependency `moduleB`, finding a class symbol `Foo` using the `session` does not work. We have to use the session for `moduleB`, because `moduleB` has dependency on `lib`. Actually, it is nature that class `B` has an annotation `Foo` (not `A`), so we have to find `Foo` from `B` (with the session for `moduleB`). However, the current implementation does not correctly use the session. It generates IrErrorCallExpression if finding the symbol based on the session does not work, which can cause serious issues. For example, if a Compose annotation was correct, and it was dropped because of the IrErrorCallExpression, the generated code will not correctly work for Compose UI. This commits updates `convertToIrConstructorCall()` to correctly use the module session based on the call site of the annotation call instead of generated IrErrorCallExpression.
diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt index ee333bc..93d6b7cb 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt
@@ -480,14 +480,18 @@ constructorCache[constructor] = irConstructorSymbol } - fun getIrConstructorSymbol(firConstructorSymbol: FirConstructorSymbol, potentiallyExternal: Boolean = true): IrConstructorSymbol { + fun getIrConstructorSymbol( + firConstructorSymbol: FirConstructorSymbol, + irClassAsParent: IrClass? = null, + potentiallyExternal: Boolean = true, + ): IrConstructorSymbol { val constructor = firConstructorSymbol.fir getCachedIrConstructorSymbol(constructor)?.let { return it } // caching of created constructor is not called here, because `callablesGenerator` calls `cacheIrConstructor` by itself val symbol = IrConstructorSymbolImpl() if (potentiallyExternal) { - val irParent = findIrParent(constructor, fakeOverrideOwnerLookupTag = null) + val irParent = findIrParent(constructor, fakeOverrideOwnerLookupTag = null) ?: irClassAsParent if (irParent.isExternalParent()) { callablesGenerator.createIrConstructor( constructor,
diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 0638cf8..1b33921 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.FirSimpleSyntheticPropertySymbol import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol import org.jetbrains.kotlin.fir.resolve.calls.getExpectedType +import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.approximateDeclarationType import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors @@ -47,6 +48,7 @@ import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.types.AbstractTypeChecker @@ -904,7 +906,6 @@ ) } } - val annotationIsAccessible = coneType.toRegularClassSymbol(session) != null val symbol = type.classifierOrNull val firConstructorSymbol = (annotation.toResolvedCallableSymbol(session) as? FirConstructorSymbol) ?: run { @@ -920,11 +921,7 @@ } val irConstructorCall = annotation.convertWithOffsets { startOffset, endOffset -> when { - // In compiler facility (debugger) scenario it's possible that annotation call is resolved in the session - // where this annotation was applied, but invisible in the current session. - // In that case we shouldn't generate `IrConstructorCall`, as it will point to non-existing constructor - // of stub IR for not found class - symbol !is IrClassSymbol || !annotationIsAccessible -> IrErrorCallExpressionImpl( + symbol !is IrClassSymbol -> IrErrorCallExpressionImpl( startOffset, endOffset, type, "Unresolved reference: ${annotation.render()}" ) @@ -941,7 +938,12 @@ val fullyExpandedConstructorSymbol = firConstructorSymbol.let { it.fir.originalConstructorIfTypeAlias?.unwrapUseSiteSubstitutionOverrides()?.symbol ?: it } - val irConstructor = declarationStorage.getIrConstructorSymbol(fullyExpandedConstructorSymbol) + val irClassFromAnnotationCallSite = firConstructorSymbol.containingClassLookupTag()?.classId?.let { classId -> + getIrClassFromModuleContainingAnnotationCall(classId, annotation) + } + val irConstructor = declarationStorage.getIrConstructorSymbol( + fullyExpandedConstructorSymbol, irClassAsParent = irClassFromAnnotationCallSite + ) IrConstructorCallImplWithShape( startOffset, endOffset, type, irConstructor, @@ -969,6 +971,20 @@ } } + /** + * Returns [IrClass] whose [ClassId] is [classId] by searching the [FirClassLikeSymbol] from the module containing the + * [FirAnnotationCall] [annotation] (the module containing KT file containing the [FirAnnotationCall] [annotation]). + * If it cannot search such [FirClassLikeSymbol], it returns `null`. + */ + private fun getIrClassFromModuleContainingAnnotationCall(classId: ClassId, annotation: FirAnnotation): IrClass? { + if (annotation !is FirAnnotationCall) return null + + val containingModule = annotation.containingDeclarationSymbol.moduleData + val firClassSymbol = containingModule.session.symbolProvider.getClassLikeSymbolByClassId(classId) as? FirClassSymbol ?: return null + @OptIn(UnsafeDuringIrConstructionAPI::class) + return classifierStorage.getIrClassSymbol(firClassSymbol).owner + } + private fun FirAnnotation.toAnnotationCall(): FirAnnotationCall? { if (this is FirAnnotationCall) return this return buildAnnotationCall {