CommonSupertypes factored out

equalTypes() moved to TypeUtils
Lower bounds (stub implementation) introduced to type parameters
This commit is contained in:
Andrey Breslav
2011-11-09 16:56:05 +03:00
parent 0a2ad80234
commit d4ea1d42c8
23 changed files with 832 additions and 795 deletions
@@ -248,7 +248,7 @@ public class JetTypeMapper {
}
if (descriptor instanceof TypeParameterDescriptor) {
return mapType(((TypeParameterDescriptor) descriptor).getBoundsAsType(), kind);
return mapType(((TypeParameterDescriptor) descriptor).getUpperBoundsAsType(), kind);
}
throw new UnsupportedOperationException("Unknown type " + jetType);
@@ -25,7 +25,7 @@ public class JetSemanticServices {
private JetSemanticServices(JetStandardLibrary standardLibrary) {
this.standardLibrary = standardLibrary;
this.typeChecker = new JetTypeChecker(standardLibrary);
this.typeChecker = JetTypeChecker.INSTANCE;
}
@NotNull
@@ -43,7 +43,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement
private final int index;
private final Variance variance;
private final Set<JetType> upperBounds;
private JetType boundsAsType;
private JetType upperBoundsAsType;
private final TypeConstructor typeConstructor;
private JetType defaultType;
private final Set<JetType> classObjectUpperBounds = Sets.newLinkedHashSet();
@@ -85,10 +85,35 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement
upperBounds.add(bound); // TODO : Duplicates?
}
@NotNull
public Set<JetType> getUpperBounds() {
return upperBounds;
}
@NotNull
public JetType getUpperBoundsAsType() {
if (upperBoundsAsType == null) {
assert upperBounds != null : "Upper bound list is null in " + getName();
assert upperBounds.size() > 0 : "Upper bound list is empty in " + getName();
upperBoundsAsType = TypeUtils.intersect(JetTypeChecker.INSTANCE, upperBounds);
if (upperBoundsAsType == null) {
upperBoundsAsType = JetStandardClasses.getNothingType();
}
}
return upperBoundsAsType;
}
@NotNull
public Set<JetType> getLowerBounds() {
return Collections.singleton(JetStandardClasses.getNothingType());
}
@NotNull
public JetType getLowerBoundsAsType() {
return JetStandardClasses.getNothingType();
}
@NotNull
@Override
public TypeConstructor getTypeConstructor() {
@@ -100,19 +125,6 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement
return DescriptorRenderer.TEXT.render(this);
}
@NotNull
public JetType getBoundsAsType() {
if (boundsAsType == null) {
assert upperBounds != null : "Upper bound list is null in " + getName();
assert upperBounds.size() > 0 : "Upper bound list is empty in " + getName();
boundsAsType = TypeUtils.intersect(JetTypeChecker.INSTANCE, upperBounds);
if (boundsAsType == null) {
boundsAsType = JetStandardClasses.getNothingType();
}
}
return boundsAsType;
}
@NotNull
@Override
@Deprecated // Use the static method TypeParameterDescriptor.substitute()
@@ -137,7 +149,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement
new LazyScopeAdapter(new LazyValue<JetScope>() {
@Override
protected JetScope compute() {
return getBoundsAsType().getMemberScope();
return getUpperBoundsAsType().getMemberScope();
}
}));
}
@@ -379,7 +379,7 @@ public class ClassDescriptorResolver {
parameter.addUpperBound(JetStandardClasses.getDefaultBound());
}
if (JetStandardClasses.isNothing(parameter.getBoundsAsType())) {
if (JetStandardClasses.isNothing(parameter.getUpperBoundsAsType())) {
PsiElement nameIdentifier = typeParameters.get(parameter.getIndex()).getNameIdentifier();
if (nameIdentifier != null) {
// trace.getErrorHandler().genericError(nameIdentifier.getNode(), "Upper bounds of " + parameter.getName() + " have empty intersection");
@@ -666,7 +666,7 @@ public class ClassDescriptorResolver {
type = typeResolver.resolveType(scope, typeReference);
JetType inType = propertyDescriptor.getInType();
if (inType != null) {
if (!semanticServices.getTypeChecker().equalTypes(type, inType)) {
if (!TypeUtils.equalTypes(type, inType)) {
// trace.getErrorHandler().genericError(typeReference.getNode(), "Setter parameter type must be equal to the type of the property, i.e. " + inType);
trace.report(WRONG_SETTER_PARAMETER_TYPE.on(setter, typeReference, inType));
}
@@ -709,7 +709,7 @@ public class ClassDescriptorResolver {
JetTypeReference returnTypeReference = getter.getReturnTypeReference();
if (returnTypeReference != null) {
returnType = typeResolver.resolveType(scope, returnTypeReference);
if (outType != null && !semanticServices.getTypeChecker().equalTypes(returnType, outType)) {
if (outType != null && !TypeUtils.equalTypes(returnType, outType)) {
// trace.getErrorHandler().genericError(returnTypeReference.getNode(), "Getter return type must be equal to the type of the property, i.e. " + propertyDescriptor.getReturnType());
trace.report(WRONG_GETTER_RETURN_TYPE.on(getter, returnTypeReference, propertyDescriptor.getReturnType()));
}
@@ -127,7 +127,7 @@ public class OverridingUtil {
TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i);
TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i);
if (!JetTypeImpl.equalTypes(superTypeParameter.getBoundsAsType(), subTypeParameter.getBoundsAsType(), axioms)) {
if (!JetTypeImpl.equalTypes(superTypeParameter.getUpperBoundsAsType(), subTypeParameter.getUpperBoundsAsType(), axioms)) {
return OverrideCompatibilityInfo.boundsMismatch(superTypeParameter, subTypeParameter);
}
}
@@ -181,13 +181,13 @@ public class TypeResolver {
private JetScope getScopeForTypeParameter(final TypeParameterDescriptor typeParameterDescriptor) {
if (checkBounds) {
return typeParameterDescriptor.getBoundsAsType().getMemberScope();
return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope();
}
else {
return new LazyScopeAdapter(new LazyValue<JetScope>() {
@Override
protected JetScope compute() {
return typeParameterDescriptor.getBoundsAsType().getMemberScope();
return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope();
}
});
}
@@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.JetSemanticServices;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl;
@@ -21,6 +20,8 @@ import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lang.types.inference.ConstraintSystem;
import org.jetbrains.jet.lang.types.inference.ConstraintSystemSolution;
import org.jetbrains.jet.lang.types.inference.SubtypingOnlyConstraintSystem;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*;
@@ -413,7 +414,7 @@ public class CallResolver {
if (!candidate.getTypeParameters().isEmpty()) {
// Type argument inference
ConstraintSystem constraintSystem = new ConstraintSystem();
ConstraintSystem constraintSystem = new SubtypingOnlyConstraintSystem();
for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) {
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO
}
@@ -451,7 +452,7 @@ public class CallResolver {
constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType);
}
ConstraintSystem.Solution solution = constraintSystem.solve();
ConstraintSystemSolution solution = constraintSystem.solve();
// solutions.put(candidate, solution);
if (solution.isSuccessful()) {
D substitute = (D) candidate.substitute(solution.getSubstitutor());
@@ -884,7 +885,7 @@ public class CallResolver {
for (int i = 0; i < valueParameters.size(); i++) {
ValueParameterDescriptor valueParameter = valueParameters.get(i);
JetType expectedType = parameterTypes.get(i);
if (!semanticServices.getTypeChecker().equalTypes(expectedType, valueParameter.getOutType())) return false;
if (!TypeUtils.equalTypes(expectedType, valueParameter.getOutType())) return false;
}
return true;
}
@@ -12,6 +12,7 @@ import org.jetbrains.jet.lang.resolve.OverridingUtil;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import java.util.List;
import java.util.Set;
@@ -123,13 +124,13 @@ public class OverloadingConflictResolver {
JetType _byte = standardLibrary.getByteType();
JetType _short = standardLibrary.getShortType();
if (semanticServices.getTypeChecker().equalTypes(specific, _double) && semanticServices.getTypeChecker().equalTypes(general, _float)) return true;
if (semanticServices.getTypeChecker().equalTypes(specific, _int)) {
if (semanticServices.getTypeChecker().equalTypes(general, _long)) return true;
if (semanticServices.getTypeChecker().equalTypes(general, _byte)) return true;
if (semanticServices.getTypeChecker().equalTypes(general, _short)) return true;
if (TypeUtils.equalTypes(specific, _double) && TypeUtils.equalTypes(general, _float)) return true;
if (TypeUtils.equalTypes(specific, _int)) {
if (TypeUtils.equalTypes(general, _long)) return true;
if (TypeUtils.equalTypes(general, _byte)) return true;
if (TypeUtils.equalTypes(general, _short)) return true;
}
if (semanticServices.getTypeChecker().equalTypes(specific, _short) && semanticServices.getTypeChecker().equalTypes(general, _byte)) return true;
if (TypeUtils.equalTypes(specific, _short) && TypeUtils.equalTypes(general, _byte)) return true;
return false;
}
@@ -2,7 +2,6 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
@@ -11,7 +10,7 @@ import org.jetbrains.jet.lang.resolve.JetModuleUtil;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor;
import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeChecker;
import org.jetbrains.jet.lang.types.TypeUtils;
import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET;
@@ -29,7 +28,7 @@ public class DataFlowValueFactory {
JetConstantExpression constantExpression = (JetConstantExpression) expression;
if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL;
}
if (JetTypeChecker.INSTANCE.equalTypes(type, JetStandardClasses.getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
if (TypeUtils.equalTypes(type, JetStandardClasses.getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?'
Pair<Object, Boolean> result = getIdForStableIdentifier(expression, bindingContext, false);
return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type));
}
@@ -0,0 +1,262 @@
package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import java.util.*;
import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE;
import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE;
/**
* @author abreslav
*/
public class CommonSupertypes {
@NotNull
public static JetType commonSupertype(@NotNull Collection<JetType> types) {
Collection<JetType> typeSet = new HashSet<JetType>(types);
assert !typeSet.isEmpty();
// If any of the types is nullable, the result must be nullable
// This also removed Nothing and Nothing? because they are subtypes of everything else
boolean nullable = false;
for (Iterator<JetType> iterator = typeSet.iterator(); iterator.hasNext();) {
JetType type = iterator.next();
assert type != null;
if (JetStandardClasses.isNothingOrNullableNothing(type)) {
iterator.remove();
}
nullable |= type.isNullable();
}
// Everything deleted => it's Nothing or Nothing?
if (typeSet.isEmpty()) {
// TODO : attributes
return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
}
if (typeSet.size() == 1) {
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
}
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map<TypeConstructor, Set<JetType>> commonSupertypes = computeCommonRawSupertypes(typeSet);
while (commonSupertypes.size() > 1) {
Set<JetType> merge = new HashSet<JetType>();
for (Set<JetType> supertypes : commonSupertypes.values()) {
merge.addAll(supertypes);
}
commonSupertypes = computeCommonRawSupertypes(merge);
}
assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
// Reconstructing type arguments if possible
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
return TypeUtils.makeNullableIfNeeded(result, nullable);
}
// Raw supertypes are superclasses w/o type arguments
// @return TypeConstructor -> all instantiations of this constructor occurring as supertypes
@NotNull
private static Map<TypeConstructor, Set<JetType>> computeCommonRawSupertypes(@NotNull Collection<JetType> types) {
assert !types.isEmpty();
final Map<TypeConstructor, Set<JetType>> constructorToAllInstances = new HashMap<TypeConstructor, Set<JetType>>();
Set<TypeConstructor> commonSuperclasses = null;
List<TypeConstructor> order = null;
for (JetType type : types) {
Set<TypeConstructor> visited = new HashSet<TypeConstructor>();
order = dfs(type, visited, new DfsNodeHandler<List<TypeConstructor>>() {
public LinkedList<TypeConstructor> list = new LinkedList<TypeConstructor>();
@Override
public void beforeChildren(JetType current) {
TypeConstructor constructor = current.getConstructor();
Set<JetType> instances = constructorToAllInstances.get(constructor);
if (instances == null) {
instances = new HashSet<JetType>();
constructorToAllInstances.put(constructor, instances);
}
instances.add(current);
}
@Override
public void afterChildren(JetType current) {
list.addFirst(current.getConstructor());
}
@Override
public List<TypeConstructor> result() {
return list;
}
});
if (commonSuperclasses == null) {
commonSuperclasses = visited;
}
else {
commonSuperclasses.retainAll(visited);
}
}
assert order != null;
Set<TypeConstructor> notSource = new HashSet<TypeConstructor>();
Map<TypeConstructor, Set<JetType>> result = new HashMap<TypeConstructor, Set<JetType>>();
for (TypeConstructor superConstructor : order) {
if (!commonSuperclasses.contains(superConstructor)) {
continue;
}
if (!notSource.contains(superConstructor)) {
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
markAll(superConstructor, notSource);
}
}
return result;
}
// constructor - type constructor of a supertype to be instantiated
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
@NotNull
private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
// we assume that all the given types are applications of the same type constructor
assert !types.isEmpty();
if (types.size() == 1) {
return types.iterator().next();
}
List<TypeParameterDescriptor> parameters = constructor.getParameters();
List<TypeProjection> newProjections = new ArrayList<TypeProjection>();
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameterDescriptor = parameters.get(i);
Set<TypeProjection> typeProjections = new HashSet<TypeProjection>();
for (JetType type : types) {
typeProjections.add(type.getArguments().get(i));
}
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
}
boolean nullable = false;
for (JetType type : types) {
nullable |= type.isNullable();
}
// TODO : attributes?
return new JetTypeImpl(Collections.<AnnotationDescriptor>emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB); // TODO : scope
}
@NotNull
private static TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set<TypeProjection> typeProjections) {
if (typeProjections.size() == 1) {
return typeProjections.iterator().next();
}
Set<JetType> ins = new HashSet<JetType>();
Set<JetType> outs = new HashSet<JetType>();
Variance variance = parameterDescriptor.getVariance();
switch (variance) {
case INVARIANT:
// Nothing
break;
case IN_VARIANCE:
outs = null;
break;
case OUT_VARIANCE:
ins = null;
break;
}
for (TypeProjection projection : typeProjections) {
Variance projectionKind = projection.getProjectionKind();
if (projectionKind.allowsInPosition()) {
if (ins != null) {
ins.add(projection.getType());
}
} else {
ins = null;
}
if (projectionKind.allowsOutPosition()) {
if (outs != null) {
outs.add(projection.getType());
}
} else {
outs = null;
}
}
if (ins != null) {
JetType intersection = TypeUtils.intersect(JetTypeChecker.INSTANCE, ins);
if (intersection == null) {
if (outs != null) {
return new TypeProjection(OUT_VARIANCE, commonSupertype(outs));
}
return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
}
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
return new TypeProjection(projectionKind, intersection);
} else if (outs != null) {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjection(projectionKind, commonSupertype(outs));
} else {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
}
}
private static void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set<TypeConstructor> markerSet) {
markerSet.add(typeConstructor);
for (JetType type : typeConstructor.getSupertypes()) {
markAll(type.getConstructor(), markerSet);
}
}
private static <R> R dfs(@NotNull JetType current, @NotNull Set<TypeConstructor> visited, @NotNull DfsNodeHandler<R> handler) {
doDfs(current, visited, handler);
return handler.result();
}
private static void doDfs(@NotNull JetType current, @NotNull Set<TypeConstructor> visited, @NotNull DfsNodeHandler<?> handler) {
if (!visited.add(current.getConstructor())) {
return;
}
handler.beforeChildren(current);
// Map<TypeConstructor, TypeProjection> substitutionContext = TypeUtils.buildSubstitutionContext(current);
TypeSubstitutor substitutor = TypeSubstitutor.create(current);
for (JetType supertype : current.getConstructor().getSupertypes()) {
TypeConstructor supertypeConstructor = supertype.getConstructor();
if (visited.contains(supertypeConstructor)) {
continue;
}
JetType substitutedSupertype = substitutor.safeSubstitute(supertype, Variance.INVARIANT);
dfs(substitutedSupertype, visited, handler);
}
handler.afterChildren(current);
}
private static class DfsNodeHandler<R> {
public void beforeChildren(JetType current) {
}
public void afterChildren(JetType current) {
}
public R result() {
return null;
}
}
}
@@ -2,7 +2,6 @@ package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import java.util.*;
@@ -15,316 +14,14 @@ import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE;
*/
public class JetTypeChecker {
public static final JetTypeChecker INSTANCE = new JetTypeChecker(null);
public static final JetTypeChecker INSTANCE = new JetTypeChecker();
private final Map<TypeConstructor, Set<TypeConstructor>> conversionMap = new HashMap<TypeConstructor, Set<TypeConstructor>>();
private final JetStandardLibrary standardLibrary;
public JetTypeChecker(JetStandardLibrary standardLibrary) {
this.standardLibrary = standardLibrary;
private JetTypeChecker() {
}
@NotNull
private Map<TypeConstructor, Set<TypeConstructor>> getConversionMap() {
// if (conversionMap.size() == 0) {
// addConversion(standardLibrary.getByte(),
// standardLibrary.getShort(),
// standardLibrary.getInt(),
// standardLibrary.getLong(),
// standardLibrary.getFloat(),
// standardLibrary.getDouble());
//
// addConversion(standardLibrary.getShort(),
// standardLibrary.getInt(),
// standardLibrary.getLong(),
// standardLibrary.getFloat(),
// standardLibrary.getDouble());
//
// addConversion(standardLibrary.getChar(),
// standardLibrary.getInt(),
// standardLibrary.getLong(),
// standardLibrary.getFloat(),
// standardLibrary.getDouble());
//
// addConversion(standardLibrary.getInt(),
// standardLibrary.getLong(),
// standardLibrary.getFloat(),
// standardLibrary.getDouble());
//
// addConversion(standardLibrary.getLong(),
// standardLibrary.getFloat(),
// standardLibrary.getDouble());
//
// addConversion(standardLibrary.getFloat(),
// standardLibrary.getDouble());
// }
return conversionMap;
}
// private void addConversion(ClassDescriptor actual, ClassDescriptor... convertedTo) {
// TypeConstructor[] constructors = new TypeConstructor[convertedTo.length];
// for (int i = 0, convertedToLength = convertedTo.length; i < convertedToLength; i++) {
// ClassDescriptor classDescriptor = convertedTo[i];
// constructors[i] = classDescriptor.getTypeConstructor();
// }
// conversionMap.put(actual.getTypeConstructor(), new HashSet<TypeConstructor>(Arrays.asList(constructors)));
// }
//
@NotNull
public JetType commonSupertype(@NotNull JetType... types) {
return commonSupertype(Arrays.asList(types));
}
@NotNull
public JetType commonSupertype(@NotNull Collection<JetType> types) {
Collection<JetType> typeSet = new HashSet<JetType>(types);
assert !typeSet.isEmpty();
// If any of the types is nullable, the result must be nullable
// This also removed Nothing and Nothing? because they are subtypes of everything else
boolean nullable = false;
for (Iterator<JetType> iterator = typeSet.iterator(); iterator.hasNext();) {
JetType type = iterator.next();
assert type != null;
if (JetStandardClasses.isNothingOrNullableNothing(type)) {
iterator.remove();
}
nullable |= type.isNullable();
}
// Everything deleted => it's Nothing or Nothing?
if (typeSet.isEmpty()) {
// TODO : attributes
return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
}
if (typeSet.size() == 1) {
return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable);
}
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map<TypeConstructor, Set<JetType>> commonSupertypes = computeCommonRawSupertypes(typeSet);
while (commonSupertypes.size() > 1) {
Set<JetType> merge = new HashSet<JetType>();
for (Set<JetType> supertypes : commonSupertypes.values()) {
merge.addAll(supertypes);
}
commonSupertypes = computeCommonRawSupertypes(merge);
}
assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types;
// constructor of the supertype -> all of its instantiations occurring as supertypes
Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
// Reconstructing type arguments if possible
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
return TypeUtils.makeNullableIfNeeded(result, nullable);
}
// Raw supertypes are superclasses w/o type arguments
// @return TypeConstructor -> all instantiations of this constructor occurring as supertypes
@NotNull
private Map<TypeConstructor, Set<JetType>> computeCommonRawSupertypes(@NotNull Collection<JetType> types) {
assert !types.isEmpty();
final Map<TypeConstructor, Set<JetType>> constructorToAllInstances = new HashMap<TypeConstructor, Set<JetType>>();
Set<TypeConstructor> commonSuperclasses = null;
List<TypeConstructor> order = null;
for (JetType type : types) {
Set<TypeConstructor> visited = new HashSet<TypeConstructor>();
order = dfs(type, visited, new DfsNodeHandler<List<TypeConstructor>>() {
public LinkedList<TypeConstructor> list = new LinkedList<TypeConstructor>();
@Override
public void beforeChildren(JetType current) {
TypeConstructor constructor = current.getConstructor();
Set<JetType> instances = constructorToAllInstances.get(constructor);
if (instances == null) {
instances = new HashSet<JetType>();
constructorToAllInstances.put(constructor, instances);
}
instances.add(current);
}
@Override
public void afterChildren(JetType current) {
list.addFirst(current.getConstructor());
}
@Override
public List<TypeConstructor> result() {
return list;
}
});
if (commonSuperclasses == null) {
commonSuperclasses = visited;
}
else {
commonSuperclasses.retainAll(visited);
}
}
assert order != null;
Set<TypeConstructor> notSource = new HashSet<TypeConstructor>();
Map<TypeConstructor, Set<JetType>> result = new HashMap<TypeConstructor, Set<JetType>>();
for (TypeConstructor superConstructor : order) {
if (!commonSuperclasses.contains(superConstructor)) {
continue;
}
if (!notSource.contains(superConstructor)) {
result.put(superConstructor, constructorToAllInstances.get(superConstructor));
markAll(superConstructor, notSource);
}
}
return result;
}
// constructor - type constructor of a supertype to be instantiated
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
@NotNull
private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
// we assume that all the given types are applications of the same type constructor
assert !types.isEmpty();
if (types.size() == 1) {
return types.iterator().next();
}
List<TypeParameterDescriptor> parameters = constructor.getParameters();
List<TypeProjection> newProjections = new ArrayList<TypeProjection>();
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameterDescriptor = parameters.get(i);
Set<TypeProjection> typeProjections = new HashSet<TypeProjection>();
for (JetType type : types) {
typeProjections.add(type.getArguments().get(i));
}
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
}
boolean nullable = false;
for (JetType type : types) {
nullable |= type.isNullable();
}
// TODO : attributes?
return new JetTypeImpl(Collections.<AnnotationDescriptor>emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB); // TODO : scope
}
@NotNull
private TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set<TypeProjection> typeProjections) {
if (typeProjections.size() == 1) {
return typeProjections.iterator().next();
}
Set<JetType> ins = new HashSet<JetType>();
Set<JetType> outs = new HashSet<JetType>();
Variance variance = parameterDescriptor.getVariance();
switch (variance) {
case INVARIANT:
// Nothing
break;
case IN_VARIANCE:
outs = null;
break;
case OUT_VARIANCE:
ins = null;
break;
}
for (TypeProjection projection : typeProjections) {
Variance projectionKind = projection.getProjectionKind();
if (projectionKind.allowsInPosition()) {
if (ins != null) {
ins.add(projection.getType());
}
} else {
ins = null;
}
if (projectionKind.allowsOutPosition()) {
if (outs != null) {
outs.add(projection.getType());
}
} else {
outs = null;
}
}
if (ins != null) {
JetType intersection = TypeUtils.intersect(this, ins);
if (intersection == null) {
if (outs != null) {
return new TypeProjection(OUT_VARIANCE, commonSupertype(outs));
}
return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
}
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
return new TypeProjection(projectionKind, intersection);
} else if (outs != null) {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjection(projectionKind, commonSupertype(outs));
} else {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
}
}
private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set<TypeConstructor> markerSet) {
markerSet.add(typeConstructor);
for (JetType type : typeConstructor.getSupertypes()) {
markAll(type.getConstructor(), markerSet);
}
}
private <R> R dfs(@NotNull JetType current, @NotNull Set<TypeConstructor> visited, @NotNull DfsNodeHandler<R> handler) {
doDfs(current, visited, handler);
return handler.result();
}
private void doDfs(@NotNull JetType current, @NotNull Set<TypeConstructor> visited, @NotNull DfsNodeHandler<?> handler) {
if (!visited.add(current.getConstructor())) {
return;
}
handler.beforeChildren(current);
// Map<TypeConstructor, TypeProjection> substitutionContext = TypeUtils.buildSubstitutionContext(current);
TypeSubstitutor substitutor = TypeSubstitutor.create(current);
for (JetType supertype : current.getConstructor().getSupertypes()) {
TypeConstructor supertypeConstructor = supertype.getConstructor();
if (visited.contains(supertypeConstructor)) {
continue;
}
JetType substitutedSupertype = substitutor.safeSubstitute(supertype, Variance.INVARIANT);
dfs(substitutedSupertype, visited, handler);
}
handler.afterChildren(current);
}
public boolean isConvertibleTo(@NotNull JetType actual, @NotNull JetType expected) {
return isSubtypeOf(actual, expected) ||
isConvertibleBySpecialConversion(actual, expected);
}
public boolean isConvertibleBySpecialConversion(@NotNull JetType actual, @NotNull JetType expected) {
if (expected.getConstructor().equals(JetStandardClasses.getTuple(0).getTypeConstructor())) {
return true;
}
// if (actual.getValueArguments().isEmpty()) {
// TypeConstructor actualConstructor = actual.getConstructor();
// TypeConstructor constructor = expected.getConstructor();
// Set<TypeConstructor> convertibleTo = getConversionMap().get(actualConstructor);
// if (convertibleTo != null) {
// return convertibleTo.contains(constructor);
// }
// }
return false;
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
// return new TypeCheckingProcedure().run(subtype, supertype);
return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype);
}
// This method returns the supertype of the first parameter that has the same constructor
@@ -344,13 +41,14 @@ public class JetTypeChecker {
return null;
}
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
// return new TypeCheckingProcedure().run(subtype, supertype);
return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype);
private static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) {
boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType();
}
public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
return isSubtypeOf(a, b) && isSubtypeOf(b, a);
private static JetType getInType(TypeParameterDescriptor parameter, TypeProjection argument) {
boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
return isOutProjected ? JetStandardClasses.getNothingType() : argument.getType();
}
private static class ExplicitInOutTypeCheckingProcedure {
@@ -386,13 +84,12 @@ public class JetTypeChecker {
List<TypeParameterDescriptor> parameters = constructor.getParameters();
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameter = parameters.get(i);
TypeProjection subArgument = subArguments.get(i);
JetType subIn = getInType(parameter, subArgument);
JetType subOut = getOutType(parameter, subArgument);
TypeProjection superArgument = superArguments.get(i);
JetType superIn = getInType(parameter, superArgument);
JetType superOut = getOutType(parameter, superArgument);
@@ -402,16 +99,6 @@ public class JetTypeChecker {
}
return true;
}
private JetType getOutType(TypeParameterDescriptor parameter, TypeProjection subArgument) {
boolean isOutProjected = subArgument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
return isOutProjected ? parameter.getBoundsAsType() : subArgument.getType();
}
private JetType getInType(TypeParameterDescriptor parameter, TypeProjection subArgument) {
boolean isOutProjected = subArgument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
return isOutProjected ? JetStandardClasses.getNothingType() : subArgument.getType();
}
}
public static abstract class AbstractTypeCheckingProcedure<T> {
@@ -590,19 +277,5 @@ public class JetTypeChecker {
}
}
private static class DfsNodeHandler<R> {
public void beforeChildren(JetType current) {
}
public void afterChildren(JetType current) {
}
public R result() {
return null;
}
}
}
@@ -216,7 +216,7 @@ public class TypeSubstitutor {
case OUT_VARIANCE:
if (projectionKindValue == Variance.IN_VARIANCE) {
effectiveProjectionKindValue = Variance.INVARIANT;
effectiveTypeValue = correspondingTypeParameter.getBoundsAsType();
effectiveTypeValue = correspondingTypeParameter.getUpperBoundsAsType();
}
else {
effectiveTypeValue = typeValue;
@@ -352,7 +352,7 @@ public class TypeUtils {
@NotNull
public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) {
return new TypeProjection(Variance.OUT_VARIANCE, parameterDescriptor.getBoundsAsType());
return new TypeProjection(Variance.OUT_VARIANCE, parameterDescriptor.getUpperBoundsAsType());
}
private static void collectImmediateSupertypes(@NotNull JetType type, @NotNull Collection<JetType> result) {
@@ -424,4 +424,7 @@ public class TypeUtils {
return false;
}
public static boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
return JetTypeChecker.INSTANCE.isSubtypeOf(a, b) && JetTypeChecker.INSTANCE.isSubtypeOf(b, a);
}
}
@@ -712,7 +712,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
if (rightType != null) {
DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
return TypeUtils.makeNullableAsSpecified(context.semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable());
return TypeUtils.makeNullableAsSpecified(CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable());
}
}
}
@@ -14,15 +14,9 @@ import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
@@ -98,7 +92,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
result = thenType;
}
else {
result = context.semanticServices.getTypeChecker().commonSupertype(Arrays.asList(thenType, elseType));
result = CommonSupertypes.commonSupertype(Arrays.asList(thenType, elseType));
}
boolean jumpInThen = thenType != null && JetStandardClasses.isNothing(thenType);
@@ -361,7 +355,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
return null;
}
else {
return context.semanticServices.getTypeChecker().commonSupertype(types);
return CommonSupertypes.commonSupertype(types);
}
}
@@ -86,7 +86,7 @@ public class ExpressionTypingServices {
Collection<JetType> types = typeMap.values();
return types.isEmpty()
? JetStandardClasses.getNothingType()
: semanticServices.getTypeChecker().commonSupertype(types);
: CommonSupertypes.commonSupertype(types);
}
public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType) {
@@ -13,7 +13,6 @@ import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.types.JetStandardClasses;
@@ -47,7 +46,7 @@ public class ExpressionTypingUtils {
}
public static boolean isBoolean(@NotNull JetSemanticServices semanticServices, @NotNull JetType type) {
return semanticServices.getTypeChecker().isConvertibleTo(type, semanticServices.getStandardLibrary().getBooleanType());
return semanticServices.getTypeChecker().isSubtypeOf(type, semanticServices.getStandardLibrary().getBooleanType());
}
public static boolean ensureBooleanResult(JetExpression operationSign, String name, JetType resultType, ExpressionTypingContext context) {
@@ -100,7 +100,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
}
if (!expressionTypes.isEmpty()) {
return context.semanticServices.getTypeChecker().commonSupertype(expressionTypes);
return CommonSupertypes.commonSupertype(expressionTypes);
}
else if (expression.getEntries().isEmpty()) {
// context.trace.getErrorHandler().genericError(expression.getNode(), "Entries required for when-expression");
@@ -1,413 +1,18 @@
package org.jetbrains.jet.lang.types.inference;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.*;
import java.util.Map;
import java.util.Set;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.Variance;
/**
* @author abreslav
*/
public class ConstraintSystem {
public interface ConstraintSystem {
void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance);
// private static final Supplier<Set<TypeValue>> SET_SUPPLIER = new Supplier<Set<TypeValue>>() {
// @Override
// public Set<TypeValue> get() {
// return Sets.newHashSet();
// }
// };
private static class LoopInTypeVariableConstraintsException extends RuntimeException {
private LoopInTypeVariableConstraintsException() {
}
private LoopInTypeVariableConstraintsException(String message) {
super(message);
}
private LoopInTypeVariableConstraintsException(String message, Throwable cause) {
super(message, cause);
}
private LoopInTypeVariableConstraintsException(Throwable cause) {
super(cause);
}
}
public static abstract class TypeValue {
private final Set<TypeValue> upperBounds = Sets.newHashSet();
private final Set<TypeValue> lowerBounds = Sets.newHashSet();
@NotNull
public Set<TypeValue> getUpperBounds() {
return upperBounds;
}
@NotNull
public Set<TypeValue> getLowerBounds() {
return lowerBounds;
}
@Nullable
public abstract KnownType getValue();
}
private static class UnknownType extends TypeValue {
private final TypeParameterDescriptor typeParameterDescriptor;
private final Variance positionVariance;
private KnownType value;
private boolean beingComputed = false;
private UnknownType(TypeParameterDescriptor typeParameterDescriptor, Variance positionVariance) {
this.typeParameterDescriptor = typeParameterDescriptor;
this.positionVariance = positionVariance;
}
@NotNull
public TypeParameterDescriptor getTypeParameterDescriptor() {
return typeParameterDescriptor;
}
@Override
public KnownType getValue() {
if (beingComputed) {
throw new LoopInTypeVariableConstraintsException();
}
if (value == null) {
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
beingComputed = true;
try {
if (positionVariance == Variance.IN_VARIANCE) {
// maximal solution
throw new UnsupportedOperationException();
}
else {
// minimal solution
Set<TypeValue> lowerBounds = getLowerBounds();
if (!lowerBounds.isEmpty()) {
Set<JetType> types = getTypes(lowerBounds);
JetType commonSupertype = typeChecker.commonSupertype(types);
for (TypeValue upperBound : getUpperBounds()) {
if (!typeChecker.isSubtypeOf(commonSupertype, upperBound.getValue().getType())) {
value = null;
}
}
println("minimal solution from lowerbounds for " + this + " is " + commonSupertype);
value = new KnownType(commonSupertype);
}
else {
Set<TypeValue> upperBounds = getUpperBounds();
Set<JetType> types = getTypes(upperBounds);
JetType intersect = TypeUtils.intersect(typeChecker, types);
value = new KnownType(intersect);
}
}
}
finally {
beingComputed = false;
}
}
return value;
}
private Set<JetType> getTypes(Set<TypeValue> lowerBounds) {
Set<JetType> types = Sets.newHashSet();
for (TypeValue lowerBound : lowerBounds) {
types.add(lowerBound.getValue().getType());
}
return types;
}
@Override
public String toString() {
return "?" + typeParameterDescriptor;
}
}
private static class KnownType extends TypeValue {
private final JetType type;
public KnownType(@NotNull JetType type) {
this.type = type;
}
@NotNull
public JetType getType() {
return type;
}
@Override
public KnownType getValue() {
return this;
}
@Override
public String toString() {
return type.toString();
}
}
private final Map<JetType, KnownType> knownTypes = Maps.newHashMap();
private final Map<TypeParameterDescriptor, UnknownType> unknownTypes = Maps.newHashMap();
private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper);
@NotNull
private TypeValue getTypeValueFor(@NotNull JetType type) {
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof TypeParameterDescriptor) {
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
UnknownType unknownType = unknownTypes.get(typeParameterDescriptor);
if (unknownType != null) {
return unknownType;
}
}
KnownType typeValue = knownTypes.get(type);
if (typeValue == null) {
typeValue = new KnownType(type);
knownTypes.put(type, typeValue);
}
return typeValue;
}
public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) {
assert !unknownTypes.containsKey(typeParameterDescriptor);
UnknownType typeValue = new UnknownType(typeParameterDescriptor, positionVariance);
unknownTypes.put(typeParameterDescriptor, typeValue);
}
@NotNull
private UnknownType getTypeVariable(TypeParameterDescriptor typeParameterDescriptor) {
UnknownType unknownType = unknownTypes.get(typeParameterDescriptor);
if (unknownType == null) {
throw new IllegalArgumentException("This type parameter is not an unknown in this constraint system: " + typeParameterDescriptor);
}
return unknownType;
}
public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) {
TypeValue typeValueForLower = getTypeValueFor(lower);
TypeValue typeValueForUpper = getTypeValueFor(upper);
addSubtypingConstraintOnTypeValues(typeValueForLower, typeValueForUpper);
}
private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) {
println(typeValueForLower + " :< " + typeValueForUpper);
typeValueForLower.getUpperBounds().add(typeValueForUpper);
typeValueForUpper.getLowerBounds().add(typeValueForLower);
}
@NotNull
public Solution solve() {
// Expand custom bounds, e.g. List<T> <: List<Int>
for (Map.Entry<JetType, KnownType> entry : Sets.newHashSet(knownTypes.entrySet())) {
JetType jetType = entry.getKey();
KnownType typeValue = entry.getValue();
for (TypeValue upperBound : typeValue.getUpperBounds()) {
if (upperBound instanceof KnownType) {
KnownType knownBoundType = (KnownType) upperBound;
boolean ok = new TypeConstraintExpander().run(jetType, knownBoundType.getType());
if (!ok) {
return new Solution(true);
}
}
}
// Lower bounds?
}
// Fill in upper bounds from type parameter bounds
for (Map.Entry<TypeParameterDescriptor, UnknownType> entry : Sets.newHashSet(unknownTypes.entrySet())) {
TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
UnknownType typeValue = entry.getValue();
for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
addSubtypingConstraintOnTypeValues(typeValue, getTypeValueFor(upperBound));
}
}
// effective bounds for each node
Set<TypeValue> visited = Sets.newHashSet();
for (KnownType knownType : knownTypes.values()) {
transitiveClosure(knownType, visited);
}
for (UnknownType unknownType : unknownTypes.values()) {
transitiveClosure(unknownType, visited);
}
// Find inconsistencies
Solution solution = new Solution(false);
for (UnknownType unknownType : unknownTypes.values()) {
check(unknownType, solution);
}
for (KnownType knownType : knownTypes.values()) {
check(knownType, solution);
}
return solution;
}
private void check(TypeValue typeValue, Solution solution) {
try {
KnownType resultingValue = typeValue.getValue();
JetType type = solution.getSubstitutor().substitute(resultingValue.getType(), Variance.INVARIANT); // TODO
for (TypeValue upperBound : typeValue.getUpperBounds()) {
JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT);
if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO
solution.registerError();
println("Constraint violation: " + type + " :< " + boundingType);
}
}
for (TypeValue lowerBound : typeValue.getLowerBounds()) {
JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT);
if (!typeChecker.isSubtypeOf(boundingType, type)) {
solution.registerError();
println("Constraint violation: " + boundingType + " :< " + type);
}
}
}
catch (LoopInTypeVariableConstraintsException e) {
solution.registerError();
e.printStackTrace();
}
}
private void transitiveClosure(TypeValue current, Set<TypeValue> visited) {
if (!visited.add(current)) {
return;
}
for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) {
transitiveClosure(upperBound, visited);
Set<TypeValue> upperBounds = upperBound.getUpperBounds();
for (TypeValue transitiveBound : upperBounds) {
addSubtypingConstraintOnTypeValues(current, transitiveBound);
}
}
}
public class Solution {
private final TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() {
@Override
public TypeProjection get(TypeConstructor key) {
DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor();
if (declarationDescriptor instanceof TypeParameterDescriptor) {
TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor;
println(descriptor + " |-> " + getValue(descriptor));
return new TypeProjection(getValue(descriptor));
}
return null;
}
@Override
public boolean isEmpty() {
return false;
}
});
private boolean failed;
public Solution(boolean failed) {
this.failed = failed;
}
public void registerError() {
failed = true;
}
public boolean isSuccessful() {
return !failed;
}
@Nullable
public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) {
KnownType value = getTypeVariable(typeParameterDescriptor).getValue();
return value == null ? null : value.getType();
}
public TypeSubstitutor getSubstitutor() {
return typeSubstitutor;
}
}
private class TypeConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure<Boolean> {
private boolean error = false;
private StatusAction fail() {
error = true;
return StatusAction.ABORT_ALL;
}
@Override
protected StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return tryToAddConstraint(subtype, supertype);
}
private StatusAction tryToAddConstraint(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeValue subtypeValue = getTypeValueFor(subtype);
TypeValue supertypeValue = getTypeValueFor(supertype);
if (someUnknown(subtypeValue, supertypeValue)) {
addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue);
}
return StatusAction.PROCEED;
}
private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) {
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
}
@Override
protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
if (someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype))) {
return StatusAction.PROCEED;
}
return fail();
}
@Override
protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) {
if (!subArgumentType.equals(superArgumentType)) {
return fail();
}
return StatusAction.PROCEED;
}
@Override
protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) {
return fail();
}
@Override
protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return StatusAction.PROCEED;
}
@Override
protected Boolean result() {
return !error;
}
}
private static void println(String message) {
// System.out.println(message);
}
}
ConstraintSystemSolution solve();
}
@@ -0,0 +1,18 @@
package org.jetbrains.jet.lang.types.inference;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeSubstitutor;
/**
* @author abreslav
*/
public interface ConstraintSystemSolution {
boolean isSuccessful();
TypeSubstitutor getSubstitutor();
@Nullable
JetType getValue(TypeParameterDescriptor typeParameterDescriptor);
}
@@ -0,0 +1,55 @@
package org.jetbrains.jet.lang.types.inference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.Variance;
import java.util.List;
import java.util.Map;
/**
* @author abreslav
*/
public class EqualityBasedConstraintSystem implements ConstraintSystem {
private abstract class Constraint {
private final TypeParameterDescriptor typeParameterDescriptor;
protected Constraint(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
this.typeParameterDescriptor = typeParameterDescriptor;
}
@NotNull
public TypeParameterDescriptor getTypeParameterDescriptor() {
return typeParameterDescriptor;
}
}
private class Unknown {
// T -> variance of its position
private final Map<TypeParameterDescriptor, Variance> typeParameters = Maps.newHashMap();
private final List<Constraint> constraints = Lists.newArrayList();
}
private final Map<TypeParameterDescriptor, Unknown> unknowns = Maps.newHashMap();
@Override
public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) {
throw new UnsupportedOperationException(); // TODO
}
@NotNull
@Override
public ConstraintSystemSolution solve() {
throw new UnsupportedOperationException(); // TODO
}
}
@@ -0,0 +1,415 @@
package org.jetbrains.jet.lang.types.inference;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.*;
import java.util.Map;
import java.util.Set;
/**
* @author abreslav
*/
public class SubtypingOnlyConstraintSystem implements ConstraintSystem {
private static class LoopInTypeVariableConstraintsException extends RuntimeException {
private LoopInTypeVariableConstraintsException() {
}
private LoopInTypeVariableConstraintsException(String message) {
super(message);
}
private LoopInTypeVariableConstraintsException(String message, Throwable cause) {
super(message, cause);
}
private LoopInTypeVariableConstraintsException(Throwable cause) {
super(cause);
}
}
public static abstract class TypeValue {
private final Set<TypeValue> upperBounds = Sets.newHashSet();
private final Set<TypeValue> lowerBounds = Sets.newHashSet();
@NotNull
public Set<TypeValue> getUpperBounds() {
return upperBounds;
}
@NotNull
public Set<TypeValue> getLowerBounds() {
return lowerBounds;
}
@Nullable
public abstract KnownType getValue();
}
private static class UnknownType extends TypeValue {
private final TypeParameterDescriptor typeParameterDescriptor;
private final Variance positionVariance;
private KnownType value;
private boolean beingComputed = false;
private UnknownType(TypeParameterDescriptor typeParameterDescriptor, Variance positionVariance) {
this.typeParameterDescriptor = typeParameterDescriptor;
this.positionVariance = positionVariance;
}
@NotNull
public TypeParameterDescriptor getTypeParameterDescriptor() {
return typeParameterDescriptor;
}
@Override
public KnownType getValue() {
if (beingComputed) {
throw new LoopInTypeVariableConstraintsException();
}
if (value == null) {
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
beingComputed = true;
try {
if (positionVariance == Variance.IN_VARIANCE) {
// maximal solution
throw new UnsupportedOperationException();
}
else {
// minimal solution
Set<TypeValue> lowerBounds = getLowerBounds();
if (!lowerBounds.isEmpty()) {
Set<JetType> types = getTypes(lowerBounds);
JetType commonSupertype = CommonSupertypes.commonSupertype(types);
for (TypeValue upperBound : getUpperBounds()) {
if (!typeChecker.isSubtypeOf(commonSupertype, upperBound.getValue().getType())) {
value = null;
}
}
println("minimal solution from lowerbounds for " + this + " is " + commonSupertype);
value = new KnownType(commonSupertype);
}
else {
Set<TypeValue> upperBounds = getUpperBounds();
Set<JetType> types = getTypes(upperBounds);
JetType intersect = TypeUtils.intersect(typeChecker, types);
value = new KnownType(intersect);
}
}
}
finally {
beingComputed = false;
}
}
return value;
}
private Set<JetType> getTypes(Set<TypeValue> lowerBounds) {
Set<JetType> types = Sets.newHashSet();
for (TypeValue lowerBound : lowerBounds) {
types.add(lowerBound.getValue().getType());
}
return types;
}
@Override
public String toString() {
return "?" + typeParameterDescriptor;
}
}
private static class KnownType extends TypeValue {
private final JetType type;
public KnownType(@NotNull JetType type) {
this.type = type;
}
@NotNull
public JetType getType() {
return type;
}
@Override
public KnownType getValue() {
return this;
}
@Override
public String toString() {
return type.toString();
}
}
private final Map<JetType, KnownType> knownTypes = Maps.newHashMap();
private final Map<TypeParameterDescriptor, UnknownType> unknownTypes = Maps.newHashMap();
private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
@NotNull
private TypeValue getTypeValueFor(@NotNull JetType type) {
DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof TypeParameterDescriptor) {
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
UnknownType unknownType = unknownTypes.get(typeParameterDescriptor);
if (unknownType != null) {
return unknownType;
}
}
KnownType typeValue = knownTypes.get(type);
if (typeValue == null) {
typeValue = new KnownType(type);
knownTypes.put(type, typeValue);
}
return typeValue;
}
@Override
public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) {
assert !unknownTypes.containsKey(typeParameterDescriptor);
UnknownType typeValue = new UnknownType(typeParameterDescriptor, positionVariance);
unknownTypes.put(typeParameterDescriptor, typeValue);
}
@NotNull
private UnknownType getTypeVariable(TypeParameterDescriptor typeParameterDescriptor) {
UnknownType unknownType = unknownTypes.get(typeParameterDescriptor);
if (unknownType == null) {
throw new IllegalArgumentException("This type parameter is not an unknown in this constraint system: " + typeParameterDescriptor);
}
return unknownType;
}
@Override
public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) {
TypeValue typeValueForLower = getTypeValueFor(lower);
TypeValue typeValueForUpper = getTypeValueFor(upper);
addSubtypingConstraintOnTypeValues(typeValueForLower, typeValueForUpper);
}
private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) {
println(typeValueForLower + " :< " + typeValueForUpper);
typeValueForLower.getUpperBounds().add(typeValueForUpper);
typeValueForUpper.getLowerBounds().add(typeValueForLower);
}
@Override
@NotNull
public ConstraintSystemSolution solve() {
// Expand custom bounds, e.g. List<T> <: List<Int>
for (Map.Entry<JetType, KnownType> entry : Sets.newHashSet(knownTypes.entrySet())) {
JetType jetType = entry.getKey();
KnownType typeValue = entry.getValue();
for (TypeValue upperBound : typeValue.getUpperBounds()) {
if (upperBound instanceof KnownType) {
KnownType knownBoundType = (KnownType) upperBound;
boolean ok = new SubtypingConstraintExpander().run(jetType, knownBoundType.getType());
if (!ok) {
return new Solution(true);
}
}
}
// Lower bounds?
}
// Fill in upper bounds from type parameter bounds
for (Map.Entry<TypeParameterDescriptor, UnknownType> entry : Sets.newHashSet(unknownTypes.entrySet())) {
TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
UnknownType typeValue = entry.getValue();
for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
addSubtypingConstraintOnTypeValues(typeValue, getTypeValueFor(upperBound));
}
}
// effective bounds for each node
Set<TypeValue> visited = Sets.newHashSet();
for (KnownType knownType : knownTypes.values()) {
transitiveClosure(knownType, visited);
}
for (UnknownType unknownType : unknownTypes.values()) {
transitiveClosure(unknownType, visited);
}
// Find inconsistencies
Solution solution = new Solution(false);
for (UnknownType unknownType : unknownTypes.values()) {
check(unknownType, solution);
}
for (KnownType knownType : knownTypes.values()) {
check(knownType, solution);
}
return solution;
}
private void check(TypeValue typeValue, Solution solution) {
try {
KnownType resultingValue = typeValue.getValue();
JetType type = solution.getSubstitutor().substitute(resultingValue.getType(), Variance.INVARIANT); // TODO
for (TypeValue upperBound : typeValue.getUpperBounds()) {
JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT);
if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO
solution.registerError();
println("Constraint violation: " + type + " :< " + boundingType);
}
}
for (TypeValue lowerBound : typeValue.getLowerBounds()) {
JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT);
if (!typeChecker.isSubtypeOf(boundingType, type)) {
solution.registerError();
println("Constraint violation: " + boundingType + " :< " + type);
}
}
}
catch (LoopInTypeVariableConstraintsException e) {
solution.registerError();
e.printStackTrace();
}
}
private void transitiveClosure(TypeValue current, Set<TypeValue> visited) {
if (!visited.add(current)) {
return;
}
for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) {
transitiveClosure(upperBound, visited);
Set<TypeValue> upperBounds = upperBound.getUpperBounds();
for (TypeValue transitiveBound : upperBounds) {
addSubtypingConstraintOnTypeValues(current, transitiveBound);
}
}
}
public class Solution implements ConstraintSystemSolution {
private final TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() {
@Override
public TypeProjection get(TypeConstructor key) {
DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor();
if (declarationDescriptor instanceof TypeParameterDescriptor) {
TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor;
println(descriptor + " |-> " + getValue(descriptor));
return new TypeProjection(getValue(descriptor));
}
return null;
}
@Override
public boolean isEmpty() {
return false;
}
});
private boolean failed;
public Solution(boolean failed) {
this.failed = failed;
}
private void registerError() {
failed = true;
}
@Override
public boolean isSuccessful() {
return !failed;
}
@Override
public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) {
KnownType value = getTypeVariable(typeParameterDescriptor).getValue();
return value == null ? null : value.getType();
}
@Override
public TypeSubstitutor getSubstitutor() {
return typeSubstitutor;
}
}
// private class EqualityConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure<Boolean> {
//
// }
private class SubtypingConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure<Boolean> {
private boolean error = false;
private StatusAction fail() {
error = true;
return StatusAction.ABORT_ALL;
}
@Override
protected StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return tryToAddConstraint(subtype, supertype);
}
private StatusAction tryToAddConstraint(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeValue subtypeValue = getTypeValueFor(subtype);
TypeValue supertypeValue = getTypeValueFor(supertype);
if (someUnknown(subtypeValue, supertypeValue)) {
addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue);
}
return StatusAction.PROCEED;
}
private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) {
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
}
@Override
protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
if (someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype))) {
return StatusAction.PROCEED;
}
return fail();
}
@Override
protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) {
if (!subArgumentType.equals(superArgumentType)) {
return fail();
}
return StatusAction.PROCEED;
}
@Override
protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) {
return fail();
}
@Override
protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) {
return StatusAction.PROCEED;
}
@Override
protected Boolean result() {
return !error;
}
}
private static void println(String message) {
// System.out.println(message);
}
}
@@ -471,7 +471,7 @@ public class JetTypeCheckerTest extends JetLiteFixture {
for (String type : types) {
subtypes.add(makeType(type));
}
JetType result = semanticServices.getTypeChecker().commonSupertype(subtypes);
JetType result = CommonSupertypes.commonSupertype(subtypes);
assertTrue(result + " != " + expected, result.equals(makeType(expected)));
}