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.
|
// as we have a nested ordering here.
|
||||||
|
|
||||||
val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() }
|
val enhancement = FirSignatureEnhancement(firJavaClass, session) { emptyList() }
|
||||||
enhancement.performFirstRoundOfBoundsResolution(firJavaClass.typeParameters)
|
val initialBounds = enhancement.performFirstRoundOfBoundsResolution(firJavaClass.typeParameters)
|
||||||
|
|
||||||
// 1. Resolve annotations
|
// 1. Resolve annotations
|
||||||
// 2. Enhance type parameter bounds - may refer to each other, take default nullability from 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
|
// 3. Enhance super types - may refer to type parameter bounds, take default nullability from annotations
|
||||||
firJavaClass.annotations.addFromJava(session, javaClass, javaTypeParameterStack)
|
firJavaClass.annotations.addFromJava(session, javaClass, javaTypeParameterStack)
|
||||||
|
|
||||||
enhancement.enhanceTypeParameterBoundsAfterFirstRound(firJavaClass.typeParameters)
|
enhancement.enhanceTypeParameterBoundsAfterFirstRound(firJavaClass.typeParameters, initialBounds)
|
||||||
|
|
||||||
val enhancedSuperTypes = buildList {
|
val enhancedSuperTypes = buildList {
|
||||||
val purelyImplementedSupertype = firJavaClass.getPurelyImplementedSupertype()
|
val purelyImplementedSupertype = firJavaClass.getPurelyImplementedSupertype()
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ private fun ClassId.toConeFlexibleType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal enum class FirJavaTypeConversionMode {
|
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(
|
internal fun FirTypeRef.resolveIfJavaType(
|
||||||
@@ -192,7 +193,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
|
|||||||
val mappedTypeArguments = when {
|
val mappedTypeArguments = when {
|
||||||
isRaw -> {
|
isRaw -> {
|
||||||
val typeParameterSymbols =
|
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
|
?.toFirRegularClassSymbol(session)?.typeParameterSymbols
|
||||||
// Given `C<T : X>`, `C` -> `C<X>..C<*>?`.
|
// Given `C<T : X>`, `C` -> `C<X>..C<*>?`.
|
||||||
when (mode) {
|
when (mode) {
|
||||||
@@ -204,7 +205,7 @@ private fun JavaClassifierType.toConeKotlinTypeForFlexibleBound(
|
|||||||
|
|
||||||
lookupTag != lowerBound?.lookupTag && typeArguments.isNotEmpty() -> {
|
lookupTag != lowerBound?.lookupTag && typeArguments.isNotEmpty() -> {
|
||||||
val typeParameterSymbols =
|
val typeParameterSymbols =
|
||||||
lookupTag.takeIf { mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND }
|
lookupTag.takeIf { mode != FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND }
|
||||||
?.toFirRegularClassSymbol(session)?.typeParameterSymbols
|
?.toFirRegularClassSymbol(session)?.typeParameterSymbols
|
||||||
Array(typeArguments.size) { index ->
|
Array(typeArguments.size) { index ->
|
||||||
// TODO: check this
|
// TODO: check this
|
||||||
|
|||||||
+64
-7
@@ -330,18 +330,75 @@ class FirSignatureEnhancement(
|
|||||||
return function.symbol
|
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) {
|
for (typeParameter in typeParameters) {
|
||||||
if (typeParameter is FirTypeParameter) {
|
if (typeParameter is FirTypeParameter) {
|
||||||
|
initialBounds.add(typeParameter.bounds.toList())
|
||||||
typeParameter.replaceBounds(typeParameter.bounds.map {
|
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
|
// 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
|
// (`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
|
// 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) {
|
private fun enhanceTypeParameterBoundsForMethod(firMethod: FirFunction) {
|
||||||
performFirstRoundOfBoundsResolution(firMethod.typeParameters)
|
val initialBounds = performFirstRoundOfBoundsResolution(firMethod.typeParameters)
|
||||||
enhanceTypeParameterBoundsAfterFirstRound(firMethod.typeParameters)
|
enhanceTypeParameterBoundsAfterFirstRound(firMethod.typeParameters, initialBounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun List<FirTypeParameterRef>.replaceBounds(block: (FirTypeParameter, FirTypeRef) -> FirTypeRef) {
|
private inline fun List<FirTypeParameterRef>.replaceBounds(block: (FirTypeParameter, FirTypeRef) -> FirTypeRef) {
|
||||||
@@ -371,7 +428,7 @@ class FirSignatureEnhancement(
|
|||||||
EnhancementSignatureParts(
|
EnhancementSignatureParts(
|
||||||
session, typeQualifierResolver, typeParameter, isCovariant = false, forceOnlyHeadTypeConstructor,
|
session, typeQualifierResolver, typeParameter, isCovariant = false, forceOnlyHeadTypeConstructor,
|
||||||
AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS, contextQualifiers
|
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 =
|
fun enhanceSuperType(type: FirTypeRef): FirTypeRef =
|
||||||
EnhancementSignatureParts(
|
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
|
// SKIP_TXT
|
||||||
|
|
||||||
// FILE: StubElement.java
|
// FILE: StubElement.java
|
||||||
@@ -12,7 +13,7 @@ public class IStubFileElementType<X extends StubElement> {
|
|||||||
|
|
||||||
// FILE: main.kt
|
// FILE: main.kt
|
||||||
fun foo(i: IStubFileElementType<*>) {
|
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>) {}
|
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