K2: handle recursive types properly in approximation
This commit is intended to deal with inconsistency in K1/K2 star projection handling. K1 star projection includes a 'type' property. This type from a star projection can be used for relevant functions / properties return types, and already includes some approximation for recursive generics. In contrast, K2 star projection is an object, and return types of relevant functions / properties are represented as captured types. To prevent recursion in them in recursive generic case, this commit includes additional replacement of their type arguments. See more details in added comments. #KT-65057 Fixed
This commit is contained in:
committed by
Space Team
parent
c4d6554493
commit
94bcf6d87f
+28
-2
@@ -318,8 +318,32 @@ abstract class AbstractTypeApproximator(
|
||||
}
|
||||
}
|
||||
val baseSubType = type.lowerType() ?: nothingType()
|
||||
// This replacement is important for resolving the code like below in K2.
|
||||
// fun bar(y: FieldOrRef<*>) = y.field
|
||||
// interface FieldOrRef<FF : AbstractField<FF>> { val field: FF }
|
||||
// abstract class AbstractField<out F : AbstractField<F>>
|
||||
// During resolving the value parameter y type, K1 also builds a type for a star projection *.
|
||||
// See fun TypeParameterDescriptor.starProjectionType(): KotlinType and fun buildStarProjectionTypeByTypeParameters.
|
||||
// Thanks to it, K1 builds the star projection type as AbstractField<*> and no other approximation is needed.
|
||||
//
|
||||
// In turn, K2 never makes such a thing (K2 star projection has no associated type).
|
||||
// Instead, it resolves y.field as CapturedType(*) C (see usage one line below v ),
|
||||
// and the constructor of this captured type has a star projection and a supertype of AbstractField(C)
|
||||
// Without this replacement, the type approximator currently cannot handle such a situation properly
|
||||
// and builds AbstractField<AbstractField<AbstractField<Any?>>>.
|
||||
// The check it == type here is intended to find a recursion inside a captured type.
|
||||
// A similar replacement for baseSubType looks unnecessary, no hits in the tests.
|
||||
val replacedSuperType = if (isK2 && toSuper && baseSuperType.getArguments().any { it == type }) {
|
||||
baseSuperType.replaceArguments {
|
||||
// It's possible to use the stub here, because K2 star projection is an object and
|
||||
// in fact this parameter is never used
|
||||
if (it != type) it else createStarProjection(TypeParameterMarkerStubForK2StarProjection)
|
||||
}
|
||||
} else baseSuperType
|
||||
|
||||
val approximatedSuperType by lazy(LazyThreadSafetyMode.NONE) { approximateToSuperType(baseSuperType, conf, depth) }
|
||||
val approximatedSuperType by lazy(LazyThreadSafetyMode.NONE) {
|
||||
approximateToSuperType(replacedSuperType, conf, depth)
|
||||
}
|
||||
val approximatedSubType by lazy(LazyThreadSafetyMode.NONE) { approximateToSubType(baseSubType, conf, depth) }
|
||||
|
||||
if (!conf.capturedType(ctx, type)) {
|
||||
@@ -336,7 +360,7 @@ abstract class AbstractTypeApproximator(
|
||||
return null
|
||||
}
|
||||
}
|
||||
val baseResult = if (toSuper) approximatedSuperType ?: baseSuperType else approximatedSubType ?: baseSubType
|
||||
val baseResult = if (toSuper) approximatedSuperType ?: replacedSuperType else approximatedSubType ?: baseSubType
|
||||
|
||||
// C = in Int, Int <: C => Int? <: C?
|
||||
// C = out Number, C <: Number => C? <: Number?
|
||||
@@ -359,6 +383,8 @@ abstract class AbstractTypeApproximator(
|
||||
}
|
||||
}
|
||||
|
||||
private object TypeParameterMarkerStubForK2StarProjection : TypeParameterMarker
|
||||
|
||||
private fun approximateSimpleToSuperType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) =
|
||||
approximateTo(type, conf, toSuper = true, depth = depth)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user