Use NewKotlinTypeChecker in OverridingUtil

This allows to use deep type refinement in OverridingUtil, fixing issues
with expect/actuals in composite types
This commit is contained in:
Dmitry Savvinov
2020-10-05 18:00:28 +03:00
parent 01d6181050
commit fc4b488d43
@@ -31,13 +31,9 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl; import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl;
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.types.FlexibleTypesKt; import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.checker.*;
import org.jetbrains.kotlin.types.KotlinTypeKt; import org.jetbrains.kotlin.types.model.TypeConstructorMarker;
import org.jetbrains.kotlin.types.TypeConstructor;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.checker.KotlinTypeCheckerImpl;
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner;
import org.jetbrains.kotlin.utils.SmartSet; import org.jetbrains.kotlin.utils.SmartSet;
import java.util.*; import java.util.*;
@@ -313,18 +309,22 @@ public class OverridingUtil {
return OverrideCompatibilityInfo.conflict("Type parameter number mismatch"); return OverrideCompatibilityInfo.conflict("Type parameter number mismatch");
} }
KotlinTypeChecker typeChecker = createTypeChecker(superTypeParameters, subTypeParameters); Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext> typeChecker = createTypeChecker(superTypeParameters, subTypeParameters);
for (int i = 0; i < superTypeParameters.size(); i++) { for (int i = 0; i < superTypeParameters.size(); i++) {
if (!areTypeParametersEquivalent(superTypeParameters.get(i), subTypeParameters.get(i), typeChecker)) { if (!areTypeParametersEquivalent(
superTypeParameters.get(i),
subTypeParameters.get(i),
typeChecker
)) {
return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch"); return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch");
} }
} }
for (int i = 0; i < superValueParameters.size(); i++) { for (int i = 0; i < superValueParameters.size(); i++) {
if (!areTypesEquivalent( if (!areTypesEquivalent(
kotlinTypeRefiner.refineType(superValueParameters.get(i)), superValueParameters.get(i),
kotlinTypeRefiner.refineType(subValueParameters.get(i)), subValueParameters.get(i),
typeChecker) typeChecker)
) { ) {
return OverrideCompatibilityInfo.incompatible("Value parameter type mismatch"); return OverrideCompatibilityInfo.incompatible("Value parameter type mismatch");
@@ -343,9 +343,10 @@ public class OverridingUtil {
if (superReturnType != null && subReturnType != null) { if (superReturnType != null && subReturnType != null) {
boolean bothErrors = KotlinTypeKt.isError(subReturnType) && KotlinTypeKt.isError(superReturnType); boolean bothErrors = KotlinTypeKt.isError(subReturnType) && KotlinTypeKt.isError(superReturnType);
if (!bothErrors && if (!bothErrors &&
!typeChecker.isSubtypeOf( !typeChecker.getFirst().isSubtypeOf(
kotlinTypeRefiner.refineType(subReturnType), typeChecker.getSecond(),
kotlinTypeRefiner.refineType(superReturnType) subReturnType.unwrap(),
superReturnType.unwrap()
) )
) { ) {
return OverrideCompatibilityInfo.conflict("Return type mismatch"); return OverrideCompatibilityInfo.conflict("Return type mismatch");
@@ -384,28 +385,32 @@ public class OverridingUtil {
} }
@NotNull @NotNull
private KotlinTypeChecker createTypeChecker( private Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext> createTypeChecker(
@NotNull List<TypeParameterDescriptor> firstParameters, @NotNull List<TypeParameterDescriptor> firstParameters,
@NotNull List<TypeParameterDescriptor> secondParameters @NotNull List<TypeParameterDescriptor> secondParameters
) { ) {
assert firstParameters.size() == secondParameters.size() : assert firstParameters.size() == secondParameters.size() :
"Should be the same number of type parameters: " + firstParameters + " vs " + secondParameters; "Should be the same number of type parameters: " + firstParameters + " vs " + secondParameters;
if (firstParameters.isEmpty()) return KotlinTypeCheckerImpl.withAxioms(equalityAxioms);
final Map<TypeConstructor, TypeConstructor> matchingTypeConstructors = new HashMap<TypeConstructor, TypeConstructor>(); NewKotlinTypeCheckerImpl typeChecker = new NewKotlinTypeCheckerImpl(kotlinTypeRefiner);
OverridingUtilTypeCheckerContext context = createTypeCheckerContext(firstParameters, secondParameters);
return new Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext>(typeChecker, context);
}
@NotNull
private OverridingUtilTypeCheckerContext createTypeCheckerContext(
@NotNull List<TypeParameterDescriptor> firstParameters,
@NotNull List<TypeParameterDescriptor> secondParameters
) {
if (firstParameters.isEmpty()) return new OverridingUtilTypeCheckerContext(null);
Map<TypeConstructor, TypeConstructor> matchingTypeConstructors = new HashMap<TypeConstructor, TypeConstructor>();
for (int i = 0; i < firstParameters.size(); i++) { for (int i = 0; i < firstParameters.size(); i++) {
matchingTypeConstructors.put(firstParameters.get(i).getTypeConstructor(), secondParameters.get(i).getTypeConstructor()); matchingTypeConstructors.put(firstParameters.get(i).getTypeConstructor(), secondParameters.get(i).getTypeConstructor());
} }
return KotlinTypeCheckerImpl.withAxioms(new KotlinTypeChecker.TypeConstructorEquality() { return new OverridingUtilTypeCheckerContext(matchingTypeConstructors);
@Override
public boolean equals(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
if (equalityAxioms.equals(a, b)) return true;
TypeConstructor img1 = matchingTypeConstructors.get(a);
TypeConstructor img2 = matchingTypeConstructors.get(b);
return (img1 != null && img1.equals(b)) || (img2 != null && img2.equals(a));
}
});
} }
@Nullable @Nullable
@@ -427,19 +432,18 @@ public class OverridingUtil {
private boolean areTypesEquivalent( private boolean areTypesEquivalent(
@NotNull KotlinType typeInSuper, @NotNull KotlinType typeInSuper,
@NotNull KotlinType typeInSub, @NotNull KotlinType typeInSub,
@NotNull KotlinTypeChecker typeChecker @NotNull Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext> typeChecker
) { ) {
boolean bothErrors = KotlinTypeKt.isError(typeInSuper) && KotlinTypeKt.isError(typeInSub); boolean bothErrors = KotlinTypeKt.isError(typeInSuper) && KotlinTypeKt.isError(typeInSub);
if (bothErrors) return true; if (bothErrors) return true;
return typeChecker.equalTypes(kotlinTypeRefiner.refineType(typeInSuper), kotlinTypeRefiner.refineType(typeInSub)); return typeChecker.getFirst().equalTypes(typeChecker.getSecond(), typeInSuper.unwrap(), typeInSub.unwrap());
} }
// See JLS 8, 8.4.4 Generic Methods // See JLS 8, 8.4.4 Generic Methods
// TODO: use TypeSubstitutor instead
private boolean areTypeParametersEquivalent( private boolean areTypeParametersEquivalent(
@NotNull TypeParameterDescriptor superTypeParameter, @NotNull TypeParameterDescriptor superTypeParameter,
@NotNull TypeParameterDescriptor subTypeParameter, @NotNull TypeParameterDescriptor subTypeParameter,
@NotNull KotlinTypeChecker typeChecker @NotNull Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext> typeChecker
) { ) {
List<KotlinType> superBounds = superTypeParameter.getUpperBounds(); List<KotlinType> superBounds = superTypeParameter.getUpperBounds();
List<KotlinType> subBounds = new ArrayList<KotlinType>(subTypeParameter.getUpperBounds()); List<KotlinType> subBounds = new ArrayList<KotlinType>(subTypeParameter.getUpperBounds());
@@ -577,10 +581,13 @@ public class OverridingUtil {
if (!isVisibilityMoreSpecific(a, b)) return false; if (!isVisibilityMoreSpecific(a, b)) return false;
Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext> checker =
DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters());
if (a instanceof FunctionDescriptor) { if (a instanceof FunctionDescriptor) {
assert b instanceof FunctionDescriptor : "b is " + b.getClass(); assert b instanceof FunctionDescriptor : "b is " + b.getClass();
return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType); return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType, checker);
} }
if (a instanceof PropertyDescriptor) { if (a instanceof PropertyDescriptor) {
assert b instanceof PropertyDescriptor : "b is " + b.getClass(); assert b instanceof PropertyDescriptor : "b is " + b.getClass();
@@ -591,11 +598,12 @@ public class OverridingUtil {
if (!isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false; if (!isAccessorMoreSpecific(pa.getSetter(), pb.getSetter())) return false;
if (pa.isVar() && pb.isVar()) { if (pa.isVar() && pb.isVar()) {
return DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters()).equalTypes(aReturnType, bReturnType); // TODO(dsavvinov): using DEFAULT here looks suspicious
return checker.getFirst().equalTypes(checker.getSecond(), aReturnType.unwrap(), bReturnType.unwrap());
} }
else { else {
// both vals or var vs val: val can't be more specific then var // both vals or var vs val: val can't be more specific then var
return !(!pa.isVar() && pb.isVar()) && isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType); return !(!pa.isVar() && pb.isVar()) && isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType, checker);
} }
} }
throw new IllegalArgumentException("Unexpected callable: " + a.getClass()); throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
@@ -629,10 +637,10 @@ public class OverridingUtil {
@NotNull CallableDescriptor a, @NotNull CallableDescriptor a,
@NotNull KotlinType aReturnType, @NotNull KotlinType aReturnType,
@NotNull CallableDescriptor b, @NotNull CallableDescriptor b,
@NotNull KotlinType bReturnType @NotNull KotlinType bReturnType,
@NotNull Pair<NewKotlinTypeCheckerImpl, ClassicTypeCheckerContext> typeChecker
) { ) {
KotlinTypeChecker typeChecker = DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters()); return typeChecker.getFirst().isSubtypeOf(typeChecker.getSecond(), aReturnType.unwrap(), bReturnType.unwrap());
return typeChecker.isSubtypeOf(aReturnType, bReturnType);
} }
@NotNull @NotNull
@@ -969,6 +977,41 @@ public class OverridingUtil {
return maxVisibility; return maxVisibility;
} }
private class OverridingUtilTypeCheckerContext extends ClassicTypeCheckerContext {
private final @Nullable Map<TypeConstructor, TypeConstructor> matchingTypeConstructors;
public OverridingUtilTypeCheckerContext(@Nullable Map<TypeConstructor, TypeConstructor> matchingTypeConstructors) {
super(
/* errorTypesEqualsToAnything = */ true,
/* stubTypesEqualsToAnything = */ true,
/* allowedTypeVariable = */ true,
kotlinTypeRefiner
);
this.matchingTypeConstructors = matchingTypeConstructors;
}
@Override
public boolean areEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
return super.areEqualTypeConstructors(a, b) || areEqualTypeConstructorsByAxioms(a, b);
}
@Override
public boolean isEqualTypeConstructors(
@NotNull TypeConstructorMarker c1, @NotNull TypeConstructorMarker c2
) {
return areEqualTypeConstructors(c1, c2);
}
private boolean areEqualTypeConstructorsByAxioms(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
if (equalityAxioms.equals(a, b)) return true;
if (matchingTypeConstructors == null) return false;
TypeConstructor img1 = matchingTypeConstructors.get(a);
TypeConstructor img2 = matchingTypeConstructors.get(b);
return (img1 != null && img1.equals(b)) || (img2 != null && img2.equals(a));
}
}
public static class OverrideCompatibilityInfo { public static class OverrideCompatibilityInfo {
public enum Result { public enum Result {
OVERRIDABLE, OVERRIDABLE,