diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt index b293f98ad3c..a13caa8019d 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt @@ -175,19 +175,17 @@ abstract class FirJavaFacade( // TODO: some (all?) of those loops can be avoided, e.g. we don't actually need to resolve class arguments of annotations // to determine whether they set default nullability - but without laziness, breaking those loops is somewhat hard, // as we have a nested ordering here. - for (typeParameter in firJavaClass.typeParameters) { - if (typeParameter is FirTypeParameter) { - typeParameter.replaceBounds(typeParameter.bounds.map { - it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND) - }) - } - } + + val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() } + enhancement.performFirstRoundOfBoundsResolution(firJavaClass.typeParameters) + // 1. Resolve annotations // 2. Enhance type parameter bounds - may refer to each other, take default nullability from annotations // 3. Enhance super types - may refer to type parameter bounds, take default nullability from annotations firJavaClass.annotations.addFromJava(session, javaClass, javaTypeParameterStack) - val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() } - enhancement.enhanceTypeParameterBounds(firJavaClass.typeParameters) + + enhancement.enhanceTypeParameterBoundsAfterFirstRound(firJavaClass.typeParameters) + val enhancedSuperTypes = buildList { val purelyImplementedSupertype = firJavaClass.getPurelyImplementedSupertype() val purelyImplementedSupertypeClassId = purelyImplementedSupertype?.classId diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt index 39c24c3c2d9..2dcbd30d541 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt @@ -68,8 +68,11 @@ class FirSignatureEnhancement( private val typeQualifierResolver = session.javaAnnotationTypeQualifierResolver - private val contextQualifiers: JavaTypeQualifiersByElementType? = + // This property is assumed to be initialized only after annotations for the class are initialized + // While in one of the cases FirSignatureEnhancement is created just one step before annotations resolution + private val contextQualifiers: JavaTypeQualifiersByElementType? by lazy(LazyThreadSafetyMode.NONE) { typeQualifierResolver.extractDefaultQualifiers(owner) + } private val enhancementsCache = session.enhancedSymbolStorage.cacheByOwner.getValue(owner.symbol, null) @@ -187,7 +190,7 @@ class FirSignatureEnhancement( if (firMethod !is FirJavaMethod && firMethod !is FirJavaConstructor) { return original } - enhanceTypeParameterBounds(firMethod.typeParameters) + enhanceTypeParameterBoundsForMethod(firMethod) return enhanceMethod(firMethod, original.callableId, name) } @@ -327,18 +330,22 @@ class FirSignatureEnhancement( return function.symbol } - fun enhanceTypeParameterBounds(typeParameters: List) { + // Perform first time initialization of bounds with FirResolvedTypeRef instances + fun performFirstRoundOfBoundsResolution(typeParameters: List) { + for (typeParameter in typeParameters) { + if (typeParameter is FirTypeParameter) { + typeParameter.replaceBounds(typeParameter.bounds.map { + it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND) + }) + } + } + } + + fun enhanceTypeParameterBoundsAfterFirstRound(typeParameters: List) { // Type parameters can have interdependencies between them. Assuming that there are no top-level cycles // (`A : B, B : A` - invalid), the cycles can still appear when type parameters use each other in argument // position (`A : C, B : D` - valid). In this case the precise enhancement of each bound depends on // the others' nullability, for which we need to enhance at least its head type constructor. - typeParameters.replaceBounds { _, bound -> - // Resolve without enhancement so we don't crash the frontend if there is a restricted cycle (`A : B, B : A`) - // or if we visit type parameters in the wrong order (`A : B, B : C` with `A` enhanced before `B`). - // TODO: the second case technically produces incorrect results - the loop below should visit type parameters - // in topological order, then a resolved-but-not-enhanced type will never be observable with valid code. - bound.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND) - } typeParameters.replaceBounds { typeParameter, bound -> enhanceTypeParameterBound(typeParameter, bound, forceOnlyHeadTypeConstructor = true) } @@ -347,6 +354,11 @@ class FirSignatureEnhancement( } } + private fun enhanceTypeParameterBoundsForMethod(firMethod: FirFunction) { + performFirstRoundOfBoundsResolution(firMethod.typeParameters) + enhanceTypeParameterBoundsAfterFirstRound(firMethod.typeParameters) + } + private inline fun List.replaceBounds(block: (FirTypeParameter, FirTypeRef) -> FirTypeRef) { for (typeParameter in this) { if (typeParameter is FirTypeParameter) {