FIR: Properly support raw types in type parameter upper bounds
^KT-49345 Fixed
This commit is contained in:
committed by
Space Team
parent
73cffa315d
commit
7b15b28ee2
@@ -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
|
||||
|
||||
+64
-7
@@ -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>) {}
|
||||
@@ -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
-1
@@ -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| {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user