Use upper bound checker for typealias expansion

This commit is contained in:
Victor Petukhov
2021-02-03 12:59:49 +03:00
parent edb8007d52
commit d783d99443
20 changed files with 306 additions and 140 deletions
@@ -250,12 +250,11 @@ class TypeAliasExpander(
val unsubstitutedArgument = unsubstitutedType.arguments[i]
val typeParameter = unsubstitutedType.constructor.parameters[i]
if (shouldCheckBounds) {
checkBoundsInTypeAlias(
reportStrategy,
reportStrategy.boundsViolationInSubstitution(
typeSubstitutor,
unsubstitutedArgument.type,
substitutedArgument.type,
typeParameter,
typeSubstitutor
typeParameter
)
}
}
@@ -265,26 +264,6 @@ class TypeAliasExpander(
companion object {
private const val MAX_RECURSION_DEPTH = 100
fun checkBoundsInTypeAlias(
reportStrategy: TypeAliasExpansionReportStrategy,
unsubstitutedArgument: KotlinType,
typeArgument: KotlinType,
typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor
) {
for (bound in typeParameterDescriptor.upperBounds) {
val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT)
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
reportStrategy.boundsViolationInSubstitution(
substitutedBound,
unsubstitutedArgument,
typeArgument,
typeParameterDescriptor
)
}
}
}
private fun assertRecursionDepth(recursionDepth: Int, typeAliasDescriptor: TypeAliasDescriptor) {
if (recursionDepth > MAX_RECURSION_DEPTH) {
throw AssertionError("Too deep recursion while expanding type alias ${typeAliasDescriptor.name}")
@@ -14,7 +14,7 @@ interface TypeAliasExpansionReportStrategy {
fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, substitutedArgument: KotlinType)
fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor)
fun boundsViolationInSubstitution(
bound: KotlinType,
substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
@@ -33,7 +33,7 @@ interface TypeAliasExpansionReportStrategy {
override fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) {}
override fun boundsViolationInSubstitution(
bound: KotlinType,
substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
@@ -88,6 +88,40 @@ fun KotlinType.getEnhancement(): KotlinType? = when (this) {
else -> null
}
private fun List<TypeProjection>.enhanceTypeArguments(depth: Int) =
map { argument ->
// TODO: think about star projections with enhancement (e.g. came from Java: Foo<@NotNull ?>)
if (argument.isStarProjection) {
return@map argument
}
val argumentType = argument.type
val enhancedArgumentType = if (argumentType is TypeWithEnhancement) argumentType.enhancement else argumentType
val enhancedDeeplyArgumentType = enhancedArgumentType.getEnhancementDeeply(depth + 1)
argument.replaceType(enhancedDeeplyArgumentType)
}
private fun KotlinType.getEnhancementDeeply(depth: Int): KotlinType {
val newArguments = arguments.enhanceTypeArguments(depth)
val newArgumentsForUpperBound = if (this is FlexibleType) upperBound.arguments.enhanceTypeArguments(depth) else newArguments
val enhancedType = if (this is TypeWithEnhancement) enhancement else this
return enhancedType.replace(
newArguments = newArguments,
newArgumentsForUpperBound = newArgumentsForUpperBound
)
}
fun KotlinType.getEnhancementDeeply(): KotlinType? {
val enhancedTypeWithArguments = getEnhancementDeeply(depth = 0)
if (enhancedTypeWithArguments === this) return null
return enhancedTypeWithArguments
}
fun KotlinType.unwrapEnhancementDeeply() = getEnhancementDeeply() ?: this
fun KotlinType.unwrapEnhancement(): KotlinType = getEnhancement() ?: this
fun UnwrappedType.inheritEnhancement(origin: KotlinType): UnwrappedType = wrapEnhancement(origin.getEnhancement())