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

framework.src.org.checkerframework.qualframework.poly.MethodParameterInference 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.qualframework.poly;

import java.util.*;

import org.checkerframework.javacutil.Pair;

import org.checkerframework.qualframework.base.QualifiedTypeMirror;
import org.checkerframework.qualframework.base.QualifierHierarchy;
import org.checkerframework.qualframework.base.TypeHierarchy;
import org.checkerframework.qualframework.base.QualifierMapVisitor;

import org.checkerframework.qualframework.poly.PolyQual.*;

/** Helper class for performing method qualifier parameter inference.
 */
class MethodParameterInference {
    /** The names of the qualifier parameters we are trying to infer. */
    private List qualParams;
    /** The types of the method's formal parameters (ordinary parameters, not
     * type or qualifier parameters). */
    private List>> formals;
    /** The actual parameter types from the method call site, after type
     * parameter inference has been done. */
    private List>> actuals;

    private QualifierHierarchy groundHierarchy;
    private QualifierHierarchy> polyQualHierarchy;
    private QualifierParameterHierarchy qualParamHierarchy;
    private TypeHierarchy> typeHierarchy;

    /** This variable will be set to true if an unsatisfiable constraint is
     * found. */
    private boolean unsatisfiable;
    /** The strictest upper bound currently known for each qualifier parameter
     * listed in {@link qualParams}. */
    private List> upperBounds;
    /** The strictest lower bound currently known for each qualifier parameter
     * listed in {@link qualParams}. */
    private List> lowerBounds;

    /** Set to true if the infer() method has already been run. */
    private boolean alreadyRan;


    public MethodParameterInference(
            List qualParams,
            List>> formals,
            List>> actuals,
            QualifierHierarchy groundHierarchy,
            QualifierHierarchy> polyQualHierarchy,
            QualifierParameterHierarchy qualParamHierarchy,
            TypeHierarchy> typeHierarchy) {
        this.qualParams = qualParams;
        this.formals = formals;
        this.actuals = actuals;
        this.groundHierarchy = groundHierarchy;
        this.polyQualHierarchy = polyQualHierarchy;
        this.qualParamHierarchy = qualParamHierarchy;
        this.typeHierarchy = typeHierarchy;

        this.unsatisfiable = false;
        this.lowerBounds = new ArrayList<>();
        this.upperBounds = new ArrayList<>();
        for (int i = 0; i < qualParams.size(); ++i) {
            this.upperBounds.add(polyQualHierarchy.getTop());
            this.lowerBounds.add(polyQualHierarchy.getBottom());
        }

        this.alreadyRan = false;
    }


    // Methods for creating and manipulating inference variables.

    private static final String INFER_TAG = "_INFER";

    private QualVar makeInferVar(String name, int i) {
        return new QualVar(INFER_TAG + i + ":" + name,
                groundHierarchy.getBottom(), groundHierarchy.getTop());
    }

    private boolean isInferVar(PolyQual q) {
        return q instanceof QualVar && ((QualVar)q).getName().startsWith(INFER_TAG);
    }

    private int inferVarIndex(PolyQual q) {
        QualVar v = (QualVar)q;
        String name = v.getName();
        return Integer.parseInt(name.substring(INFER_TAG.length(), name.indexOf(':')));
    }


    /** Run qualifier parameter inference using the arguments provided to the
     * constructor. */
    public Map> infer() {
        if (this.alreadyRan) {
            throw new IllegalStateException("already ran infer() on this MethodParameterInference object");
        }
        this.alreadyRan = true;

        List, Wildcard>> constraints = findConstraints();

        for (Pair, Wildcard> p : constraints) {
            if (p == null) {
                unsatisfiable = true;
                continue;
            }

            Wildcard subset = p.first;
            Wildcard superset = p.second;

            processConstraint(subset, superset);
        }

        return getAssignment();
    }

    /** Collect the containment constraints that arise from requiring each
     * element in {@code actuals} to be a subtype of the corresponding element
     * of {@code formals}.  The resulting constraints will have inference
     * variables in the place of the method's qualifier variables. */
    private List, Wildcard>> findConstraints() {
        // Replace each qualifier variable named in `this.qualParams` with a
        // new inference variable.  (An inference variable is just a qualifier
        // variable with a special name.)

        // Note that non-capture qualifier variables cannot have bounds other
        // than bottom/top, so we don't need to look at the bounds on the
        // parameter declarations.
        List> inferVars = new ArrayList<>();
        Map> inferSubst = new HashMap<>();
        for (int i = 0; i < qualParams.size(); ++i) {
            inferVars.add(makeInferVar(qualParams.get(i), i));
            inferSubst.put(qualParams.get(i), new Wildcard(inferVars.get(i)));
        }

        List>> substitutedFormals = new ArrayList<>();
        for (QualifiedTypeMirror> formal : formals) {
            substitutedFormals.add(SUBSTITUTE_VISITOR.visit(formal, inferSubst));
        }

        // Generate constraints by requiring each actual parameter to be a
        // subtype of the corresponding formal parameter (with inference
        // variables substituted into the formal parameters).

        List, Wildcard>> constraints = new ArrayList<>();
        qualParamHierarchy.setConstraintTarget(constraints);

        for (int i = 0; i < formals.size(); ++i) {
            typeHierarchy.isSubtype(actuals.get(i), substitutedFormals.get(i));
        }

        qualParamHierarchy.setConstraintTarget(null);

        return constraints;
    }

    /** Process a single containment constraint and update {@code upperBounds}
     * and {@code lowerBounds} accordingly. */
    private void processConstraint(Wildcard subset, Wildcard superset) {
        if (!isInferVar(subset.getLowerBound())
                && !isInferVar(subset.getUpperBound())
                && !isInferVar(superset.getLowerBound())
                && !isInferVar(superset.getUpperBound())) {
            // There are no vars to infer so the constraint isn't part of the solution
            return;
        }
        addSubtypeBound(superset.getLowerBound(), subset.getLowerBound());
        addSubtypeBound(subset.getLowerBound(), subset.getUpperBound());
        addSubtypeBound(subset.getUpperBound(), superset.getUpperBound());
    }

    private void addSubtypeBound(PolyQual subtype, PolyQual supertype) {
        if (isInferVar(subtype) && isInferVar(supertype)) {
            throw new UnsupportedOperationException();
        } else if (isInferVar(subtype)) {
            // INFER#1 <: TAINTED.  So the upper bound for #1 should be no
            // higher than TAINTED.
            int id = inferVarIndex(subtype);
            PolyQual oldUpper = upperBounds.get(id);
            upperBounds.set(id, polyQualHierarchy.greatestLowerBound(oldUpper, supertype));
        } else if (isInferVar(supertype)) {
            // TAINTED <: INFER#1.  So the lower bound for #1 should be no
            // lower than TAINTED.
            int id = inferVarIndex(supertype);
            PolyQual oldLower = lowerBounds.get(id);
            lowerBounds.set(id, polyQualHierarchy.leastUpperBound(oldLower, subtype));
        } else {
            // The constraint UNTAINTED <: TAINTED is always true, so do
            // nothing.  The constraint TAINTED <: UNTAINTED is always false,
            // so mark the current set of constraints as unsatisfiable.
            if (!polyQualHierarchy.isSubtype(subtype, supertype)) {
                this.unsatisfiable = true;
            }
        }
    }

    /** Build a map that gives the inferred qualifier for each qualifier
     * parameter, or null if the constraints are unsatisfiable. */
    public Map> getAssignment() {
        if (unsatisfiable) {
            return null;
        }

        Map> map = new HashMap<>();
        for (int i = 0; i < qualParams.size(); ++i) {
            map.put(qualParams.get(i), lowerBounds.get(i));
            // TODO: Check that `lower <: upper`.  The check itself is easy to
            // implement, but getting correct error reporting out of
            // QPTF.methodFromUse is a little harder.
        }

        return map;
    }


    /** Helper visitor to perform substitution at every location in a {@link
     * QualifiedTypeMirror}. */
    private QualifierMapVisitor, QualParams, Map>> SUBSTITUTE_VISITOR =
        new QualifierMapVisitor, QualParams, Map>>() {
            @Override
            public QualParams process(QualParams params, Map> substs) {
                if (params.equals(qualParamHierarchy.getBottom())) {
                    return qualParamHierarchy.getBottom();
                }

                return params.substituteAll(substs);
            }
        };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy