KT-11588 Type aliases

Make type alias expansion PSI-independent
This commit is contained in:
Dmitry Petrov
2016-05-12 15:37:38 +03:00
parent 65293008fd
commit 75e08ace17
5 changed files with 121 additions and 90 deletions
@@ -1169,22 +1169,15 @@ public class DescriptorResolver {
}
public static void checkBoundsInTypeAlias(
@NotNull KtUserType typeAliasElement,
@NotNull TypeAliasExpansionReportStrategy reportStrategy,
@NotNull KotlinType typeArgument,
@NotNull TypeParameterDescriptor typeParameterDescriptor,
@NotNull TypeSubstitutor substitutor,
@NotNull BindingTrace trace
@NotNull TypeSubstitutor substitutor
) {
DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration();
assert containingDeclaration instanceof ClassifierDescriptor :
"Containing declaration of a type parameter should be a classifier, got " + containingDeclaration;
ClassifierDescriptor containingClassifier = (ClassifierDescriptor) containingDeclaration;
for (KotlinType bound : typeParameterDescriptor.getUpperBounds()) {
KotlinType substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT);
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(
typeAliasElement, substitutedBound, typeArgument, containingClassifier));
reportStrategy.boundsViolationInSubstitution(substitutedBound, typeArgument, typeParameterDescriptor);
}
}
}
@@ -0,0 +1,35 @@
/*
* 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
interface TypeAliasExpansionReportStrategy {
fun wrongNumberOfTypeArguments(typeAlias: TypeAliasDescriptor, numberOfParameters: Int)
fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, expandingType: KotlinType)
fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor)
fun boundsViolationInSubstitution(bound: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor)
object DEFAULT : 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, argument: KotlinType, typeParameter: TypeParameterDescriptor) {}
}
}
@@ -428,9 +428,13 @@ class TypeResolver(
// TODO appendDefaultArgumentsForInnerScope
val typeAliasArguments = typeAliasQualifierPart.typeArguments?.arguments.orEmpty()
val reportStrategy = TracingTypeAliasExpansionReportStrategy(
c.trace,
type, typeAliasQualifierPart.typeArguments ?: typeAliasQualifierPart.expression,
typeAliasDescriptor, typeAliasDescriptor.declaredTypeParameters, typeAliasArguments)
if (typeAliasParameters.size != typeAliasArguments.size) {
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(typeAliasQualifierPart.typeArguments ?: typeAliasQualifierPart.expression,
typeAliasParameters.size, typeAliasDescriptor))
reportStrategy.wrongNumberOfTypeArguments(typeAliasDescriptor, typeAliasParameters.size)
return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeAliasConstructor)
}
@@ -440,24 +444,56 @@ class TypeResolver(
type(abbreviatedType)
}
else {
val typeAliasExpansion = createTypeAliasExpansionFromSource(c, type, typeAliasDescriptor, typeAliasArguments)
val expandedType = expandTypeAlias(c, typeAliasExpansion, annotations)
val arguments = resolveTypeProjections(c, typeAliasDescriptor.typeConstructor, typeAliasArguments)
val typeAliasExpansion = createTypeAliasExpansion(null, typeAliasDescriptor, arguments)
val expandedType = expandTypeAlias(c, typeAliasExpansion, reportStrategy, annotations, 0)
type(expandedType)
}
}
private class TracingTypeAliasExpansionReportStrategy(
val trace: BindingTrace,
val type: KtUserType,
val typeArgumentsOrTypeName: KtElement,
val typeAliasDescriptor: TypeAliasDescriptor,
val typeParameters: List<TypeParameterDescriptor>,
val typeArguments: List<KtTypeProjection>
) : TypeAliasExpansionReportStrategy {
private val mappedArguments = typeParameters.zip(typeArguments).toMap()
override fun wrongNumberOfTypeArguments(typeAlias: TypeAliasDescriptor, numberOfParameters: Int) {
trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(typeArgumentsOrTypeName, numberOfParameters, typeAliasDescriptor))
}
override fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, expandingType: KotlinType) {
val argumentElement = typeParameter?.let { mappedArguments[it] }
if (argumentElement != null && typeParameter != null) {
trace.report(CONFLICTING_PROJECTION.on(argumentElement, typeParameter))
}
else {
trace.report(CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION.on(type, typeAliasDescriptor.underlyingType))
}
}
override fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) {
trace.report(RECURSIVE_TYPEALIAS_EXPANSION.on(type, typeAlias))
}
override fun boundsViolationInSubstitution(bound: KotlinType, argument: KotlinType, typeParameter: TypeParameterDescriptor) {
TODO()
}
}
private class AbbreviatedTypeImpl(override val abbreviatedType: KotlinType): AbbreviatedType
private fun withAbbreviatedType(abbreviatedType: KotlinType): TypeCapabilities =
SingletonTypeCapabilities(AbbreviatedType::class.java, AbbreviatedTypeImpl(abbreviatedType))
private class TypeAliasExpansionContext(
val element: KtUserType,
val argumentElements: Map<TypeParameterDescriptor, KtTypeProjection>
)
private fun KotlinType.withAbbreviatedType(abbreviatedType: KotlinType): KotlinType =
if (isError)
this
else
replace(newCapabilities = capabilities.overrideCapability(AbbreviatedType::class.java,
AbbreviatedTypeImpl(abbreviatedType)))
private class TypeAliasExpansion(
val context: TypeAliasExpansionContext,
val parent: TypeAliasExpansion?,
val descriptor: TypeAliasDescriptor,
val arguments: List<TypeProjection>,
@@ -475,57 +511,28 @@ class TypeResolver(
this.descriptor == descriptor || (parent?.isRecursion(descriptor) ?: false)
}
private fun createTypeAliasExpansionFromSource(
c: TypeResolutionContext,
element: KtUserType,
descriptor: TypeAliasDescriptor,
argumentElements: List<KtTypeProjection>
): TypeAliasExpansion {
val typeAliasParameters = descriptor.typeConstructor.parameters
val arguments = resolveTypeProjections(c, descriptor.typeConstructor, argumentElements)
val mappedArguments: Map<TypeParameterDescriptor, TypeProjection>
val mappedElements: Map<TypeParameterDescriptor, KtTypeProjection>
if (typeAliasParameters.isNotEmpty()) {
val resultingArguments = HashMap<TypeParameterDescriptor, TypeProjection>()
val resultingElements = HashMap<TypeParameterDescriptor, KtTypeProjection>()
typeAliasParameters.forEachIndexed { i, typeParameterDescriptor ->
resultingArguments[typeParameterDescriptor] = arguments[i]
resultingElements[typeParameterDescriptor] = argumentElements[i]
}
mappedArguments = resultingArguments
mappedElements = resultingElements
}
else {
mappedArguments = emptyMap()
mappedElements = emptyMap()
}
return TypeAliasExpansion(TypeAliasExpansionContext(element, mappedElements), null, descriptor, arguments, mappedArguments)
}
private fun createNestedTypeAliasExpansion(
parent: TypeAliasExpansion,
private fun createTypeAliasExpansion(
parent: TypeAliasExpansion?,
typeAliasDescriptor: TypeAliasDescriptor,
expandedArguments: List<TypeProjection>
arguments: List<TypeProjection>
): TypeAliasExpansion {
val mappedArguments = HashMap<TypeParameterDescriptor, TypeProjection>()
typeAliasDescriptor.typeConstructor.parameters.forEachIndexed { i, typeParameterDescriptor ->
mappedArguments[typeParameterDescriptor] = expandedArguments[i]
}
return TypeAliasExpansion(parent.context, parent, typeAliasDescriptor, expandedArguments, mappedArguments)
val typeParameters = typeAliasDescriptor.declaredTypeParameters // TODO inner type aliases
val mappedArguments = typeParameters.zip(arguments).toMap()
return TypeAliasExpansion(parent, typeAliasDescriptor, arguments, mappedArguments)
}
private fun expandTypeAlias(
c: TypeResolutionContext,
typeAliasExpansion: TypeAliasExpansion,
annotations: Annotations
reportStrategy: TypeAliasExpansionReportStrategy,
annotations: Annotations,
recursionDepth: Int
): KotlinType {
val originalProjection = TypeProjectionImpl(Variance.INVARIANT, typeAliasExpansion.descriptor.underlyingType)
val expandedProjection = expandTypeProjectionForTypeAlias(c, originalProjection, typeAliasExpansion, null, 1)
val expandedProjection = expandTypeProjectionForTypeAlias(c, originalProjection, typeAliasExpansion, null, reportStrategy, recursionDepth)
val expandedType = expandedProjection.type
if (expandedProjection.type.isError) return 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"
@@ -535,9 +542,7 @@ class TypeResolver(
originalProjection.type.isMarkedNullable,
typeAliasExpansion.arguments, MemberScope.Empty)
return expandedProjection.type.replace(
newAnnotations = annotations,
newCapabilities = withAbbreviatedType(abbreviatedType))
return expandedType.withAbbreviatedType(abbreviatedType)
}
private fun expandTypeProjectionForTypeAlias(
@@ -545,6 +550,7 @@ class TypeResolver(
originalProjection: TypeProjection,
typeAliasExpansion: TypeAliasExpansion,
typeParameterDescriptor: TypeParameterDescriptor?,
reportStrategy: TypeAliasExpansionReportStrategy,
recursionDepth: Int
): TypeProjection {
assertRecursionDepth(recursionDepth) {
@@ -556,7 +562,7 @@ class TypeResolver(
val typeAliasArgument = typeAliasExpansion.getReplacement(originalType.constructor)
if (typeAliasArgument == null) {
return substituteNonArgumentTypeForTypeAlias(c, originalProjection, typeAliasExpansion, recursionDepth)
return substituteNonArgumentTypeForTypeAlias(c, originalProjection, typeAliasExpansion, reportStrategy, recursionDepth)
}
val originalVariance =
@@ -578,14 +584,7 @@ class TypeResolver(
argumentVariance
else {
if (originalVariance != argumentVariance && !typeAliasArgument.isStarProjection) {
val argumentElement = typeParameterDescriptor?.let { typeAliasExpansion.context.argumentElements[it] }
if (argumentElement != null && typeParameterDescriptor != null) {
c.trace.report(CONFLICTING_PROJECTION.on(argumentElement, typeParameterDescriptor))
}
else {
c.trace.report(CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION.on(
typeAliasExpansion.context.element, typeAliasExpansion.descriptor.underlyingType))
}
reportStrategy.conflictingProjection(typeAliasExpansion.descriptor, typeParameterDescriptor, originalType)
}
argumentVariance
}
@@ -599,6 +598,7 @@ class TypeResolver(
c: TypeResolutionContext,
originalProjection: TypeProjection,
typeAliasExpansion: TypeAliasExpansion,
reportStrategy: TypeAliasExpansionReportStrategy,
recursionDepth: Int
): TypeProjection {
val type = originalProjection.type
@@ -611,27 +611,26 @@ class TypeResolver(
}
is TypeAliasDescriptor -> {
if (typeAliasExpansion.isRecursion(typeDescriptor)) {
c.trace.report(RECURSIVE_TYPEALIAS_EXPANSION.on(typeAliasExpansion.context.element, typeDescriptor))
reportStrategy.recursiveTypeAlias(typeDescriptor)
return TypeProjectionImpl(Variance.INVARIANT, ErrorUtils.createErrorType("Recursive type alias: ${typeDescriptor.name}"))
}
val expandedArguments = type.arguments.mapIndexed { i, typeAliasArgument ->
expandTypeProjectionForTypeAlias(c, typeAliasArgument, typeAliasExpansion, typeConstructor.parameters[i], recursionDepth)
expandTypeProjectionForTypeAlias(c, typeAliasArgument, typeAliasExpansion, typeConstructor.parameters[i], reportStrategy, recursionDepth + 1)
}
val nestedExpansion = createNestedTypeAliasExpansion(typeAliasExpansion, typeDescriptor, expandedArguments)
val nestedExpansion = createTypeAliasExpansion(typeAliasExpansion, typeDescriptor, expandedArguments)
val expandedType = expandTypeAlias(c, nestedExpansion, type.annotations)
val expandedType = expandTypeAlias(c, nestedExpansion, reportStrategy, type.annotations, recursionDepth + 1)
return TypeProjectionImpl(originalProjection.projectionKind,
if (expandedType.isError) expandedType else expandedType.replace(newCapabilities = withAbbreviatedType(type)))
return TypeProjectionImpl(originalProjection.projectionKind, expandedType.withAbbreviatedType(type))
}
else -> {
val substitutedArguments = type.arguments.mapIndexed { i, originalArgument ->
expandTypeProjectionForTypeAlias(c, originalArgument, typeAliasExpansion, typeConstructor.parameters[i], recursionDepth + 1)
expandTypeProjectionForTypeAlias(c, originalArgument, typeAliasExpansion, typeConstructor.parameters[i], reportStrategy, recursionDepth + 1)
}
checkTypeArgumentsSubstitutionInTypeAliasExpansion(c, type, substitutedArguments, typeAliasExpansion)
checkTypeArgumentsSubstitutionInTypeAliasExpansion(type, substitutedArguments, reportStrategy)
val substitutedType = type.replace(newArguments = substitutedArguments)
@@ -641,16 +640,15 @@ class TypeResolver(
}
private fun checkTypeArgumentsSubstitutionInTypeAliasExpansion(
c: TypeResolutionContext,
type: KotlinType,
substitutedArguments: List<TypeProjection>,
typeAliasExpansion: TypeAliasExpansion
reportStrategy: TypeAliasExpansionReportStrategy
) {
val typeSubstitutor = TypeSubstitutor.create(type)
substitutedArguments.forEachIndexed { i, substitutedArgument ->
val typeParameter = type.constructor.parameters[i]
DescriptorResolver.checkBoundsInTypeAlias(typeAliasExpansion.context.element, substitutedArgument.type, typeParameter, typeSubstitutor, c.trace)
DescriptorResolver.checkBoundsInTypeAlias(reportStrategy, substitutedArgument.type, typeParameter, typeSubstitutor)
}
}
@@ -17,10 +17,7 @@
package org.jetbrains.kotlin.resolve.lazy.descriptors
import com.google.common.collect.Sets
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
@@ -47,6 +47,14 @@ fun <T : TypeCapability> TypeCapabilities.addCapability(clazz: Class<T>, typeCap
return CompositeTypeCapabilities(this, newCapabilities)
}
fun <T : TypeCapability> TypeCapabilities.overrideCapability(clazz: Class<T>, typeCapability: T): TypeCapabilities {
if (getCapability(clazz) === typeCapability) return this
val newCapabilities = SingletonTypeCapabilities(clazz, typeCapability)
if (this === org.jetbrains.kotlin.types.TypeCapabilities.NONE) return newCapabilities
return CompositeTypeCapabilities(newCapabilities, this)
}
inline fun <reified T : TypeCapability> KotlinType.getCapability(): T? = getCapability(T::class.java)