diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt new file mode 100644 index 00000000000..96d8b3f26d7 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpander.kt @@ -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) + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansion.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansion.kt new file mode 100644 index 00000000000..8b68e51c150 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansion.kt @@ -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, + val mapping: Map +) { + 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 + ): 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 + } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansionReportStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansionReportStrategy.kt index 12c83c73715..26aa83cc0a8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansionReportStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeAliasExpansionReportStrategy.kt @@ -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) {} } -} +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 0131253d2d7..39af9feccf8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -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, typeArguments: List ) : 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, - val mapping: Map - ) { - 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 - ): 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