blob: a9d1bb3c3b15a0761d4d130a22933615086f403c [file] [log] [blame]
package org.checkerframework.framework.util.typeinference.constraint;
import java.util.List;
import java.util.Set;
import javax.lang.model.type.TypeKind;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNullType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedUnionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.framework.type.visitor.AbstractAtmComboVisitor;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.framework.util.typeinference.TypeArgInferenceUtil;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.StringsPlume;
/**
* Takes a single step in reducing a AFConstraint.
*
* <p>The visit method will determine if the given constraint should either:
*
* <ul>
* <li>be discarded - in this case, the visitor just returns
* <li>reduced to a simpler constraint or set of constraints - in this case, the new constraint or
* set of constraints is added to newConstraints
* </ul>
*
* Sprinkled throughout this class are comments of the form:
*
* <pre>{@code
* // If F has the form G<..., Yk-1, ? super U, Yk+1, ...>, where U involves Tj
* }</pre>
*
* These are excerpts from the JLS, if you search for them you will find the corresponding JLS
* description of the case being covered.
*/
abstract class AFReducingVisitor extends AbstractAtmComboVisitor<Void, Set<AFConstraint>> {
public final Class<? extends AFConstraint> reducerType;
public final AnnotatedTypeFactory typeFactory;
protected AFReducingVisitor(
final Class<? extends AFConstraint> reducerType, final AnnotatedTypeFactory typeFactory) {
this.reducerType = reducerType;
this.typeFactory = typeFactory;
}
public abstract AFConstraint makeConstraint(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype);
public abstract AFConstraint makeInverseConstraint(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype);
public abstract AFConstraint makeEqualityConstraint(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype);
public void addConstraint(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype, Set<AFConstraint> constraints) {
constraints.add(makeConstraint(subtype, supertype));
}
public void addInverseConstraint(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype, Set<AFConstraint> constraints) {
constraints.add(makeInverseConstraint(subtype, supertype));
}
public void addEqualityConstraint(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype, Set<AFConstraint> constraints) {
constraints.add(makeEqualityConstraint(subtype, supertype));
}
/**
* Called when we encounter an AF constraint on a type combination that we did not think is
* possible. This either implies that the type combination is possible, we accidentally created an
* invalid A2F or F2A Constraint, or we called the visit method on two AnnotatedTypeMirrors that
* do not appear together in a constraint.
*/
@Override
protected String defaultErrorMessage(
AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype, Set<AFConstraint> constraints) {
return StringsPlume.joinLines(
"Unexpected " + reducerType.getSimpleName() + " + Combination:",
"subtype=" + subtype,
"supertype=" + supertype,
"constraints=[",
StringsPlume.join(", ", constraints),
"]");
}
// ------------------------------------------------------------------------
// Arrays as arguments
// From the JLS:
// If F = U[], where the type U involves Tj, then if A is an array type V[], or a type
// variable with an upper bound that is an array type V[], where V is a reference type, this
// algorithm is applied recursively to the constraint V << U or U << V (depending on the
// constraint type).
@Override
public Void visitArray_Array(
AnnotatedArrayType subtype, AnnotatedArrayType supertype, Set<AFConstraint> constraints) {
addConstraint(subtype.getComponentType(), supertype.getComponentType(), constraints);
return null;
}
@Override
public Void visitArray_Declared(
AnnotatedArrayType subtype, AnnotatedDeclaredType supertype, Set<AFConstraint> constraints) {
return null;
}
@Override
public Void visitArray_Null(
AnnotatedArrayType subtype, AnnotatedNullType supertype, Set<AFConstraint> constraints) {
return null;
}
@Override
public Void visitArray_Wildcard(
AnnotatedArrayType subtype, AnnotatedWildcardType supertype, Set<AFConstraint> constraints) {
visitWildcardAsSuperType(subtype, supertype, constraints);
return null;
}
// Despite the above the comment at the beginning of the "array as arguments" section, a type
// variable cannot actually have an array type as its upper bound (e.g. <T extends Integer[]> is
// not allowed).
// So the only cases in which we visitArray_Typevar would be cases in which either:
// 1) Typevar is a type parameter for which we are inferring an argument, in which case the
// combination is already irreducible and we would not pass it to this class.
// 2) Typevar is an outer scope type variable, in which case it could NOT reference any of the
// type parameters for which we are inferring arguments and therefore will not lead to any
// meaningful AFConstraints.
// public void visitArray_Typevar
// ------------------------------------------------------------------------
// Declared as argument
/**
* I believe there should be only 1 way to have a constraint of this form: {@code visit (Array<T>,
* T [])} At this point, I don't think that's a valid argument for a formal parameter. If this
* occurs it is because of idiosyncrasies with the Checker Framework . We're going to skip this
* case for now.
*/
@Override
public Void visitDeclared_Array(
AnnotatedDeclaredType subtype, AnnotatedArrayType supertype, Set<AFConstraint> constraints) {
return null;
}
// From the JLS Spec:
// If F has the form G<..., Yk-1,U, Yk+1, ...>, where U involves Tj
@Override
public Void visitDeclared_Declared(
AnnotatedDeclaredType subtype,
AnnotatedDeclaredType supertype,
Set<AFConstraint> constraints) {
if (subtype.wasRaw() || supertype.wasRaw()) {
// The error will be caught in {@link DefaultTypeArgumentInference#infer} and
// inference will be aborted, but type-checking will continue.
throw new BugInCF("Can't infer type arguments when raw types are involved.");
}
if (!TypesUtils.isErasedSubtype(
subtype.getUnderlyingType(),
supertype.getUnderlyingType(),
typeFactory.getChecker().getTypeUtils())) {
return null;
}
AnnotatedDeclaredType subAsSuper =
AnnotatedTypes.castedAsSuper(typeFactory, subtype, supertype);
final List<AnnotatedTypeMirror> subTypeArgs = subAsSuper.getTypeArguments();
final List<AnnotatedTypeMirror> superTypeArgs = supertype.getTypeArguments();
for (int i = 0; i < subTypeArgs.size(); i++) {
final AnnotatedTypeMirror subTypeArg = subTypeArgs.get(i);
final AnnotatedTypeMirror superTypeArg = superTypeArgs.get(i);
// If F has the form G<..., Yk-1, ? extends U, Yk+1, ...>, where U involves Tj
// If F has the form G<..., Yk-1, ? super U, Yk+1, ...>, where U involves Tj
// Since we always have both bounds in the checker framework we always compare both
if (superTypeArg.getKind() == TypeKind.WILDCARD) {
final AnnotatedWildcardType superWc = (AnnotatedWildcardType) superTypeArg;
if (subTypeArg.getKind() == TypeKind.WILDCARD) {
final AnnotatedWildcardType subWc = (AnnotatedWildcardType) subTypeArg;
TypeArgInferenceUtil.checkForUninferredTypes(subWc);
addConstraint(subWc.getExtendsBound(), superWc.getExtendsBound(), constraints);
addInverseConstraint(superWc.getSuperBound(), subWc.getSuperBound(), constraints);
} else {
addConstraint(subTypeArg, superWc.getExtendsBound(), constraints);
addInverseConstraint(superWc.getSuperBound(), subTypeArg, constraints);
}
} else {
// if F has the form G<..., Yk-1, U, Yk+1, ...>, where U is a type expression that involves
// Tj
addEqualityConstraint(subTypeArg, superTypeArg, constraints);
}
}
return null;
}
@Override
public Void visitDeclared_Intersection(
AnnotatedDeclaredType subtype,
AnnotatedIntersectionType supertype,
Set<AFConstraint> constraints) {
// Note: AnnotatedIntersectionTypes cannot have a type variable as one of the direct
// parameters but a type variable may be the type subtype to an intersection bound <e.g. <T
// extends Serializable & Iterable<T>>
for (final AnnotatedTypeMirror intersectionBound : supertype.getBounds()) {
if (intersectionBound instanceof AnnotatedDeclaredType
&& !((AnnotatedDeclaredType) intersectionBound).getTypeArguments().isEmpty()) {
addConstraint(subtype, supertype, constraints);
}
}
return null;
}
// Remember that NULL types can come from lower bounds
@Override
public Void visitDeclared_Null(
AnnotatedDeclaredType subtype, AnnotatedNullType supertype, Set<AFConstraint> constraints) {
return null;
}
// a primitive supertype provides us no information on the type of any type parameters for that
// method
@Override
public Void visitDeclared_Primitive(
AnnotatedDeclaredType subtype,
AnnotatedPrimitiveType supertype,
Set<AFConstraint> constraints) {
return null;
}
@Override
public Void visitDeclared_Typevar(
AnnotatedDeclaredType subtype,
AnnotatedTypeVariable supertype,
Set<AFConstraint> constraints) {
// Note: We expect the A2F constraints where F == a targeted type supertype to already be
// removed. Therefore, supertype should NOT be a target.
addConstraint(subtype, supertype, constraints);
return null;
}
@Override
public Void visitDeclared_Union(
AnnotatedDeclaredType subtype, AnnotatedUnionType supertype, Set<AFConstraint> constraints) {
return null; // TODO: NOT SUPPORTED AT THE MOMENT
}
@Override
public Void visitDeclared_Wildcard(
AnnotatedDeclaredType subtype,
AnnotatedWildcardType supertype,
Set<AFConstraint> constraints) {
visitWildcardAsSuperType(subtype, supertype, constraints);
return null;
}
// ------------------------------------------------------------------------
// Intersection as subtype
@Override
public Void visitIntersection_Declared(
AnnotatedIntersectionType subtype,
AnnotatedDeclaredType supertype,
Set<AFConstraint> constraints) {
// at least one of the intersection bound types must be convertible to the param type
final AnnotatedDeclaredType subtypeAsParam =
AnnotatedTypes.castedAsSuper(typeFactory, subtype, supertype);
if (subtypeAsParam != null && !subtypeAsParam.equals(supertype)) {
addConstraint(subtypeAsParam, supertype, constraints);
}
return null;
}
@Override
public Void visitIntersection_Intersection(
AnnotatedIntersectionType argument,
AnnotatedIntersectionType parameter,
Set<AFConstraint> constraints) {
return null; // TODO: NOT SUPPORTED AT THE MOMENT
}
// provides no information as the AnnotatedNullType cannot refer to a type parameter
@Override
public Void visitIntersection_Null(
AnnotatedIntersectionType argument,
AnnotatedNullType parameter,
Set<AFConstraint> constraints) {
return null;
}
// ------------------------------------------------------------------------
// Null as argument
/**
* NULL types only have primary annotations. A type parameter could only appear as a component of
* the parameter type and therefore has no relationship to these primary annotations
*/
@Override
public Void visitNull_Array(
AnnotatedNullType argument, AnnotatedArrayType parameter, Set<AFConstraint> constraints) {
return null;
}
/**
* NULL types only have primary annotations. A type parameter could only appear as a component of
* the parameter type and therefore has no relationship to these primary annotations
*/
@Override
public Void visitNull_Declared(
AnnotatedNullType argument, AnnotatedDeclaredType parameter, Set<AFConstraint> constraints) {
return null;
}
/**
* TODO: PERHAPS FOR ALL OF THESE WHERE WE COMPARE AGAINST THE LOWER BOUND, WE SHOULD INSTEAD
* COMPARE TODO: against the UPPER_BOUND with the LOWER_BOUND's PRIMARY ANNOTATIONS For captured
* types, the lower bound might be interesting so we compare against the lower bound but for most
* types the constraint added in this method is probably discarded in the next round of reduction
* (especially since we don't implement capture at the moment).
*/
@Override
public Void visitNull_Typevar(
AnnotatedNullType subtype, AnnotatedTypeVariable supertype, Set<AFConstraint> constraints) {
// Note: We would expect that parameter is not one of the targets or else it would already
// be removed. Therefore we compare NULL against its bound.
addConstraint(subtype, supertype.getLowerBound(), constraints);
return null;
}
@Override
public Void visitNull_Wildcard(
AnnotatedNullType subtype, AnnotatedWildcardType supertype, Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(supertype);
// we don't use visitSupertype because Null types won't have interesting components
constraints.add(new A2F(subtype, supertype.getSuperBound()));
return null;
}
@Override
public Void visitNull_Null(
AnnotatedNullType argument, AnnotatedNullType parameter, Set<AFConstraint> constraints) {
return null;
}
@Override
public Void visitNull_Union(
AnnotatedNullType argument, AnnotatedUnionType parameter, Set<AFConstraint> constraints) {
return null; // TODO: UNIONS ARE NOT YET SUPPORTED
}
// Despite the fact that intersections are not yet supported, this is the right impelementation.
// NULL types only have primary annotations. Since type parameters cannot be a member of the
// intersection's bounds (though they can be component types), we do not need to do anything
// further.
@Override
public Void visitNull_Intersection(
AnnotatedNullType argument,
AnnotatedIntersectionType parameter,
Set<AFConstraint> constraints) {
return null;
}
// Primitive parameter types tell us nothing about the type parameters
@Override
public Void visitNull_Primitive(
AnnotatedNullType argument, AnnotatedPrimitiveType parameter, Set<AFConstraint> constraints) {
return null;
}
// ------------------------------------------------------------------------
// Primitive as argument
@Override
public Void visitPrimitive_Declared(
AnnotatedPrimitiveType subtype,
AnnotatedDeclaredType supertype,
Set<AFConstraint> constraints) {
// we may be able to eliminate this case, since I believe the corresponding constraint will
// just be discarded as the parameter must be a boxed primitive
addConstraint(typeFactory.getBoxedType(subtype), supertype, constraints);
return null;
}
// Primitive parameter types tell us nothing about the type parameters
@Override
public Void visitPrimitive_Primitive(
AnnotatedPrimitiveType subtype,
AnnotatedPrimitiveType supertype,
Set<AFConstraint> constraints) {
return null;
}
@Override
public Void visitPrimitive_Intersection(
AnnotatedPrimitiveType subtype,
AnnotatedIntersectionType supertype,
Set<AFConstraint> constraints) {
addConstraint(typeFactory.getBoxedType(subtype), supertype, constraints);
return null;
}
// ------------------------------------------------------------------------
// Union as argument
@Override
public Void visitUnion_Declared(
AnnotatedUnionType argument, AnnotatedDeclaredType parameter, Set<AFConstraint> constraints) {
return null; // TODO: UNIONS ARE NOT CURRENTLY SUPPORTED
}
// ------------------------------------------------------------------------
// typevar as argument
// If we've reached this point, the typevar is NOT one of the types we are inferring.
@Override
public Void visitTypevar_Declared(
AnnotatedTypeVariable subtype,
AnnotatedDeclaredType supertype,
Set<AFConstraint> constraints) {
addConstraint(subtype.getUpperBound(), supertype, constraints);
return null;
}
@Override
public Void visitTypevar_Typevar(
AnnotatedTypeVariable subtype,
AnnotatedTypeVariable supertype,
Set<AFConstraint> constraints) {
// if we've reached this point and the two are corresponding type variables, then they are
// NOT ones that may have a type variable we are inferring types for and therefore we can
// discard this constraint
if (!AnnotatedTypes.areCorrespondingTypeVariables(
typeFactory.getElementUtils(), subtype, supertype)) {
addConstraint(subtype.getUpperBound(), supertype.getLowerBound(), constraints);
}
return null;
}
@Override
public Void visitTypevar_Null(
AnnotatedTypeVariable subtype, AnnotatedNullType supertype, Set<AFConstraint> constraints) {
addConstraint(subtype.getUpperBound(), supertype, constraints);
return null;
}
@Override
public Void visitTypevar_Wildcard(
AnnotatedTypeVariable subtype,
AnnotatedWildcardType supertype,
Set<AFConstraint> constraints) {
visitWildcardAsSuperType(subtype, supertype, constraints);
return null;
}
@Override
public Void visitTypevar_Intersection(
AnnotatedTypeVariable subtype,
AnnotatedIntersectionType supertype,
Set<AFConstraint> constraints) {
addConstraint(subtype.getUpperBound(), supertype, constraints);
return null;
}
// ------------------------------------------------------------------------
// wildcard as subtype
@Override
public Void visitWildcard_Array(
AnnotatedWildcardType subtype, AnnotatedArrayType supertype, Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(subtype);
addConstraint(subtype.getExtendsBound(), supertype, constraints);
return null;
}
@Override
public Void visitWildcard_Declared(
AnnotatedWildcardType subtype,
AnnotatedDeclaredType supertype,
Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(subtype);
addConstraint(subtype.getExtendsBound(), supertype, constraints);
return null;
}
@Override
public Void visitWildcard_Intersection(
AnnotatedWildcardType subtype,
AnnotatedIntersectionType supertype,
Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(subtype);
addConstraint(subtype.getExtendsBound(), supertype, constraints);
return null;
}
@Override
public Void visitWildcard_Primitive(
AnnotatedWildcardType subtype,
AnnotatedPrimitiveType supertype,
Set<AFConstraint> constraints) {
return null;
}
@Override
public Void visitWildcard_Typevar(
AnnotatedWildcardType subtype,
AnnotatedTypeVariable supertype,
Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(subtype);
addConstraint(subtype.getExtendsBound(), supertype, constraints);
return null;
}
@Override
public Void visitWildcard_Wildcard(
AnnotatedWildcardType subtype,
AnnotatedWildcardType supertype,
Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(subtype);
// since wildcards are handled in visitDeclared_Declared this could only occur if two
// wildcards were passed to type subtype inference at the top level. This can only occur
// because we do not implement capture conversion.
visitWildcardAsSuperType(subtype.getExtendsBound(), supertype, constraints);
return null;
}
// should the same logic apply to typevars?
public void visitWildcardAsSuperType(
AnnotatedTypeMirror subtype, AnnotatedWildcardType supertype, Set<AFConstraint> constraints) {
TypeArgInferenceUtil.checkForUninferredTypes(supertype);
// this case occur only when supertype should actually be capture converted (which we don't
// do) because all other wildcard cases would be handled via Declared_Declared
addConstraint(subtype, supertype.getSuperBound(), constraints);
// if type1 is below the superbound then it is necessarily below the extends bound
// BUT the extends bound may have interesting component types (like the array component)
// to which we also want to apply constraints
// e.g. visitArray_Wildcard(@I String[], ? extends @A String[])
// if @I is an annotation we are trying to infer then we still want to infer that @I <: @A
// in fact
addInverseConstraint(subtype, supertype.getExtendsBound(), constraints);
}
}