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 a13caa8019d..62cf9244dc2 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 @@ -177,14 +177,14 @@ abstract class FirJavaFacade( // as we have a nested ordering here. val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() } - enhancement.performFirstRoundOfBoundsResolution(firJavaClass.typeParameters) + val initialBounds = 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) - enhancement.enhanceTypeParameterBoundsAfterFirstRound(firJavaClass.typeParameters) + enhancement.enhanceTypeParameterBoundsAfterFirstRound(firJavaClass.typeParameters, initialBounds) val enhancedSuperTypes = buildList { val purelyImplementedSupertype = firJavaClass.getPurelyImplementedSupertype() diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt index 0a066326a3c..eaecfed5515 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/JavaTypeConversion.kt @@ -39,7 +39,8 @@ private fun ClassId.toConeFlexibleType( } internal enum class FirJavaTypeConversionMode { - DEFAULT, ANNOTATION_MEMBER, SUPERTYPE, TYPE_PARAMETER_BOUND + DEFAULT, ANNOTATION_MEMBER, SUPERTYPE, + TYPE_PARAMETER_BOUND_FIRST_ROUND, TYPE_PARAMETER_BOUND_AFTER_FIRST_ROUND } internal fun FirTypeRef.resolveIfJavaType( @@ -192,7 +193,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( val mappedTypeArguments = when { isRaw -> { val typeParameterSymbols = - lookupTag.takeIf { lowerBound == null && mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND } + lookupTag.takeIf { lowerBound == null && mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND } ?.toFirRegularClassSymbol(session)?.typeParameterSymbols // Given `C`, `C` -> `C..C<*>?`. when (mode) { @@ -204,7 +205,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound( lookupTag != lowerBound?.lookupTag && typeArguments.isNotEmpty() -> { val typeParameterSymbols = - lookupTag.takeIf { mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND } + lookupTag.takeIf { mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND } ?.toFirRegularClassSymbol(session)?.typeParameterSymbols Array(typeArguments.size) { index -> // TODO: check this 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 2dcbd30d541..9fb6ebd3157 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 @@ -330,18 +330,75 @@ class FirSignatureEnhancement( return function.symbol } - // Perform first time initialization of bounds with FirResolvedTypeRef instances - fun performFirstRoundOfBoundsResolution(typeParameters: List) { + /** + * Perform first time initialization of bounds with FirResolvedTypeRef instances + * But after that bounds are still not enhanced and more over might have not totally correct raw types bounds + * (see the next step in the method performSecondRoundOfBoundsResolution) + * + * In case of A, or similar cases, the bound is converted to the flexible version A<*>..A<*>?, + * while in the end it's assumed to be A>..A<*>? + * + * That's necessary because at this stage it's not quite easy to come just to the final version since for that + * we would the need upper bounds of all the type parameters that might not yet be initialized at the moment + * + * See the usages of FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND + */ + fun performFirstRoundOfBoundsResolution(typeParameters: List): List> { + val initialBounds: MutableList> = mutableListOf() + for (typeParameter in typeParameters) { if (typeParameter is FirTypeParameter) { + initialBounds.add(typeParameter.bounds.toList()) typeParameter.replaceBounds(typeParameter.bounds.map { - it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND) + it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND) }) } } + + return initialBounds + } + + /** + * In most cases that method doesn't change anything + * + * But the cases like A + * After the first step we've got all bounds are initialized to potentially approximated version of raw types + * And here, we compute the final version using previously initialized bounds + * + * So, mostly it works just as the first step, but assumes that bounds already contain FirResolvedTypeRef + */ + private fun performSecondRoundOfBoundsResolution( + typeParameters: List, + initialBounds: List> + ) { + var currentIndex = 0 + for (typeParameter in typeParameters) { + if (typeParameter is FirTypeParameter) { + typeParameter.replaceBounds(initialBounds[currentIndex].map { + it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_AFTER_FIRST_ROUND) + }) + + currentIndex++ + } + } } - fun enhanceTypeParameterBoundsAfterFirstRound(typeParameters: List) { + /** + * There are four rounds of bounds resolution for Java type parameters + * 1. Plain conversion of Java types without any enhancement (with approximated raw types) + * 2. The same conversion, but raw types are not computed precisely + * 3. Enhancement for top-level types (no enhancement for arguments) + * 4. Enhancement for the whole types (with arguments) + * + * This method requires type parameters that have already been run through the first round + */ + fun enhanceTypeParameterBoundsAfterFirstRound( + typeParameters: List, + // The state of bounds before the first round + initialBounds: List>, + ) { + performSecondRoundOfBoundsResolution(typeParameters, initialBounds) + // 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 @@ -355,8 +412,8 @@ class FirSignatureEnhancement( } private fun enhanceTypeParameterBoundsForMethod(firMethod: FirFunction) { - performFirstRoundOfBoundsResolution(firMethod.typeParameters) - enhanceTypeParameterBoundsAfterFirstRound(firMethod.typeParameters) + val initialBounds = performFirstRoundOfBoundsResolution(firMethod.typeParameters) + enhanceTypeParameterBoundsAfterFirstRound(firMethod.typeParameters, initialBounds) } private inline fun List.replaceBounds(block: (FirTypeParameter, FirTypeRef) -> FirTypeRef) { @@ -371,7 +428,7 @@ class FirSignatureEnhancement( EnhancementSignatureParts( session, typeQualifierResolver, typeParameter, isCovariant = false, forceOnlyHeadTypeConstructor, AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS, contextQualifiers - ).enhance(bound, emptyList(), FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND) + ).enhance(bound, emptyList(), FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_AFTER_FIRST_ROUND) fun enhanceSuperType(type: FirTypeRef): FirTypeRef = EnhancementSignatureParts( diff --git a/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.fir.kt b/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.fir.kt deleted file mode 100644 index 52061ece8ef..00000000000 --- a/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -// SKIP_TXT - -// FILE: StubElement.java -public interface StubElement {} - -// FILE: IStubFileElementType.java -import org.jetbrains.annotations.*; - -public class IStubFileElementType { - public X getFoo() { return null; } -} - -// FILE: main.kt -fun foo(i: IStubFileElementType<*>) { - bar(i.getFoo()) // In FIR, `i.getFoo()` has a type ft, StubElement<*>?>, while in FE1.0 it's ft, StubElement<*>?> -} - -fun bar(w: StubElement) {} diff --git a/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.kt b/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.kt index 42bfe592b0f..a26edc32725 100644 --- a/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.kt +++ b/compiler/testData/diagnostics/tests/j+k/rawUpperBounds.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT // FILE: StubElement.java @@ -12,7 +13,7 @@ public class IStubFileElementType { // FILE: main.kt fun foo(i: IStubFileElementType<*>) { - bar(i.getFoo()) // In FIR, `i.getFoo()` has a type ft, StubElement<*>?>, while in FE1.0 it's ft, StubElement<*>?> + bar(i.getFoo()) } fun bar(w: StubElement) {} diff --git a/compiler/testData/loadJava/compiledJava/RawUpperBound.fir.txt b/compiler/testData/loadJava/compiledJava/RawUpperBound.fir.txt index d25b67ad76c..f1db75f01f7 100644 --- a/compiler/testData/loadJava/compiledJava/RawUpperBound.fir.txt +++ b/compiler/testData/loadJava/compiledJava/RawUpperBound.fir.txt @@ -1,2 +1,2 @@ -public abstract interface RawUpperBound, test/RawUpperBound<*>?>|> : R|kotlin/Any| { +public abstract interface RawUpperBound, test/RawUpperBound<*>?>>, test/RawUpperBound<*>?>|> : R|kotlin/Any| { } diff --git a/compiler/testData/loadJava/compiledJava/RecursiveRawUpperBound.fir.txt b/compiler/testData/loadJava/compiledJava/RecursiveRawUpperBound.fir.txt index e7518677ff2..d033b7900ec 100644 --- a/compiler/testData/loadJava/compiledJava/RecursiveRawUpperBound.fir.txt +++ b/compiler/testData/loadJava/compiledJava/RecursiveRawUpperBound.fir.txt @@ -1,2 +1,2 @@ -public abstract interface RecursiveRawUpperBound, test/RecursiveRawUpperBound<*>?>|> : R|kotlin/Any| { +public abstract interface RecursiveRawUpperBound, test/RecursiveRawUpperBound<*>?>>, test/RecursiveRawUpperBound<*>?>|> : R|kotlin/Any| { } diff --git a/compiler/testData/loadJava/compiledJava/RecursiveWildcardUpperBound.fir.txt b/compiler/testData/loadJava/compiledJava/RecursiveWildcardUpperBound.fir.txt index 297b9d99b7b..0f3173607e3 100644 --- a/compiler/testData/loadJava/compiledJava/RecursiveWildcardUpperBound.fir.txt +++ b/compiler/testData/loadJava/compiledJava/RecursiveWildcardUpperBound.fir.txt @@ -1,2 +1,2 @@ -public abstract interface RecursiveWildcardUpperBound, test/RecursiveWildcardUpperBound<*>?>|> : R|kotlin/Any| { +public abstract interface RecursiveWildcardUpperBound, test/RecursiveWildcardUpperBound<*>?>>, test/RecursiveWildcardUpperBound<*>?>|> : R|kotlin/Any| { }