All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.framework.util.typeinference.constraint.AFReducingVisitor Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.framework.util.typeinference.constraint;

import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.*;
import org.checkerframework.framework.type.DefaultTypeHierarchy;
import org.checkerframework.framework.type.visitor.AbstractAtmComboVisitor;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.framework.util.PluginUtil;

import javax.lang.model.type.TypeKind;
import java.util.List;
import java.util.Set;

/**
 * Takes a single step in reducing a AFConstraint.
 *
 * The visit method will determine if the given constraint should either:
 *    a) be discarded - in this case, the visitor just returns
 *    b) reduced to a simpler constraint or set of constraints - in this case, the new constraint
 *    or set of constraints is added to newConstraints
 *
 *  Sprinkled throughout this class are comments of the form:
 *
 * 
{@code
 *  // If F has the form G<..., Yk-1, ? super U, Yk+1, ...>, where U involves Tj
 * }
* * 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> { public final Class reducerType; public final AnnotatedTypeFactory typeFactory; public AFReducingVisitor(final Class 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 constraints) { constraints.add(makeConstraint(subtype, supertype)); } public void addInverseConstraint(AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype, Set constraints) { constraints.add(makeInverseConstraint(subtype, supertype)); } public void addEqualityConstraint(AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype, Set 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 constraints) { return "Unexpected " + reducerType.getSimpleName() + " + Combination:\b" + "subtype=" + subtype + "\n" + "supertype=" + supertype + "\n" + "constraints=[\n" + PluginUtil.join(", ", constraints) + "\n]"; } //------------------------------------------------------------------------ // 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 constraints) { addConstraint(subtype.getComponentType(), supertype.getComponentType(), constraints); return null; } @Override public Void visitArray_Declared(AnnotatedArrayType subtype, AnnotatedDeclaredType supertype, Set constraints) { return null; } @Override public Void visitArray_Null(AnnotatedArrayType subtype, AnnotatedNullType supertype, Set constraints) { return null; } @Override public Void visitArray_Wildcard(AnnotatedArrayType subtype, AnnotatedWildcardType supertype, Set 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. 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 [])} * At this point, I don't think that's a valid argument for a formal parameter. If this occurs * is 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 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 constraints) { if (subtype.wasRaw() || supertype.wasRaw()) { return null; } AnnotatedDeclaredType subAsSuper = DefaultTypeHierarchy.castedAsSuper(subtype, supertype); if (subAsSuper == null) { return null; } final List subTypeArgs = subAsSuper.getTypeArguments(); final List 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; 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 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 > for (final AnnotatedTypeMirror intersectionBound : supertype.directSuperTypes()) { if (intersectionBound instanceof AnnotatedDeclaredType && ((AnnotatedDeclaredType) intersectionBound).getTypeArguments().size() > 0) { 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 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 constraints) { return null; } @Override public Void visitDeclared_Typevar(AnnotatedDeclaredType subtype, AnnotatedTypeVariable supertype, Set constraints) { // Note: We expect the A2F constraints where F == a targeted type supertype to already be removed // Note: Therefore, supertype should NOT be a target addConstraint(subtype, supertype, constraints); return null; } @Override public Void visitDeclared_Union(AnnotatedDeclaredType subtype, AnnotatedUnionType supertype, Set constraints) { return null; //TODO: NOT SUPPORTED AT THE MOMENT } @Override public Void visitDeclared_Wildcard(AnnotatedDeclaredType subtype, AnnotatedWildcardType supertype, Set constraints) { visitWildcardAsSuperType(subtype, supertype, constraints); return null; } //------------------------------------------------------------------------ // Intersection as subtype @Override public Void visitIntersection_Declared(AnnotatedIntersectionType subtype, AnnotatedDeclaredType supertype, Set constraints) { // at least one of the intersection bound types must be convertible to the param type final AnnotatedDeclaredType subtypeAsParam = DefaultTypeHierarchy.castedAsSuper(subtype, supertype); if (subtypeAsParam != null && !subtypeAsParam.equals(subtype)) { addConstraint(subtypeAsParam, supertype, constraints); } return null; } @Override public Void visitIntersection_Intersection(AnnotatedIntersectionType argument, AnnotatedIntersectionType parameter, Set 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 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 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 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 constraints) { // Note: We would expect that parameter is not one of the targets or else it would already be removed // NOTE: Therefore we compare NULL against its bound addConstraint(subtype, supertype.getLowerBound(), constraints); return null; } @Override public Void visitNull_Wildcard(AnnotatedNullType subtype, AnnotatedWildcardType supertype, Set constraints) { // 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 constraints) { return null; } @Override public Void visitNull_Union(AnnotatedNullType argument, AnnotatedUnionType parameter, Set 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 constraints) { return null; } // Primitive parameter types tell us nothing about the type parameters @Override public Void visitNull_Primitive(AnnotatedNullType argument, AnnotatedPrimitiveType parameter, Set constraints) { return null; } //------------------------------------------------------------------------ // Primitive as argument @Override public Void visitPrimitive_Declared(AnnotatedPrimitiveType subtype, AnnotatedDeclaredType supertype, Set 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 constraints) { return null; } @Override public Void visitPrimitive_Intersection(AnnotatedPrimitiveType subtype, AnnotatedIntersectionType supertype, Set constraints) { addConstraint(typeFactory.getBoxedType(subtype), supertype, constraints); return null; } //------------------------------------------------------------------------ // Union as argument @Override public Void visitUnion_Declared(AnnotatedUnionType argument, AnnotatedDeclaredType parameter, Set 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 constraints) { addConstraint(subtype.getUpperBound(), supertype, constraints); return null; } @Override public Void visitTypevar_Typevar(AnnotatedTypeVariable subtype, AnnotatedTypeVariable supertype, Set 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 constraints) { addConstraint(subtype.getUpperBound(), supertype, constraints); return null; } @Override public Void visitTypevar_Wildcard(AnnotatedTypeVariable subtype, AnnotatedWildcardType supertype, Set constraints) { visitWildcardAsSuperType(subtype, supertype, constraints); return null; } //------------------------------------------------------------------------ // wildcard as subtype @Override public Void visitWildcard_Array(AnnotatedWildcardType subtype, AnnotatedArrayType supertype, Set constraints) { addConstraint(subtype.getExtendsBound(), supertype, constraints); return null; } @Override public Void visitWildcard_Declared(AnnotatedWildcardType subtype, AnnotatedDeclaredType supertype, Set constraints) { addConstraint(subtype.getExtendsBound(), supertype, constraints); return null; } @Override public Void visitWildcard_Intersection(AnnotatedWildcardType subtype, AnnotatedIntersectionType supertype, Set constraints) { addConstraint(subtype.getExtendsBound(), supertype, constraints); return null; } @Override public Void visitWildcard_Primitive(AnnotatedWildcardType subtype, AnnotatedPrimitiveType supertype, Set constraints) { return null; } @Override public Void visitWildcard_Typevar(AnnotatedWildcardType subtype, AnnotatedTypeVariable supertype, Set constraints) { addConstraint(subtype.getExtendsBound(), supertype, constraints); return null; } @Override public Void visitWildcard_Wildcard(AnnotatedWildcardType subtype, AnnotatedWildcardType supertype, Set constraints) { // 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 constraints) { // 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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy