Don't approximate captured types while substitute them in upper bound position

This commit is contained in:
Victor Petukhov
2021-07-07 14:01:26 +03:00
parent c2cf2f36cd
commit 3b16865152
4 changed files with 33 additions and 14 deletions
@@ -12,15 +12,15 @@ class A<T> {
}
fun foo2(a: A<out CharSequence>, b: A<in CharSequence>) {
a.foo1(Out<CharSequence>())
a.foo1<Out<CharSequence>>(Out())
a.foo1(<!TYPE_MISMATCH!>Out<CharSequence>()<!>)
a.foo1<<!UPPER_BOUND_VIOLATED!>Out<CharSequence><!>>(Out())
a.foo1(Out())
a.foo1(Out<Nothing>())
a.foo2(Inv())
a.foo2(Inv<CharSequence>())
a.foo2<Inv<CharSequence>>(Inv())
a.foo2(<!TYPE_MISMATCH!>Inv<CharSequence>()<!>)
a.foo2<<!UPPER_BOUND_VIOLATED!>Inv<CharSequence><!>>(<!TYPE_MISMATCH!>Inv()<!>)
a.foo3(In())
a.foo3(In<CharSequence>())
@@ -31,12 +31,12 @@ fun foo2(a: A<out CharSequence>, b: A<in CharSequence>) {
b.foo1<Out<CharSequence>>(Out())
b.foo2(Inv())
b.foo2(Inv<CharSequence>())
b.foo2<Inv<CharSequence>>(Inv())
b.foo2(<!TYPE_MISMATCH!>Inv<CharSequence>()<!>)
b.foo2<<!UPPER_BOUND_VIOLATED!>Inv<CharSequence><!>>(<!TYPE_MISMATCH!>Inv()<!>)
b.foo3(In<CharSequence>())
b.foo3<In<CharSequence>>(In())
b.foo3(<!TYPE_MISMATCH!>In<CharSequence>()<!>)
b.foo3<<!UPPER_BOUND_VIOLATED!>In<CharSequence><!>>(In())
b.foo3(In<Any?>())
b.foo3(In())
@@ -20,10 +20,12 @@ import kotlin.annotations.jvm.Mutable;
import kotlin.annotations.jvm.ReadOnly;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.SourceElement;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import java.util.HashMap;
import java.util.List;
@@ -55,7 +57,7 @@ public class DescriptorSubstitutor {
@NotNull @Mutable List<TypeParameterDescriptor> result,
@Nullable boolean[] wereChanges
) {
Map<TypeConstructor, TypeProjection> mutableSubstitution = new HashMap<TypeConstructor, TypeProjection>();
Map<TypeConstructor, TypeProjection> mutableSubstitutionMap = new HashMap<TypeConstructor, TypeProjection>();
Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> substitutedMap = new HashMap<TypeParameterDescriptor, TypeParameterDescriptorImpl>();
int index = 0;
@@ -71,20 +73,26 @@ public class DescriptorSubstitutor {
descriptor.getStorageManager()
);
mutableSubstitution.put(descriptor.getTypeConstructor(), new TypeProjectionImpl(substituted.getDefaultType()));
mutableSubstitutionMap.put(descriptor.getTypeConstructor(), new TypeProjectionImpl(substituted.getDefaultType()));
substitutedMap.put(descriptor, substituted);
result.add(substituted);
}
TypeSubstitutor substitutor = TypeSubstitutor.createChainedSubstitutor(
originalSubstitution, TypeConstructorSubstitution.createByConstructorsMap(mutableSubstitution)
);
TypeConstructorSubstitution mutableSubstitution = TypeConstructorSubstitution.createByConstructorsMap(mutableSubstitutionMap);
TypeSubstitutor substitutor = TypeSubstitutor.createChainedSubstitutor(originalSubstitution, mutableSubstitution);
TypeSubstitutor nonApproximatingSubstitutor =
TypeSubstitutor.createChainedSubstitutor(originalSubstitution.replaceWithNonApproximating(), mutableSubstitution);
for (TypeParameterDescriptor descriptor : typeParameters) {
TypeParameterDescriptorImpl substituted = substitutedMap.get(descriptor);
for (KotlinType upperBound : descriptor.getUpperBounds()) {
KotlinType substitutedBound = substitutor.substitute(upperBound, Variance.OUT_VARIANCE);
ClassifierDescriptor upperBoundDeclaration = upperBound.getConstructor().getDeclarationDescriptor();
TypeSubstitutor boundSubstitutor = upperBoundDeclaration instanceof TypeParameterDescriptor && TypeUtilsKt.hasTypeParameterRecursiveBounds((TypeParameterDescriptor) upperBoundDeclaration)
? substitutor
: nonApproximatingSubstitutor;
KotlinType substitutedBound = boundSubstitutor.substitute(upperBound, Variance.OUT_VARIANCE);
if (substitutedBound == null) return null;
if (substitutedBound != upperBound && wereChanges != null) {
@@ -42,6 +42,16 @@ abstract class TypeSubstitution {
open fun filterAnnotations(annotations: Annotations) = annotations
fun buildSubstitutor(): TypeSubstitutor = TypeSubstitutor.create(this)
fun replaceWithNonApproximating() = object : TypeSubstitution() {
override fun get(key: KotlinType) = this@TypeSubstitution[key]
override fun approximateCapturedTypes() = false
override fun approximateContravariantCapturedTypes() = false
override fun filterAnnotations(annotations: Annotations) = this@TypeSubstitution.filterAnnotations(annotations)
override fun prepareTopLevelType(topLevelType: KotlinType, position: Variance) =
this@TypeSubstitution.prepareTopLevelType(topLevelType, position)
override fun isEmpty() = this@TypeSubstitution.isEmpty()
}
}
abstract class TypeConstructorSubstitution : TypeSubstitution() {
@@ -216,6 +216,7 @@ private fun KotlinType.extractTypeParametersFromUpperBounds(
}
}
@JvmOverloads
fun hasTypeParameterRecursiveBounds(
typeParameter: TypeParameterDescriptor,
selfConstructor: TypeConstructor? = null,