[NI] Properly support UnsafeVariance annotation

#KT-38134 Fixed
 #KT-34433 Fixed
 #KT-31823 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2020-04-16 04:27:22 +03:00
parent 1614c1b8a6
commit 4ffcbc0c2f
20 changed files with 244 additions and 20 deletions
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations;
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructorKt;
import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor;
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
@@ -91,7 +92,7 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
}
try {
return unsafeSubstitute(new TypeProjectionImpl(howThisTypeIsUsed, type), 0).getType();
return unsafeSubstitute(new TypeProjectionImpl(howThisTypeIsUsed, type), null, 0).getType();
} catch (SubstitutionException e) {
return ErrorUtils.createErrorType(e.getMessage());
}
@@ -121,14 +122,18 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
}
try {
return unsafeSubstitute(typeProjection, 0);
return unsafeSubstitute(typeProjection, null, 0);
} catch (SubstitutionException e) {
return null;
}
}
@NotNull
private TypeProjection unsafeSubstitute(@NotNull TypeProjection originalProjection, int recursionDepth) throws SubstitutionException {
private TypeProjection unsafeSubstitute(
@NotNull TypeProjection originalProjection,
@Nullable TypeParameterDescriptor typeParameter,
int recursionDepth
) throws SubstitutionException {
assertRecursionDepth(recursionDepth, originalProjection, substitution);
if (originalProjection.isStarProjection()) return originalProjection;
@@ -141,6 +146,7 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
TypeProjection substitution = unsafeSubstitute(
new TypeProjectionImpl(originalProjection.getProjectionKind(), origin),
typeParameter,
recursionDepth + 1
);
@@ -154,14 +160,27 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
return originalProjection; // todo investigate
}
TypeProjection replacement = substitution.get(type);
TypeProjection substituted = substitution.get(type);
TypeProjection replacement =
substituted != null ?
projectedTypeForConflictedTypeWithUnsafeVariance(type, substituted, typeParameter, originalProjection) :
null;
Variance originalProjectionKind = originalProjection.getProjectionKind();
if (replacement == null && FlexibleTypesKt.isFlexible(type) && !TypeCapabilitiesKt.isCustomTypeVariable(type)) {
FlexibleType flexibleType = FlexibleTypesKt.asFlexibleType(type);
TypeProjection substitutedLower =
unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibleType.getLowerBound()), recursionDepth + 1);
unsafeSubstitute(
new TypeProjectionImpl(originalProjectionKind, flexibleType.getLowerBound()),
typeParameter,
recursionDepth + 1
);
TypeProjection substitutedUpper =
unsafeSubstitute(new TypeProjectionImpl(originalProjectionKind, flexibleType.getUpperBound()), recursionDepth + 1);
unsafeSubstitute(
new TypeProjectionImpl(originalProjectionKind, flexibleType.getUpperBound()),
typeParameter,
recursionDepth + 1
);
Variance substitutedProjectionKind = substitutedLower.getProjectionKind();
assert (substitutedProjectionKind == substitutedUpper.getProjectionKind()) &&
@@ -225,6 +244,37 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
return substituteCompoundType(originalProjection, recursionDepth);
}
@NotNull
private static TypeProjection projectedTypeForConflictedTypeWithUnsafeVariance(
@NotNull KotlinType originalType,
@NotNull TypeProjection substituted,
@Nullable TypeParameterDescriptor typeParameter,
@NotNull TypeProjection originalProjection
) {
if (!originalType.getAnnotations().hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) return substituted;
TypeConstructor constructor = substituted.getType().getConstructor();
if (!(constructor instanceof NewCapturedTypeConstructor)) return substituted;
NewCapturedTypeConstructor capturedType = (NewCapturedTypeConstructor) constructor;
TypeProjection capturedTypeProjection = capturedType.getProjection();
Variance varianceOfCapturedType = capturedTypeProjection.getProjectionKind();
VarianceConflictType conflictWithTopLevelType = conflictType(originalProjection.getProjectionKind(), varianceOfCapturedType);
if (conflictWithTopLevelType == VarianceConflictType.OUT_IN_IN_POSITION) {
return new TypeProjectionImpl(capturedTypeProjection.getType());
}
if (typeParameter == null) return substituted;
VarianceConflictType conflictTypeWithTypeParameter = conflictType(typeParameter.getVariance(), varianceOfCapturedType);
if (conflictTypeWithTypeParameter == VarianceConflictType.OUT_IN_IN_POSITION) {
return new TypeProjectionImpl(capturedTypeProjection.getType());
}
return substituted;
}
@NotNull
private static Annotations filterOutUnsafeVariance(@NotNull Annotations annotations) {
if (!annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) return annotations;
@@ -275,7 +325,7 @@ public class TypeSubstitutor implements TypeSubstitutorMarker {
TypeParameterDescriptor typeParameter = typeParameters.get(i);
TypeProjection typeArgument = typeArguments.get(i);
TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, recursionDepth + 1);
TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, typeParameter, recursionDepth + 1);
switch (conflictType(typeParameter.getVariance(), substitutedTypeArgument.getProjectionKind())) {
case NO_CONFLICT: