FIR: Properly support raw types in type parameter upper bounds

^KT-49345 Fixed
This commit is contained in:
Denis.Zharkov
2022-10-14 18:18:39 +02:00
committed by Space Team
parent 73cffa315d
commit 7b15b28ee2
8 changed files with 75 additions and 34 deletions
@@ -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()
@@ -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<T : X>`, `C` -> `C<X>..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
@@ -330,18 +330,75 @@ class FirSignatureEnhancement(
return function.symbol
}
// Perform first time initialization of bounds with FirResolvedTypeRef instances
fun performFirstRoundOfBoundsResolution(typeParameters: List<FirTypeParameterRef>) {
/**
* 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<T extends 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<*>>..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<FirTypeParameterRef>): List<List<FirTypeRef>> {
val initialBounds: MutableList<List<FirTypeRef>> = 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<T extends 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<FirTypeParameterRef>,
initialBounds: List<List<FirTypeRef>>
) {
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<FirTypeParameterRef>) {
/**
* 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<FirTypeParameterRef>,
// The state of bounds before the first round
initialBounds: List<List<FirTypeRef>>,
) {
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>, B : D<A>` - 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<FirTypeParameterRef>.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(
@@ -1,18 +0,0 @@
// SKIP_TXT
// FILE: StubElement.java
public interface StubElement<T extends CharSequence> {}
// FILE: IStubFileElementType.java
import org.jetbrains.annotations.*;
public class IStubFileElementType<X extends StubElement> {
public X getFoo() { return null; }
}
// FILE: main.kt
fun foo(i: IStubFileElementType<*>) {
bar(<!ARGUMENT_TYPE_MISMATCH!>i.getFoo()<!>) // In FIR, `i.getFoo()` has a type ft<StubElement<*>, StubElement<*>?>, while in FE1.0 it's ft<StubElement<CharSequence>, StubElement<*>?>
}
fun bar(w: StubElement<CharSequence>) {}
+2 -1
View File
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// SKIP_TXT
// FILE: StubElement.java
@@ -12,7 +13,7 @@ public class IStubFileElementType<X extends StubElement> {
// FILE: main.kt
fun foo(i: IStubFileElementType<*>) {
bar(i.getFoo()) // In FIR, `i.getFoo()` has a type ft<StubElement<*>, StubElement<*>?>, while in FE1.0 it's ft<StubElement<CharSequence>, StubElement<*>?>
bar(i.getFoo())
}
fun bar(w: StubElement<CharSequence>) {}
@@ -1,2 +1,2 @@
public abstract interface RawUpperBound<T : R|ft<test/RawUpperBound<*>, test/RawUpperBound<*>?>|> : R|kotlin/Any| {
public abstract interface RawUpperBound<T : R|ft<Raw type test/RawUpperBound<ft<Raw type test/RawUpperBound<*>, test/RawUpperBound<*>?>>, test/RawUpperBound<*>?>|> : R|kotlin/Any| {
}
@@ -1,2 +1,2 @@
public abstract interface RecursiveRawUpperBound<T : R|ft<test/RecursiveRawUpperBound<*>, test/RecursiveRawUpperBound<*>?>|> : R|kotlin/Any| {
public abstract interface RecursiveRawUpperBound<T : R|ft<Raw type test/RecursiveRawUpperBound<ft<Raw type test/RecursiveRawUpperBound<*>, test/RecursiveRawUpperBound<*>?>>, test/RecursiveRawUpperBound<*>?>|> : R|kotlin/Any| {
}
@@ -1,2 +1,2 @@
public abstract interface RecursiveWildcardUpperBound<T : R|ft<test/RecursiveWildcardUpperBound<*>, test/RecursiveWildcardUpperBound<*>?>|> : R|kotlin/Any| {
public abstract interface RecursiveWildcardUpperBound<T : R|ft<Raw type test/RecursiveWildcardUpperBound<ft<Raw type test/RecursiveWildcardUpperBound<*>, test/RecursiveWildcardUpperBound<*>?>>, test/RecursiveWildcardUpperBound<*>?>|> : R|kotlin/Any| {
}