Refactor: extract TypeAliasExpander as a separate class.

This commit is contained in:
Dmitry Petrov
2016-06-07 11:32:02 +03:00
parent cb08d976d3
commit cf8cbad11c
4 changed files with 244 additions and 187 deletions
@@ -0,0 +1,175 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
class TypeAliasExpander(
private val reportStrategy: TypeAliasExpansionReportStrategy
) {
fun expand(typeAliasExpansion: TypeAliasExpansion, annotations: Annotations) =
expandRecursively(typeAliasExpansion, annotations, 0, true)
fun expandWithoutAbbreviation(typeAliasExpansion: TypeAliasExpansion, annotations: Annotations) =
expandRecursively(typeAliasExpansion, annotations, 0, false)
private fun expandRecursively(
typeAliasExpansion: TypeAliasExpansion,
annotations: Annotations,
recursionDepth: Int,
withAbbreviatedType: Boolean
): KotlinType {
val originalProjection = TypeProjectionImpl(Variance.INVARIANT, typeAliasExpansion.descriptor.underlyingType)
val expandedProjection = expandTypeProjection(originalProjection, typeAliasExpansion, null, recursionDepth)
val expandedType = expandedProjection.type
if (expandedType.isError) return expandedType
assert(expandedProjection.projectionKind == Variance.INVARIANT) {
"Type alias expansion: result for ${typeAliasExpansion.descriptor} is ${expandedProjection.projectionKind}, should be invariant"
}
return if (withAbbreviatedType) {
val abbreviatedType = KotlinTypeImpl.create(annotations,
typeAliasExpansion.descriptor.typeConstructor,
originalProjection.type.isMarkedNullable,
typeAliasExpansion.arguments,
MemberScope.Empty)
expandedType.withAbbreviatedType(abbreviatedType)
}
else {
expandedType
}
}
private fun expandTypeProjection(
originalProjection: TypeProjection,
typeAliasExpansion: TypeAliasExpansion,
typeParameterDescriptor: TypeParameterDescriptor?,
recursionDepth: Int
): TypeProjection {
assertRecursionDepth(recursionDepth, typeAliasExpansion.descriptor)
val originalType = originalProjection.type
val typeAliasArgument = typeAliasExpansion.getReplacement(originalType.constructor)
if (typeAliasArgument == null) {
return expandNonArgumentTypeProjection(originalProjection, typeAliasExpansion, recursionDepth)
}
val originalVariance =
if (originalProjection.projectionKind != Variance.INVARIANT)
originalProjection.projectionKind
else if (typeParameterDescriptor != null)
typeParameterDescriptor.variance
else
Variance.INVARIANT
val argumentVariance = typeAliasArgument.projectionKind
val substitutedVariance =
if (argumentVariance == Variance.INVARIANT)
originalVariance
else if (originalVariance == Variance.INVARIANT || originalVariance == argumentVariance)
argumentVariance
else if (typeAliasArgument.isStarProjection)
argumentVariance
else {
if (originalVariance != argumentVariance && !typeAliasArgument.isStarProjection) {
reportStrategy.conflictingProjection(typeAliasExpansion.descriptor, typeParameterDescriptor, originalType)
}
argumentVariance
}
val substitutedType = TypeUtils.makeNullableIfNeeded(typeAliasArgument.type, originalType.isMarkedNullable)
return TypeProjectionImpl(substitutedVariance, substitutedType)
}
private fun expandNonArgumentTypeProjection(
originalProjection: TypeProjection,
typeAliasExpansion: TypeAliasExpansion,
recursionDepth: Int
): TypeProjection {
val type = originalProjection.type
val typeConstructor = type.constructor
val typeDescriptor = typeConstructor.declarationDescriptor
when (typeDescriptor) {
is TypeParameterDescriptor -> {
return originalProjection
}
is TypeAliasDescriptor -> {
if (typeAliasExpansion.isRecursion(typeDescriptor)) {
reportStrategy.recursiveTypeAlias(typeDescriptor)
return TypeProjectionImpl(Variance.INVARIANT, ErrorUtils.createErrorType("Recursive type alias: ${typeDescriptor.name}"))
}
val expandedArguments = type.arguments.mapIndexed { i, typeAliasArgument ->
expandTypeProjection(typeAliasArgument, typeAliasExpansion, typeConstructor.parameters[i], recursionDepth + 1)
}
val nestedExpansion = TypeAliasExpansion.create(typeAliasExpansion, typeDescriptor, expandedArguments)
val expandedType = expandRecursively(nestedExpansion, type.annotations, recursionDepth + 1, false)
return TypeProjectionImpl(originalProjection.projectionKind, expandedType.withAbbreviatedType(type))
}
else -> {
val substitutedArguments = type.arguments.mapIndexed { i, originalArgument ->
expandTypeProjection(originalArgument, typeAliasExpansion, typeConstructor.parameters[i], recursionDepth + 1)
}
val substitutedType = type.replace(newArguments = substitutedArguments)
checkTypeArgumentsSubstitution(type, substitutedType)
return TypeProjectionImpl(originalProjection.projectionKind, substitutedType)
}
}
}
private fun checkTypeArgumentsSubstitution(unsubstitutedType: KotlinType, substitutedType: KotlinType) {
val typeSubstitutor = TypeSubstitutor.create(substitutedType)
substitutedType.arguments.forEachIndexed { i, substitutedArgument ->
if (!substitutedArgument.type.dependsOnTypeAliasParameters()) {
val unsubstitutedArgument = unsubstitutedType.arguments[i]
val typeParameter = unsubstitutedType.constructor.parameters[i]
DescriptorResolver.checkBoundsInTypeAlias(reportStrategy, unsubstitutedArgument.type, substitutedArgument.type, typeParameter, typeSubstitutor)
}
}
}
companion object {
private const val MAX_RECURSION_DEPTH = 100
private fun assertRecursionDepth(recursionDepth: Int, typeAliasDescriptor: TypeAliasDescriptor) {
if (recursionDepth > MAX_RECURSION_DEPTH) {
throw AssertionError("Too deep recursion while expanding type alias ${typeAliasDescriptor.name}")
}
}
val NON_REPORTING = TypeAliasExpander(TypeAliasExpansionReportStrategy.DO_NOTHING)
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.types.TypeUtils
class TypeAliasExpansion private constructor(
val parent: TypeAliasExpansion?,
val descriptor: TypeAliasDescriptor,
val arguments: List<TypeProjection>,
val mapping: Map<TypeParameterDescriptor, TypeProjection>
) {
fun getReplacement(constructor: TypeConstructor): TypeProjection? {
val descriptor = constructor.declarationDescriptor
return if (descriptor is TypeParameterDescriptor)
mapping[descriptor]
else
null
}
fun isRecursion(descriptor: TypeAliasDescriptor): Boolean =
this.descriptor == descriptor || (parent?.isRecursion(descriptor) ?: false)
companion object {
fun create(
parent: TypeAliasExpansion?,
typeAliasDescriptor: TypeAliasDescriptor,
arguments: List<TypeProjection>
): TypeAliasExpansion {
val typeParameters = typeAliasDescriptor.typeConstructor.parameters.map { it.original as TypeParameterDescriptor }
val mappedArguments = typeParameters.zip(arguments).toMap()
return TypeAliasExpansion(parent, typeAliasDescriptor, arguments, mappedArguments)
}
fun createWithFormalArguments(typeAliasDescriptor: TypeAliasDescriptor) =
create(null, typeAliasDescriptor, emptyList())
}
}
fun KotlinType.dependsOnTypeAliasParameters(): Boolean =
TypeUtils.contains(this) { type ->
val constructorDeclaration = type.constructor.declarationDescriptor
constructorDeclaration is TypeParameterDescriptor && constructorDeclaration.containingDeclaration is TypeAliasDescriptor
}
@@ -26,10 +26,10 @@ interface TypeAliasExpansionReportStrategy {
fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor)
fun boundsViolationInSubstitution(bound: KotlinType, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor)
object DEFAULT : TypeAliasExpansionReportStrategy {
object DO_NOTHING : TypeAliasExpansionReportStrategy {
override fun wrongNumberOfTypeArguments(typeAlias: TypeAliasDescriptor, numberOfParameters: Int) {}
override fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, expandingType: KotlinType) {}
override fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) {}
override fun boundsViolationInSubstitution(bound: KotlinType, unsubstitutedArgument: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor) {}
}
}
}
@@ -75,9 +75,8 @@ class TypeResolver(
}
fun resolveExpandedTypeForTypeAlias(typeAliasDescriptor: TypeAliasDescriptor): KotlinType {
val typeAliasExpansion = createTypeAliasExpansion(null, typeAliasDescriptor, emptyList())
val expandedType = expandTypeAlias(typeAliasExpansion, TypeAliasExpansionReportStrategy.DEFAULT, Annotations.EMPTY, 0,
withAbbreviatedType = false)
val typeAliasExpansion = TypeAliasExpansion.createWithFormalArguments(typeAliasDescriptor)
val expandedType = TypeAliasExpander.NON_REPORTING.expandWithoutAbbreviation(typeAliasExpansion, Annotations.EMPTY)
return expandedType
}
@@ -424,12 +423,6 @@ class TypeResolver(
return type(resultingType)
}
private fun KotlinType.dependsOnTypeAliasParameters(): Boolean =
TypeUtils.contains(this) { type ->
val constructorDeclaration = type.constructor.declarationDescriptor
constructorDeclaration is TypeParameterDescriptor && constructorDeclaration.containingDeclaration is TypeAliasDescriptor
}
private fun resolveTypeForTypeAlias(
c: TypeResolutionContext,
annotations: Annotations,
@@ -479,8 +472,8 @@ class TypeResolver(
type(abbreviatedType)
}
else {
val typeAliasExpansion = createTypeAliasExpansion(null, descriptor, arguments)
val expandedType = expandTypeAlias(typeAliasExpansion, reportStrategy, annotations, 0)
val typeAliasExpansion = TypeAliasExpansion.create(null, descriptor, arguments)
val expandedType = TypeAliasExpander(reportStrategy).expand(typeAliasExpansion, annotations)
type(expandedType)
}
}
@@ -493,7 +486,6 @@ class TypeResolver(
typeParameters: List<TypeParameterDescriptor>,
typeArguments: List<KtTypeProjection>
) : TypeAliasExpansionReportStrategy {
constructor (trace: BindingTrace, typeAliasDescriptor: TypeAliasDescriptor) : this(trace, null, null, typeAliasDescriptor, emptyList(), emptyList())
private val mappedArguments = typeParameters.zip(typeArguments).toMap()
@@ -532,172 +524,7 @@ class TypeResolver(
}
}
private class TypeAliasExpansion(
val parent: TypeAliasExpansion?,
val descriptor: TypeAliasDescriptor,
val arguments: List<TypeProjection>,
val mapping: Map<TypeParameterDescriptor, TypeProjection>
) {
fun getReplacement(constructor: TypeConstructor): TypeProjection? {
val descriptor = constructor.declarationDescriptor
return if (descriptor is TypeParameterDescriptor)
mapping[descriptor]
else
null
}
fun isRecursion(descriptor: TypeAliasDescriptor): Boolean =
this.descriptor == descriptor || (parent?.isRecursion(descriptor) ?: false)
}
private fun createTypeAliasExpansion(
parent: TypeAliasExpansion?,
typeAliasDescriptor: TypeAliasDescriptor,
arguments: List<TypeProjection>
): TypeAliasExpansion {
val typeParameters = typeAliasDescriptor.typeConstructor.parameters.map { it.original as TypeParameterDescriptor }
val mappedArguments = typeParameters.zip(arguments).toMap()
return TypeAliasExpansion(parent, typeAliasDescriptor, arguments, mappedArguments)
}
private fun expandTypeAlias(
typeAliasExpansion: TypeAliasExpansion,
reportStrategy: TypeAliasExpansionReportStrategy,
annotations: Annotations,
recursionDepth: Int,
withAbbreviatedType: Boolean = true
): KotlinType {
val originalProjection = TypeProjectionImpl(Variance.INVARIANT, typeAliasExpansion.descriptor.underlyingType)
val expandedProjection = expandTypeProjectionForTypeAlias(originalProjection, typeAliasExpansion, null, reportStrategy, recursionDepth)
val expandedType = expandedProjection.type
if (expandedType.isError) return expandedType
assert(expandedProjection.projectionKind == Variance.INVARIANT) {
"Type alias expansion: result for ${typeAliasExpansion.descriptor} is ${expandedProjection.projectionKind}, should be invariant"
}
return if (withAbbreviatedType) {
val abbreviatedType = KotlinTypeImpl.create(annotations,
typeAliasExpansion.descriptor.typeConstructor,
originalProjection.type.isMarkedNullable,
typeAliasExpansion.arguments,
MemberScope.Empty)
expandedType.withAbbreviatedType(abbreviatedType)
}
else {
expandedType
}
}
private fun expandTypeProjectionForTypeAlias(
originalProjection: TypeProjection,
typeAliasExpansion: TypeAliasExpansion,
typeParameterDescriptor: TypeParameterDescriptor?,
reportStrategy: TypeAliasExpansionReportStrategy,
recursionDepth: Int
): TypeProjection {
assertRecursionDepth(recursionDepth) {
"Too deep recursion while expanding type alias ${typeAliasExpansion.descriptor}"
}
val originalType = originalProjection.type
val typeAliasArgument = typeAliasExpansion.getReplacement(originalType.constructor)
if (typeAliasArgument == null) {
return substituteNonArgumentTypeForTypeAlias(originalProjection, typeAliasExpansion, reportStrategy, recursionDepth)
}
val originalVariance =
if (originalProjection.projectionKind != Variance.INVARIANT)
originalProjection.projectionKind
else if (typeParameterDescriptor != null)
typeParameterDescriptor.variance
else
Variance.INVARIANT
val argumentVariance = typeAliasArgument.projectionKind
val substitutedVariance =
if (argumentVariance == Variance.INVARIANT)
originalVariance
else if (originalVariance == Variance.INVARIANT || originalVariance == argumentVariance)
argumentVariance
else if (typeAliasArgument.isStarProjection)
argumentVariance
else {
if (originalVariance != argumentVariance && !typeAliasArgument.isStarProjection) {
reportStrategy.conflictingProjection(typeAliasExpansion.descriptor, typeParameterDescriptor, originalType)
}
argumentVariance
}
val substitutedType = TypeUtils.makeNullableIfNeeded(typeAliasArgument.type, originalType.isMarkedNullable)
return TypeProjectionImpl(substitutedVariance, substitutedType)
}
private fun substituteNonArgumentTypeForTypeAlias(
originalProjection: TypeProjection,
typeAliasExpansion: TypeAliasExpansion,
reportStrategy: TypeAliasExpansionReportStrategy,
recursionDepth: Int
): TypeProjection {
val type = originalProjection.type
val typeConstructor = type.constructor
val typeDescriptor = typeConstructor.declarationDescriptor
when (typeDescriptor) {
is TypeParameterDescriptor -> {
return originalProjection
}
is TypeAliasDescriptor -> {
if (typeAliasExpansion.isRecursion(typeDescriptor)) {
reportStrategy.recursiveTypeAlias(typeDescriptor)
return TypeProjectionImpl(Variance.INVARIANT, ErrorUtils.createErrorType("Recursive type alias: ${typeDescriptor.name}"))
}
val expandedArguments = type.arguments.mapIndexed { i, typeAliasArgument ->
expandTypeProjectionForTypeAlias(typeAliasArgument, typeAliasExpansion, typeConstructor.parameters[i], reportStrategy, recursionDepth + 1)
}
val nestedExpansion = createTypeAliasExpansion(typeAliasExpansion, typeDescriptor, expandedArguments)
val expandedType = expandTypeAlias(nestedExpansion, reportStrategy, type.annotations, recursionDepth + 1)
return TypeProjectionImpl(originalProjection.projectionKind, expandedType.withAbbreviatedType(type))
}
else -> {
val substitutedArguments = type.arguments.mapIndexed { i, originalArgument ->
expandTypeProjectionForTypeAlias(originalArgument, typeAliasExpansion, typeConstructor.parameters[i], reportStrategy, recursionDepth + 1)
}
val substitutedType = type.replace(newArguments = substitutedArguments)
checkTypeArgumentsSubstitutionInTypeAliasExpansion(type, substitutedType, reportStrategy)
return TypeProjectionImpl(originalProjection.projectionKind, substitutedType)
}
}
}
private fun checkTypeArgumentsSubstitutionInTypeAliasExpansion(
unsubstitutedType: KotlinType,
substitutedType: KotlinType,
reportStrategy: TypeAliasExpansionReportStrategy
) {
val typeSubstitutor = TypeSubstitutor.create(substitutedType)
substitutedType.arguments.forEachIndexed { i, substitutedArgument ->
if (!substitutedArgument.type.dependsOnTypeAliasParameters()) {
val unsubstitutedArgument = unsubstitutedType.arguments[i]
val typeParameter = unsubstitutedType.constructor.parameters[i]
DescriptorResolver.checkBoundsInTypeAlias(reportStrategy, unsubstitutedArgument.type, substitutedArgument.type, typeParameter, typeSubstitutor)
}
}
}
private fun createErrorTypeForTypeConstructor(
c: TypeResolutionContext,
@@ -901,14 +728,6 @@ class TypeResolver(
}
companion object {
private const val MAX_RECURSION_DEPTH = 100
private inline fun assertRecursionDepth(recursionDepth: Int, lazyMessage: () -> String) {
if (recursionDepth > MAX_RECURSION_DEPTH) {
throw AssertionError(lazyMessage())
}
}
@JvmStatic fun resolveProjectionKind(projectionKind: KtProjectionKind): Variance {
return when (projectionKind) {
KtProjectionKind.IN -> IN_VARIANCE