From 4c4f99c3568083b4c138ce539e1f0560df936bcc Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Tue, 30 Jun 2015 09:01:42 +0300 Subject: [PATCH] Use approximation of captured types in incorporation to generate new constraints --- .../inference/constraintIncorporation.kt | 81 ++++++++----------- .../org/jetbrains/kotlin/types/TypeUtils.kt | 13 +-- 2 files changed, 35 insertions(+), 59 deletions(-) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt index 2a77ccbbb61..e39eab674cd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt @@ -16,8 +16,6 @@ package org.jetbrains.kotlin.resolve.calls.inference -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound @@ -26,13 +24,10 @@ import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_B import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition -import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind -import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.Variance.INVARIANT -import org.jetbrains.kotlin.types.Variance.IN_VARIANCE import org.jetbrains.kotlin.types.typeUtil.getNestedTypeArguments -import java.util.ArrayList +import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes fun ConstraintSystemImpl.incorporateBound(newBound: Bound) { val typeVariable = newBound.typeVariable @@ -74,57 +69,49 @@ private fun ConstraintSystemImpl.addConstraintFromBounds(old: Bound, new: Bound) } } -private fun ConstraintSystemImpl.generateNewBound( - bound: Bound, - substitution: Bound -) { - // Let's have a variable T, a bound 'T <=> My', and a substitution 'R <=> Type'. +private fun ConstraintSystemImpl.generateNewBound(bound: Bound, substitution: Bound) { + // Let's have a bound 'T <=> My', and a substitution 'R <=> Type'. // Here <=> means lower_bound, upper_bound or exact_bound constraint. - // Then a new bound 'T <=> My' can be generated. - - // A variance of R in 'My' (with respect to both use-site and declaration-site variance). - val substitutionVariance: Variance = bound.constrainingType.getNestedTypeArguments().firstOrNull { - getMyTypeVariable(it.getType()) === substitution.typeVariable - }?.getProjectionKind() ?: return + // Then a new bound 'T <=> My<_/in/out Type>' can be generated. // We don't substitute anything into recursive constraints if (substitution.typeVariable == bound.typeVariable) return - //todo variance checker - val newKind = computeKindOfNewBound(bound.kind, substitutionVariance, substitution.kind) ?: return + val substitutedType = when (substitution.kind) { + EXACT_BOUND -> substitution.constrainingType + UPPER_BOUND -> CapturedType(TypeProjectionImpl(Variance.OUT_VARIANCE, substitution.constrainingType)) + LOWER_BOUND -> CapturedType(TypeProjectionImpl(Variance.IN_VARIANCE, substitution.constrainingType)) + } - val newTypeProjection = TypeProjectionImpl(substitutionVariance, substitution.constrainingType) + val newTypeProjection = TypeProjectionImpl(substitutedType) val substitutor = TypeSubstitutor.create(mapOf(substitution.typeVariable.getTypeConstructor() to newTypeProjection)) - val newConstrainingType = substitutor.substitute(bound.constrainingType, INVARIANT)!! - - // We don't generate new recursive constraints - val nestedTypeVariables = newConstrainingType.getNestedTypeVariables() - if (nestedTypeVariables.contains(bound.typeVariable) || nestedTypeVariables.contains(substitution.typeVariable)) return + val type = substitutor.substitute(bound.constrainingType, INVARIANT) ?: return val position = CompoundConstraintPosition(bound.position, substitution.position) - addBound(bound.typeVariable, newConstrainingType, newKind, position) -} -private fun computeKindOfNewBound(constrainingKind: BoundKind, substitutionVariance: Variance, substitutionKind: BoundKind): BoundKind? { - // In examples below: List, MutableList, Comparator, the variance of My may be any. + fun addNewBound(newConstrainingType: JetType, newBoundKind: BoundKind) { + // We don't generate new recursive constraints + val nestedTypeVariables = newConstrainingType.getNestedTypeVariables() + if (nestedTypeVariables.contains(bound.typeVariable) || nestedTypeVariables.contains(substitution.typeVariable)) return - // T <=> My, R <=> Type -> T <=> My + addBound(bound.typeVariable, newConstrainingType, newBoundKind, position) + } - // T < My, R = Int -> T < My - if (substitutionKind == EXACT_BOUND) return constrainingKind - - // T < MutableList, R < Number - nothing can be inferred (R might become 'Int' later) - // todo T < MutableList, R < Int => T < MutableList - if (substitutionVariance == INVARIANT) return null - - val kind = if (substitutionVariance == IN_VARIANCE) substitutionKind.reverse() else substitutionKind - - // T = List, R < Int -> T < List; T = Consumer, R < Int -> T > Consumer - if (constrainingKind == EXACT_BOUND) return kind - - // T < List, R < Int -> T < List; T < Consumer, R > Int -> T < Consumer - if (constrainingKind == kind) return kind - - // otherwise we can generate no new constraints - return null + if (substitution.kind == EXACT_BOUND) { + addNewBound(type, bound.kind) + return + } + val approximationBounds = approximateCapturedTypes(type) + // todo + // if we allow non-trivial type projections, we bump into errors like + // "Empty intersection for types [MutableCollection, MutableCollection, MutableCollection]" + fun JetType.containsConstrainingTypeWithoutProjection() = this.getNestedTypeArguments().any { + it.getType().getConstructor() == substitution.constrainingType.getConstructor() && it.getProjectionKind() == Variance.INVARIANT + } + if (approximationBounds.upper.containsConstrainingTypeWithoutProjection() && bound.kind != LOWER_BOUND) { + addNewBound(approximationBounds.upper, UPPER_BOUND) + } + if (approximationBounds.lower.containsConstrainingTypeWithoutProjection() && bound.kind != UPPER_BOUND) { + addNewBound(approximationBounds.lower, LOWER_BOUND) + } } \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 43f0a4984a8..056ad37ee88 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -99,18 +99,7 @@ fun JetType.getNestedTypeArguments(): List { result.add(typeProjection) - val type = typeProjection.getType() - - type.getConstructor().getParameters().zip(type.getArguments()).forEach { - val (parameter, argument) = it - val newTypeProjection = if (argument.getProjectionKind() == Variance.INVARIANT && parameter.getVariance() != Variance.INVARIANT) { - TypeProjectionImpl(parameter.getVariance(), argument.getType()) - } - else { - argument - } - stack.add(newTypeProjection) - } + typeProjection.getType().getArguments().forEach { stack.add(it) } } return result } \ No newline at end of file