TypeSubstitutor rewritten to be more readable
This commit is contained in:
@@ -16,14 +16,16 @@
|
||||
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.SubstitutingScope;
|
||||
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
@@ -32,34 +34,6 @@ public class TypeSubstitutor {
|
||||
|
||||
private static final int MAX_RECURSION_DEPTH = 100;
|
||||
|
||||
public static TypeSubstitutor makeConstantSubstitutor(Collection<TypeParameterDescriptor> typeParameterDescriptors, JetType type) {
|
||||
final Set<TypeConstructor> constructors = Sets.newHashSet();
|
||||
for (TypeParameterDescriptor typeParameterDescriptor : typeParameterDescriptors) {
|
||||
constructors.add(typeParameterDescriptor.getTypeConstructor());
|
||||
}
|
||||
final TypeProjection projection = new TypeProjection(type);
|
||||
|
||||
return TypeSubstitutor.create(new TypeSubstitution() {
|
||||
@Override
|
||||
public TypeProjection get(TypeConstructor key) {
|
||||
if (constructors.contains(key)) {
|
||||
return projection;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TypeConstructor.makeConstantSubstitutor(" + constructors + " -> " + projection + ")";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class MapToTypeSubstitutionAdapter implements TypeSubstitution {
|
||||
private final @NotNull Map<TypeConstructor, TypeProjection> substitutionContext;
|
||||
|
||||
@@ -85,7 +59,7 @@ public class TypeSubstitutor {
|
||||
|
||||
public static final TypeSubstitutor EMPTY = create(TypeSubstitution.EMPTY);
|
||||
|
||||
public static final class SubstitutionException extends Exception {
|
||||
private static final class SubstitutionException extends Exception {
|
||||
public SubstitutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
@@ -143,7 +117,7 @@ public class TypeSubstitutor {
|
||||
}
|
||||
|
||||
try {
|
||||
return unsafeSubstitute(type, howThisTypeIsUsed);
|
||||
return unsafeSubstitute(new TypeProjection(howThisTypeIsUsed, type), 0).getType();
|
||||
} catch (SubstitutionException e) {
|
||||
return ErrorUtils.createErrorType(e.getMessage());
|
||||
}
|
||||
@@ -156,170 +130,100 @@ public class TypeSubstitutor {
|
||||
}
|
||||
|
||||
try {
|
||||
return unsafeSubstitute(type, howThisTypeIsUsed);
|
||||
return unsafeSubstitute(new TypeProjection(howThisTypeIsUsed, type), 0).getType();
|
||||
} catch (SubstitutionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JetType unsafeSubstitute(@NotNull JetType type, @NotNull Variance howThisTypeIsUsed) throws SubstitutionException {
|
||||
if (ErrorUtils.isErrorType(type)) return type;
|
||||
private TypeProjection unsafeSubstitute(@NotNull TypeProjection originalProjection, int recursionDepth) throws SubstitutionException {
|
||||
assertRecursionDepth(recursionDepth, originalProjection, substitution);
|
||||
// The type is within the substitution range, i.e. T or T?
|
||||
JetType type = originalProjection.getType();
|
||||
if (JetStandardClasses.isNothing(type) || ErrorUtils.isErrorType(type)) return originalProjection;
|
||||
|
||||
TypeProjection value = getValueWithCorrectNullability(substitution, type);
|
||||
if (value != null) {
|
||||
TypeConstructor constructor = type.getConstructor();
|
||||
assert constructor.getDeclarationDescriptor() instanceof TypeParameterDescriptor;
|
||||
TypeProjection replacement = substitution.get(type.getConstructor());
|
||||
|
||||
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) constructor.getDeclarationDescriptor();
|
||||
if (replacement != null) {
|
||||
// It must be a type parameter: only they can be directly substituted for
|
||||
TypeParameterDescriptor typeParameter = (TypeParameterDescriptor) type.getConstructor().getDeclarationDescriptor();
|
||||
|
||||
TypeProjection result = substitutionResult(typeParameterDescriptor, howThisTypeIsUsed, Variance.INVARIANT, value, 0);
|
||||
switch (conflictType(originalProjection.getProjectionKind(), replacement.getProjectionKind())) {
|
||||
case OUT_IN_IN_POSITION:
|
||||
throw new SubstitutionException("Out-projection in in-position");
|
||||
case IN_IN_OUT_POSITION:
|
||||
replacement = SubstitutionUtils.makeStarProjection(typeParameter);
|
||||
break;
|
||||
}
|
||||
boolean resultingIsNullable = type.isNullable() || replacement.getType().isNullable();
|
||||
JetType substitutedType = TypeUtils.makeNullableAsSpecified(replacement.getType(), resultingIsNullable);
|
||||
Variance resultingProjectionKind = combine(originalProjection.getProjectionKind(), replacement.getProjectionKind());
|
||||
|
||||
return TypeUtils.makeNullableIfNeeded(result.getType(), type.isNullable());
|
||||
return new TypeProjection(resultingProjectionKind, substitutedType);
|
||||
}
|
||||
else {
|
||||
// The type is not within the substitution range, i.e. Foo, Bar<T> etc.
|
||||
List<TypeProjection> substitutedArguments = substituteTypeArguments(
|
||||
type.getConstructor().getParameters(), type.getArguments(), recursionDepth);
|
||||
|
||||
return specializeType(type, howThisTypeIsUsed, 0);
|
||||
}
|
||||
|
||||
private TypeProjection getValueWithCorrectNullability(TypeSubstitution substitution, JetType type) {
|
||||
TypeProjection typeProjection = substitution.get(type.getConstructor());
|
||||
if (typeProjection == null) return null;
|
||||
|
||||
return type.isNullable() ? makeNullableProjection(typeProjection) : typeProjection;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TypeProjection makeNullableProjection(@NotNull TypeProjection value) {
|
||||
return new TypeProjection(value.getProjectionKind(), TypeUtils.makeNullable(value.getType()));
|
||||
}
|
||||
|
||||
private JetType specializeType(JetType subjectType, Variance callSiteVariance, int recursionDepth) throws SubstitutionException {
|
||||
assertRecursionDepth(recursionDepth, subjectType, substitution);
|
||||
if (ErrorUtils.isErrorType(subjectType)) return subjectType;
|
||||
|
||||
List<TypeProjection> newArguments = new ArrayList<TypeProjection>();
|
||||
List<TypeProjection> arguments = subjectType.getArguments();
|
||||
for (int i = 0, argumentsSize = arguments.size(); i < argumentsSize; i++) {
|
||||
TypeProjection argument = arguments.get(i);
|
||||
TypeParameterDescriptor parameterDescriptor = subjectType.getConstructor().getParameters().get(i);
|
||||
newArguments.add(substituteInProjection(
|
||||
substitution,
|
||||
argument,
|
||||
parameterDescriptor,
|
||||
callSiteVariance, recursionDepth + 1));
|
||||
JetType substitutedType = new JetTypeImpl(type.getAnnotations(), // Old annotations. This is questionable
|
||||
type.getConstructor(), // The same constructor
|
||||
type.isNullable(), // Same nullability
|
||||
substitutedArguments,
|
||||
new SubstitutingScope(type.getMemberScope(), this));
|
||||
return new TypeProjection(originalProjection.getProjectionKind(), substitutedType);
|
||||
}
|
||||
return new JetTypeImpl(
|
||||
subjectType.getAnnotations(),
|
||||
subjectType.getConstructor(),
|
||||
subjectType.isNullable(),
|
||||
newArguments,
|
||||
new SubstitutingScope(subjectType.getMemberScope(), this));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TypeProjection substituteInProjection(
|
||||
@NotNull TypeSubstitution substitutionContext,
|
||||
@NotNull TypeProjection passedProjection,
|
||||
@NotNull TypeParameterDescriptor correspondingTypeParameter,
|
||||
@NotNull Variance contextCallSiteVariance,
|
||||
int recursionDepth) throws SubstitutionException {
|
||||
assertRecursionDepth(recursionDepth, correspondingTypeParameter, passedProjection, substitution);
|
||||
private List<TypeProjection> substituteTypeArguments(List<TypeParameterDescriptor> typeParameters, List<TypeProjection> typeArguments, int recursionDepth)
|
||||
throws SubstitutionException {
|
||||
List<TypeProjection> substitutedArguments = Lists.newArrayList();
|
||||
for (int i = 0; i < typeParameters.size(); i++) {
|
||||
TypeParameterDescriptor typeParameter = typeParameters.get(i);
|
||||
TypeProjection typeArgument = typeArguments.get(i);
|
||||
|
||||
JetType typeToSubstituteIn = passedProjection.getType();
|
||||
if (ErrorUtils.isErrorType(typeToSubstituteIn)) return passedProjection;
|
||||
TypeProjection substitutedTypeArgument = unsafeSubstitute(typeArgument, recursionDepth + 1);
|
||||
|
||||
Variance passedProjectionKind = passedProjection.getProjectionKind();
|
||||
Variance parameterVariance = correspondingTypeParameter.getVariance();
|
||||
|
||||
Variance effectiveProjectionKind = asymmetricOr(passedProjectionKind, parameterVariance);
|
||||
Variance effectiveContextVariance = contextCallSiteVariance.superpose(effectiveProjectionKind);
|
||||
|
||||
TypeProjection projectionValue = getValueWithCorrectNullability(substitutionContext, typeToSubstituteIn);
|
||||
if (projectionValue != null) {
|
||||
assert typeToSubstituteIn.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor;
|
||||
|
||||
if (!allows(parameterVariance, passedProjectionKind)) {
|
||||
return SubstitutionUtils.makeStarProjection(correspondingTypeParameter);
|
||||
switch (conflictType(typeParameter.getVariance(), substitutedTypeArgument.getProjectionKind())) {
|
||||
case OUT_IN_IN_POSITION:
|
||||
substitutedTypeArgument = new TypeProjection(Variance.IN_VARIANCE, typeParameter.getLowerBoundsAsType());
|
||||
break;
|
||||
case IN_IN_OUT_POSITION:
|
||||
substitutedTypeArgument = SubstitutionUtils.makeStarProjection(typeParameter);
|
||||
break;
|
||||
}
|
||||
|
||||
return substitutionResult(correspondingTypeParameter, effectiveContextVariance, passedProjectionKind, projectionValue, recursionDepth + 1);
|
||||
substitutedArguments.add(substitutedTypeArgument);
|
||||
}
|
||||
return new TypeProjection(
|
||||
passedProjectionKind,
|
||||
specializeType(
|
||||
typeToSubstituteIn,
|
||||
effectiveContextVariance, recursionDepth + 1));
|
||||
return substitutedArguments;
|
||||
}
|
||||
|
||||
private TypeProjection substitutionResult(
|
||||
TypeParameterDescriptor correspondingTypeParameter,
|
||||
Variance effectiveContextVariance,
|
||||
Variance passedProjectionKind,
|
||||
TypeProjection value,
|
||||
int recursionDepth) throws SubstitutionException {
|
||||
assertRecursionDepth(recursionDepth, correspondingTypeParameter, value, substitution);
|
||||
private static Variance combine(Variance typeParameterVariance, Variance projectionKind) {
|
||||
if (typeParameterVariance == Variance.INVARIANT) return projectionKind;
|
||||
if (projectionKind == Variance.INVARIANT) return typeParameterVariance;
|
||||
return typeParameterVariance.superpose(projectionKind);
|
||||
}
|
||||
|
||||
Variance projectionKindValue = value.getProjectionKind();
|
||||
JetType typeValue = value.getType();
|
||||
Variance effectiveProjectionKindValue = asymmetricOr(passedProjectionKind, projectionKindValue);
|
||||
JetType effectiveTypeValue;
|
||||
switch (effectiveContextVariance) {
|
||||
case INVARIANT:
|
||||
effectiveProjectionKindValue = projectionKindValue;
|
||||
effectiveTypeValue = typeValue;
|
||||
break;
|
||||
case IN_VARIANCE:
|
||||
if (projectionKindValue == Variance.OUT_VARIANCE) {
|
||||
throw new SubstitutionException(""); // TODO
|
||||
// effectiveProjectionKindValue = Variance.INVARIANT;
|
||||
// effectiveTypeValue = JetStandardClasses.getNothingType();
|
||||
}
|
||||
else {
|
||||
effectiveTypeValue = typeValue;
|
||||
}
|
||||
break;
|
||||
case OUT_VARIANCE:
|
||||
if (projectionKindValue == Variance.IN_VARIANCE) {
|
||||
effectiveProjectionKindValue = Variance.INVARIANT;
|
||||
effectiveTypeValue = correspondingTypeParameter.getUpperBoundsAsType();
|
||||
}
|
||||
else {
|
||||
effectiveTypeValue = typeValue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(effectiveContextVariance.toString());
|
||||
private enum VarianceConflictType {
|
||||
NO_CONFLICT,
|
||||
IN_IN_OUT_POSITION,
|
||||
OUT_IN_IN_POSITION;
|
||||
}
|
||||
|
||||
private static VarianceConflictType conflictType(Variance position, Variance argument) {
|
||||
if (position == Variance.IN_VARIANCE && argument == Variance.OUT_VARIANCE) {
|
||||
return VarianceConflictType.OUT_IN_IN_POSITION;
|
||||
}
|
||||
|
||||
// if (!allows(effectiveContextVariance, projectionKindValue)) {
|
||||
// throw new SubstitutionException(""); // TODO : error message
|
||||
// }
|
||||
//
|
||||
return new TypeProjection(effectiveProjectionKindValue, specializeType(effectiveTypeValue, effectiveContextVariance, recursionDepth + 1));
|
||||
}
|
||||
|
||||
private static Variance asymmetricOr(Variance a, Variance b) {
|
||||
return a == Variance.INVARIANT ? b : a;
|
||||
}
|
||||
|
||||
private static boolean allows(Variance declarationSiteVariance, Variance callSiteVariance) {
|
||||
switch (declarationSiteVariance) {
|
||||
case INVARIANT: return true;
|
||||
case IN_VARIANCE: return callSiteVariance != Variance.OUT_VARIANCE;
|
||||
case OUT_VARIANCE: return callSiteVariance != Variance.IN_VARIANCE;
|
||||
if (position == Variance.OUT_VARIANCE && argument == Variance.IN_VARIANCE) {
|
||||
return VarianceConflictType.IN_IN_OUT_POSITION;
|
||||
}
|
||||
throw new IllegalStateException(declarationSiteVariance.toString());
|
||||
return VarianceConflictType.NO_CONFLICT;
|
||||
}
|
||||
|
||||
private static void assertRecursionDepth(int recursionDepth, TypeParameterDescriptor parameter, TypeProjection value, TypeSubstitution substitution) {
|
||||
private static void assertRecursionDepth(int recursionDepth, TypeProjection projection, TypeSubstitution substitution) {
|
||||
if (recursionDepth > MAX_RECURSION_DEPTH) {
|
||||
throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(value) + " for " + safeToString(parameter) + "; substitution: " + safeToString(substitution));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertRecursionDepth(int recursionDepth, JetType type, TypeSubstitution substitution) {
|
||||
if (recursionDepth > MAX_RECURSION_DEPTH) {
|
||||
throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(type) + "; substitution: " + safeToString(substitution));
|
||||
throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(projection) + "; substitution: " + safeToString(substitution));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
class In<in T>() {
|
||||
fun f(<!UNUSED_PARAMETER!>t<!> : T) : Unit {}
|
||||
fun f(<!UNUSED_PARAMETER!>t<!> : Int) : Int = 1
|
||||
fun f1(<!UNUSED_PARAMETER!>t<!> : T) : Unit {}
|
||||
}
|
||||
|
||||
class Out<out T>() {
|
||||
fun f() : T {throw IllegalStateException()}
|
||||
fun f(a : Int) : Int = a
|
||||
}
|
||||
|
||||
class Inv<T>() {
|
||||
fun f(t : T) : T = t
|
||||
fun inf(<!UNUSED_PARAMETER!>t<!> : T) : Unit {}
|
||||
fun outf() : T {throw IllegalStateException()}
|
||||
}
|
||||
|
||||
fun testInOut() {
|
||||
In<String>().f("1");
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<in String>).f("1")
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<out String>).f(<!TYPE_MISMATCH!>"1"<!>) // Wrong Arg
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<*>).f(<!TYPE_MISMATCH!>"1"<!>) // Wrong Arg
|
||||
|
||||
In<String>().f(1);
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<in String>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<out String>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<out String>).<!UNRESOLVED_REFERENCE!>f1<!>(1) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<*>).f(1);
|
||||
|
||||
Out<Int>().f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<out Int>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<in Int>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<*>).f(1)
|
||||
|
||||
Out<Int>().f()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<out Int>).f()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<in Int>).f()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<*>).f()
|
||||
|
||||
Inv<Int>().f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<in Int>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<out Int>).<!UNRESOLVED_REFERENCE!>f<!>(1) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<*>).<!UNRESOLVED_REFERENCE!>f<!>(1) // !!
|
||||
|
||||
Inv<Int>().inf(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<in Int>).inf(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<out Int>).<!UNRESOLVED_REFERENCE!>inf<!>(1) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<*>).<!UNRESOLVED_REFERENCE!>inf<!>(1) // !!
|
||||
|
||||
Inv<Int>().outf()
|
||||
<!TYPE_MISMATCH!>(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<in Int>).outf()<!> : Int // Type mismatch
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<out Int>).outf()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<*>).outf()
|
||||
|
||||
Inv<Int>().outf(<!TOO_MANY_ARGUMENTS!>1<!>) // Wrong Arg
|
||||
}
|
||||
Reference in New Issue
Block a user