Added captured types and approximation
CapturedType captures type projection while solving the constraint system. During the substitution type containing captured types is approximated to get rid of captured types and (for simple cases) replace them with corresponding type projections. Note that Array<Array< CapturedType(out Int) >> is (over)approximated by Array<out<Array<out Int>> See 'Mixed-site variance' by Ross Tate for details. #KT-2570 Fixed #KT-2872 Fixed #KT-3213 Fixed
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.lang.resolve.calls.inference
|
||||
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.TypeProjection
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.annotations.Annotations
|
||||
import org.jetbrains.jet.lang.types.Variance
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.types.JetTypeImpl
|
||||
import org.jetbrains.jet.lang.types.ErrorUtils
|
||||
|
||||
public class CapturedTypeConstructor(
|
||||
public val typeProjection: TypeProjection
|
||||
): TypeConstructor {
|
||||
{
|
||||
assert(typeProjection.getProjectionKind() != Variance.INVARIANT) {
|
||||
"Only nontrivial projections can be captured, not: $typeProjection"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getParameters(): List<TypeParameterDescriptor> = listOf()
|
||||
|
||||
override fun getSupertypes(): Collection<JetType> {
|
||||
val superType = if (typeProjection.getProjectionKind() == Variance.OUT_VARIANCE)
|
||||
typeProjection.getType()
|
||||
else
|
||||
KotlinBuiltIns.getInstance().getNullableAnyType()
|
||||
return listOf(superType)
|
||||
}
|
||||
|
||||
override fun isFinal() = true
|
||||
|
||||
override fun isDenotable() = false
|
||||
|
||||
override fun getDeclarationDescriptor() = null
|
||||
|
||||
override fun getAnnotations() = Annotations.EMPTY
|
||||
|
||||
override fun toString() = "Captured($typeProjection)"
|
||||
}
|
||||
|
||||
public fun createCapturedType(typeProjection: TypeProjection): JetType {
|
||||
val scope = ErrorUtils.createErrorScope("No member resolution should be done on captured type, " +
|
||||
"it used only during constraint system resolution", true)
|
||||
return JetTypeImpl(Annotations.EMPTY, CapturedTypeConstructor(typeProjection), false, listOf(), scope)
|
||||
}
|
||||
|
||||
public fun JetType.isCaptured(): Boolean = getConstructor() is CapturedTypeConstructor
|
||||
+4
-5
@@ -43,6 +43,8 @@ public trait ConstraintPosition {
|
||||
val kind: ConstraintPositionKind
|
||||
|
||||
fun isStrong(): Boolean = kind != TYPE_BOUND_POSITION
|
||||
|
||||
fun isCaptureAllowed(): Boolean = kind in setOf(VALUE_PARAMETER_POSITION, RECEIVER_POSITION)
|
||||
}
|
||||
|
||||
private open data class ConstraintPositionImpl(override val kind: ConstraintPositionKind) : ConstraintPosition {
|
||||
@@ -53,14 +55,11 @@ private data class ConstraintPositionWithIndex(override val kind: ConstraintPosi
|
||||
}
|
||||
|
||||
class CompoundConstraintPosition(
|
||||
val positions: Collection<ConstraintPosition>
|
||||
vararg positions: ConstraintPosition
|
||||
) : ConstraintPositionImpl(ConstraintPositionKind.COMPOUND_CONSTRAINT_POSITION) {
|
||||
val positions: Collection<ConstraintPosition> = positions.toList()
|
||||
|
||||
override fun isStrong() = positions.any { it.isStrong() }
|
||||
|
||||
override fun toString() = "$kind(${positions.joinToString()}"
|
||||
}
|
||||
|
||||
public fun getCompoundConstraintPosition(vararg positions: ConstraintPosition): ConstraintPosition {
|
||||
return CompoundConstraintPosition(positions.toList())
|
||||
}
|
||||
+29
-5
@@ -41,7 +41,6 @@ import org.jetbrains.jet.lang.resolve.calls.inference.constraintPosition.Constra
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.constraintPosition.ConstraintPositionKind
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.constraintPosition.CompoundConstraintPosition
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.constraintPosition.getCompoundConstraintPosition
|
||||
import org.jetbrains.jet.lang.types.CustomTypeVariable
|
||||
import org.jetbrains.jet.lang.types.getCustomTypeVariable
|
||||
import org.jetbrains.jet.lang.types.isFlexible
|
||||
@@ -226,6 +225,14 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun capture(typeVariable: JetType, typeProjection: TypeProjection): Boolean {
|
||||
if (isMyTypeVariable(typeVariable) && constraintPosition.isCaptureAllowed()) {
|
||||
generateTypeParameterCaptureConstraint(typeVariable, typeProjection, constraintPosition)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun noCorrespondingSupertype(subtype: JetType, supertype: JetType): Boolean {
|
||||
errorConstraintPositions.add(constraintPosition)
|
||||
return true
|
||||
@@ -303,9 +310,16 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
generateTypeParameterConstraint(superType, subType, boundKind, constraintPosition)
|
||||
return
|
||||
}
|
||||
// if superType is nullable and subType is not nullable, unsafe call error will be generated later,
|
||||
// if superType is nullable and subType is not nullable, unsafe call or type mismatch error will be generated later,
|
||||
// but constraint system should be solved anyway
|
||||
typeCheckingProcedure.isSubtypeOf(TypeUtils.makeNotNullable(subType), TypeUtils.makeNotNullable(superType))
|
||||
val subTypeNotNullable = TypeUtils.makeNotNullable(subType)
|
||||
val superTypeNotNullable = TypeUtils.makeNotNullable(superType)
|
||||
if (constraintKind == EQUAL) {
|
||||
typeCheckingProcedure.equalTypes(subTypeNotNullable, superTypeNotNullable)
|
||||
}
|
||||
else {
|
||||
typeCheckingProcedure.isSubtypeOf(subTypeNotNullable, superTypeNotNullable)
|
||||
}
|
||||
}
|
||||
simplifyConstraint(newSubType, superType)
|
||||
}
|
||||
@@ -352,6 +366,16 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateTypeParameterCaptureConstraint(
|
||||
parameterType: JetType,
|
||||
constrainingTypeProjection: TypeProjection,
|
||||
constraintPosition: ConstraintPosition
|
||||
) {
|
||||
val typeBounds = getTypeBounds(parameterType)
|
||||
val capturedType = createCapturedType(constrainingTypeProjection)
|
||||
typeBounds.addBound(EXACT_BOUND, capturedType, constraintPosition)
|
||||
}
|
||||
|
||||
public fun processDeclaredBoundConstraints() {
|
||||
for ((typeParameterDescriptor, typeBounds) in typeParameterBounds) {
|
||||
for (declaredUpperBound in typeParameterDescriptor.getUpperBounds()) {
|
||||
@@ -359,7 +383,7 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
val bounds = ArrayList(typeBounds.bounds)
|
||||
for (bound in bounds) {
|
||||
if (bound.kind == LOWER_BOUND || bound.kind == EXACT_BOUND) {
|
||||
val position = getCompoundConstraintPosition(
|
||||
val position = CompoundConstraintPosition(
|
||||
TYPE_BOUND_POSITION.position(typeParameterDescriptor.getIndex()), bound.position)
|
||||
addSubtypeConstraint(bound.constrainingType, declaredUpperBound, position)
|
||||
}
|
||||
@@ -368,7 +392,7 @@ public class ConstraintSystemImpl : ConstraintSystem {
|
||||
val typeBoundsForUpperBound = getTypeBounds(declaredUpperBound)
|
||||
for (bound in typeBoundsForUpperBound.bounds) {
|
||||
if (bound.kind == UPPER_BOUND || bound.kind == EXACT_BOUND) {
|
||||
val position = getCompoundConstraintPosition(
|
||||
val position = CompoundConstraintPosition(
|
||||
TYPE_BOUND_POSITION.position(typeParameterDescriptor.getIndex()), bound.position)
|
||||
typeBounds.addBound(UPPER_BOUND, bound.constrainingType, position)
|
||||
}
|
||||
|
||||
+2
-1
@@ -156,7 +156,8 @@ public class TypeBoundsImpl(
|
||||
|
||||
private fun tryPossibleAnswer(possibleAnswer: JetType?): Boolean {
|
||||
if (possibleAnswer == null) return false
|
||||
if (!possibleAnswer.getConstructor().isDenotable()) return false
|
||||
// a captured type might be an answer
|
||||
if (!possibleAnswer.getConstructor().isDenotable() && !possibleAnswer.isCaptured()) return false
|
||||
|
||||
for (bound in bounds) {
|
||||
when (bound.kind) {
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.lang.types.typesApproximation
|
||||
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
import org.jetbrains.jet.lang.types.TypeProjection
|
||||
import org.jetbrains.jet.lang.types.TypeProjectionImpl
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.types.Variance
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.lang.types.JetTypeImpl
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.CapturedTypeConstructor
|
||||
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||
import org.jetbrains.jet.lang.types.TypeSubstitution
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.jet.lang.types.LazyType
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.isCaptured
|
||||
|
||||
public data class ApproximationBounds<T>(
|
||||
public val lower: T,
|
||||
public val upper: T
|
||||
)
|
||||
|
||||
private class TypeArgument(
|
||||
val typeParameter: TypeParameterDescriptor,
|
||||
val inProjection: JetType,
|
||||
val outProjection: JetType
|
||||
) {
|
||||
val isConsistent: Boolean
|
||||
get() = JetTypeChecker.DEFAULT.isSubtypeOf(inProjection, outProjection)
|
||||
}
|
||||
|
||||
private val NULLABLE_ANY = KotlinBuiltIns.getInstance().getNullableAnyType()
|
||||
|
||||
private val NOTHING = KotlinBuiltIns.getInstance().getNothingType()
|
||||
|
||||
private fun TypeArgument.toTypeProjection(): TypeProjection {
|
||||
assert(isConsistent) { "Only consistent enhanced type propection can be converted to type projection" }
|
||||
fun removeProjectionIfRedundant(variance: Variance) = if (variance == typeParameter.getVariance()) Variance.INVARIANT else variance
|
||||
return when {
|
||||
inProjection == outProjection -> TypeProjectionImpl(inProjection)
|
||||
inProjection == NOTHING -> TypeProjectionImpl(removeProjectionIfRedundant(Variance.OUT_VARIANCE), outProjection)
|
||||
outProjection == NULLABLE_ANY -> TypeProjectionImpl(removeProjectionIfRedundant(Variance.IN_VARIANCE), inProjection)
|
||||
else -> throw AssertionError("Enhanced type projection can't be converted to type projection: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun TypeProjection.toTypeArgument(typeParameter: TypeParameterDescriptor) =
|
||||
when (TypeSubstitutor.combine(typeParameter.getVariance(), getProjectionKind()) : Variance) {
|
||||
Variance.INVARIANT -> TypeArgument(typeParameter, getType(), getType())
|
||||
Variance.IN_VARIANCE -> TypeArgument(typeParameter, getType(), NULLABLE_ANY)
|
||||
Variance.OUT_VARIANCE -> TypeArgument(typeParameter, NOTHING, getType())
|
||||
}
|
||||
|
||||
public fun approximateCapturedTypesIfNecessary(typeProjection: TypeProjection?): TypeProjection? {
|
||||
if (typeProjection == null) return null
|
||||
|
||||
val type = typeProjection.getType()
|
||||
if (!TypeUtils.containsSpecialType(type, { it.isCaptured() })) {
|
||||
return typeProjection
|
||||
}
|
||||
val howThisTypeIsUsed = typeProjection.getProjectionKind()
|
||||
if (howThisTypeIsUsed == Variance.OUT_VARIANCE) {
|
||||
// only 'return' type containing captured types should be over-approximated
|
||||
val approximation = approximateCapturedTypes(type)
|
||||
return TypeProjectionImpl(howThisTypeIsUsed, approximation.upper)
|
||||
}
|
||||
return substituteCapturedTypes(typeProjection)
|
||||
}
|
||||
|
||||
private fun substituteCapturedTypes(typeProjection: TypeProjection): TypeProjection? {
|
||||
val typeSubstitutor = TypeSubstitutor.create(object : TypeSubstitution {
|
||||
override fun get(typeConstructor: TypeConstructor?): TypeProjection? {
|
||||
return (typeConstructor as? CapturedTypeConstructor)?.typeProjection
|
||||
}
|
||||
override fun isEmpty() = false
|
||||
})
|
||||
return typeSubstitutor.substituteWithoutApproximation(typeProjection)
|
||||
}
|
||||
|
||||
public fun approximateCapturedTypes(type: JetType): ApproximationBounds<JetType> {
|
||||
val typeConstructor = type.getConstructor()
|
||||
if (type.isCaptured()) {
|
||||
val typeProjection = (typeConstructor as CapturedTypeConstructor).typeProjection
|
||||
|
||||
return when (typeProjection.getProjectionKind()) {
|
||||
Variance.IN_VARIANCE -> ApproximationBounds(typeProjection.getType(), NULLABLE_ANY)
|
||||
Variance.OUT_VARIANCE -> ApproximationBounds(NOTHING, typeProjection.getType())
|
||||
else -> throw AssertionError("Only nontrivial projections should have been captured, not: $typeProjection")
|
||||
}
|
||||
}
|
||||
if (type.getArguments().isEmpty()) {
|
||||
return ApproximationBounds(type, type)
|
||||
}
|
||||
val lowerBoundArguments = ArrayList<TypeArgument>()
|
||||
val upperBoundArguments = ArrayList<TypeArgument>()
|
||||
for ((typeProjection, typeParameter) in type.getArguments().zip(typeConstructor.getParameters())) {
|
||||
val (lower, upper) = approximateProjection(typeProjection.toTypeArgument(typeParameter))
|
||||
lowerBoundArguments.add(lower)
|
||||
upperBoundArguments.add(upper)
|
||||
}
|
||||
val lowerBoundIsTrivial = lowerBoundArguments.any { !it.isConsistent }
|
||||
return ApproximationBounds(
|
||||
if (lowerBoundIsTrivial) NOTHING else type.replaceTypeArguments(lowerBoundArguments),
|
||||
type.replaceTypeArguments(upperBoundArguments))
|
||||
}
|
||||
|
||||
private fun JetType.replaceTypeArguments(newTypeArguments: List<TypeArgument>): JetType {
|
||||
assert(getArguments().size() == newTypeArguments.size()) { "Incorrect type arguments $newTypeArguments" }
|
||||
return JetTypeImpl(getAnnotations(), getConstructor(), isMarkedNullable(), newTypeArguments.map { it.toTypeProjection() }, getMemberScope())
|
||||
}
|
||||
|
||||
private fun approximateProjection(typeArgument: TypeArgument): ApproximationBounds<TypeArgument> {
|
||||
val (inLower, inUpper) = approximateCapturedTypes(typeArgument.inProjection)
|
||||
val (outLower, outUpper) = approximateCapturedTypes(typeArgument.outProjection)
|
||||
return ApproximationBounds(
|
||||
lower = TypeArgument(typeArgument.typeParameter, inUpper, outLower),
|
||||
upper = TypeArgument(typeArgument.typeParameter, inLower, outUpper))
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.SubstitutingScope;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
import org.jetbrains.jet.lang.types.typeUtil.TypeUtilPackage;
|
||||
import org.jetbrains.jet.lang.types.typesApproximation.TypesApproximationPackage;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -60,18 +61,22 @@ public class TypeSubstitutor {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TypeSubstitutor create(@NotNull TypeSubstitution substitution) {
|
||||
return new TypeSubstitutor(substitution);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TypeSubstitutor create(@NotNull TypeSubstitution... substitutions) {
|
||||
return create(new CompositeTypeSubstitution(substitutions));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TypeSubstitutor create(@NotNull Map<TypeConstructor, TypeProjection> substitutionContext) {
|
||||
return create(new MapToTypeSubstitutionAdapter(substitutionContext));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static TypeSubstitutor create(@NotNull JetType context) {
|
||||
return create(buildSubstitutionContext(context.getConstructor().getParameters(), context.getArguments()));
|
||||
}
|
||||
@@ -135,6 +140,12 @@ public class TypeSubstitutor {
|
||||
|
||||
@Nullable
|
||||
public TypeProjection substitute(@NotNull TypeProjection typeProjection) {
|
||||
TypeProjection substitutedTypeProjection = substituteWithoutApproximation(typeProjection);
|
||||
return TypesApproximationPackage.approximateCapturedTypesIfNecessary(substitutedTypeProjection);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TypeProjection substituteWithoutApproximation(@NotNull TypeProjection typeProjection) {
|
||||
if (isEmpty()) {
|
||||
return typeProjection;
|
||||
}
|
||||
@@ -159,14 +170,15 @@ public class TypeSubstitutor {
|
||||
unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibility.getLowerBound()), recursionDepth + 1);
|
||||
TypeProjection substitutedUpper =
|
||||
unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibility.getUpperBound()), recursionDepth + 1);
|
||||
// todo: projection kind is neglected
|
||||
return new TypeProjectionImpl(originalProjectionKind,
|
||||
DelegatingFlexibleType.create(
|
||||
substitutedLower.getType(),
|
||||
substitutedUpper.getType(),
|
||||
flexibility.getExtraCapabilities()
|
||||
)
|
||||
);
|
||||
|
||||
Variance substitutedProjectionKind = substitutedLower.getProjectionKind();
|
||||
assert (substitutedProjectionKind == substitutedUpper.getProjectionKind()) &&
|
||||
originalProjectionKind == Variance.INVARIANT || originalProjectionKind == substitutedProjectionKind :
|
||||
"Unexpected substituted projection kind: " + substitutedProjectionKind + "; original: " + originalProjectionKind;
|
||||
|
||||
JetType substitutedFlexibleType = DelegatingFlexibleType.create(
|
||||
substitutedLower.getType(), substitutedUpper.getType(), flexibility.getExtraCapabilities());
|
||||
return new TypeProjectionImpl(substitutedProjectionKind, substitutedFlexibleType);
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isNothing(type) || type.isError()) return originalProjection;
|
||||
@@ -174,15 +186,12 @@ public class TypeSubstitutor {
|
||||
TypeProjection replacement = substitution.get(type.getConstructor());
|
||||
|
||||
if (replacement != null) {
|
||||
// It must be a type parameter: only they can be directly substituted for
|
||||
TypeParameterDescriptor typeParameter = (TypeParameterDescriptor) type.getConstructor().getDeclarationDescriptor();
|
||||
|
||||
switch (conflictType(originalProjectionKind, replacement.getProjectionKind())) {
|
||||
case OUT_IN_IN_POSITION:
|
||||
throw new SubstitutionException("Out-projection in in-position");
|
||||
case IN_IN_OUT_POSITION:
|
||||
//noinspection ConstantConditions
|
||||
return TypeUtils.makeStarProjection(typeParameter);
|
||||
// todo use the right type parameter variance and upper bound
|
||||
return new TypeProjectionImpl(Variance.OUT_VARIANCE, KotlinBuiltIns.getInstance().getNullableAnyType());
|
||||
case NO_CONFLICT:
|
||||
JetType substitutedType;
|
||||
CustomTypeVariable typeVariable = TypesPackage.getCustomTypeVariable(type);
|
||||
@@ -191,7 +200,7 @@ public class TypeSubstitutor {
|
||||
}
|
||||
else {
|
||||
// this is a simple type T or T?: if it's T, we should just take replacement, if T? - we make replacement nullable
|
||||
substitutedType = type.isMarkedNullable() ? TypeUtils.makeNullable(replacement.getType()) : replacement.getType();
|
||||
substitutedType = TypeUtils.makeNullableIfNeeded(replacement.getType(), type.isMarkedNullable());
|
||||
}
|
||||
|
||||
Variance resultingProjectionKind = combine(originalProjectionKind, replacement.getProjectionKind());
|
||||
@@ -201,14 +210,21 @@ public class TypeSubstitutor {
|
||||
}
|
||||
}
|
||||
// The type is not within the substitution range, i.e. Foo, Bar<T> etc.
|
||||
return substituteCompoundType(type, originalProjectionKind, recursionDepth);
|
||||
return substituteCompoundType(originalProjection, recursionDepth);
|
||||
}
|
||||
|
||||
private TypeProjection substituteCompoundType(
|
||||
final JetType type,
|
||||
Variance projectionKind,
|
||||
TypeProjection originalProjection,
|
||||
int recursionDepth
|
||||
) throws SubstitutionException {
|
||||
final JetType type = originalProjection.getType();
|
||||
Variance projectionKind = originalProjection.getProjectionKind();
|
||||
if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
|
||||
// substitution can't change type parameter
|
||||
// todo substitute bounds
|
||||
return originalProjection;
|
||||
}
|
||||
|
||||
List<TypeProjection> substitutedArguments = substituteTypeArguments(
|
||||
type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
|
||||
|
||||
@@ -265,10 +281,12 @@ public class TypeSubstitutor {
|
||||
return substitutedArguments;
|
||||
}
|
||||
|
||||
private static Variance combine(Variance typeParameterVariance, Variance projectionKind) {
|
||||
@NotNull
|
||||
public static Variance combine(@NotNull Variance typeParameterVariance, @NotNull Variance projectionKind) {
|
||||
if (typeParameterVariance == Variance.INVARIANT) return projectionKind;
|
||||
if (projectionKind == Variance.INVARIANT) return typeParameterVariance;
|
||||
if (typeParameterVariance == projectionKind) return projectionKind;
|
||||
//todo ask why?
|
||||
return Variance.IN_VARIANCE;
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.types.checker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
||||
import org.jetbrains.jet.lang.types.TypeProjection;
|
||||
|
||||
class TypeCheckerTypingConstraints implements TypingConstraints {
|
||||
@Override
|
||||
@@ -36,6 +37,11 @@ class TypeCheckerTypingConstraints implements TypingConstraints {
|
||||
return typeCheckingProcedure.isSubtypeOf(subtype, supertype);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean capture(@NotNull JetType type, @NotNull TypeProjection typeProjection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||
return false; // type checking fails
|
||||
|
||||
@@ -106,6 +106,9 @@ public class TypeCheckingProcedure {
|
||||
TypeProjection typeProjection1 = type1Arguments.get(i);
|
||||
TypeParameterDescriptor typeParameter2 = constructor2.getParameters().get(i);
|
||||
TypeProjection typeProjection2 = type2Arguments.get(i);
|
||||
if (capture(typeProjection1, typeProjection2, typeParameter1)) {
|
||||
continue;
|
||||
}
|
||||
if (getEffectiveProjectionKind(typeParameter1, typeProjection1) != getEffectiveProjectionKind(typeParameter2, typeProjection2)) {
|
||||
return false;
|
||||
}
|
||||
@@ -225,6 +228,8 @@ public class TypeCheckingProcedure {
|
||||
JetType superIn = getInType(parameter, superArgument);
|
||||
JetType superOut = getOutType(parameter, superArgument);
|
||||
|
||||
if (capture(subArgument, superArgument, parameter)) continue;
|
||||
|
||||
boolean argumentIsErrorType = subArgument.getType().isError() || superArgument.getType().isError();
|
||||
if (!argumentIsErrorType && parameter.getVariance() == INVARIANT
|
||||
&& subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
|
||||
@@ -237,4 +242,25 @@ public class TypeCheckingProcedure {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean capture(
|
||||
@NotNull TypeProjection firstProjection,
|
||||
@NotNull TypeProjection secondProjection,
|
||||
@NotNull TypeParameterDescriptor parameter
|
||||
) {
|
||||
// Capturing makes sense only for invariant classes
|
||||
if (parameter.getVariance() != INVARIANT) return false;
|
||||
|
||||
// Now, both subtype and supertype relations transform to equality constraints on type arguments:
|
||||
// Array<T> is a subtype, supertype or equal to Array<out Int> then T captures a type that extends Int: 'Captured(out Int)'
|
||||
// Array<T> is a subtype, supertype or equal to Array<in Int> then T captures a type that extends Int: 'Captured(in Int)'
|
||||
|
||||
if (firstProjection.getProjectionKind() == INVARIANT && secondProjection.getProjectionKind() != INVARIANT) {
|
||||
return constraints.capture(firstProjection.getType(), secondProjection);
|
||||
}
|
||||
if (firstProjection.getProjectionKind() != INVARIANT && secondProjection.getProjectionKind() == INVARIANT) {
|
||||
return constraints.capture(secondProjection.getType(), firstProjection);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.types.checker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
||||
import org.jetbrains.jet.lang.types.TypeProjection;
|
||||
|
||||
/**
|
||||
* Methods of this class return true to continue type checking and false to fail
|
||||
@@ -30,5 +31,7 @@ public interface TypingConstraints {
|
||||
|
||||
boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure);
|
||||
|
||||
boolean capture(@NotNull JetType type, @NotNull TypeProjection typeProjection);
|
||||
|
||||
boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user