Search in sources :

Example 1 with AFConstraint

use of org.checkerframework.framework.util.typeinference.constraint.AFConstraint in project checker-framework by typetools.

the class DefaultTypeArgumentInference method reduceAfConstraints.

/**
 * Given a set of AFConstraints, remove all constraints that are not relevant to inference and
 * return a set of AFConstraints in which the F is a use of one of the type parameters to infer.
 */
protected void reduceAfConstraints(final AnnotatedTypeFactory typeFactory, final Set<AFConstraint> outgoing, final Queue<AFConstraint> toProcess, final Set<TypeVariable> targets) {
    final Set<AFConstraint> visited = new HashSet<>();
    List<AFReducer> reducers = Arrays.asList(new A2FReducer(typeFactory), new F2AReducer(typeFactory), new FIsAReducer(typeFactory));
    Set<AFConstraint> newConstraints = new HashSet<>(10);
    while (!toProcess.isEmpty()) {
        newConstraints.clear();
        AFConstraint constraint = toProcess.remove();
        if (!visited.contains(constraint)) {
            if (constraint.isIrreducible(targets)) {
                outgoing.add(constraint);
            } else {
                final Iterator<AFReducer> reducerIterator = reducers.iterator();
                boolean handled = false;
                while (!handled && reducerIterator.hasNext()) {
                    handled = reducerIterator.next().reduce(constraint, newConstraints);
                }
                if (!handled) {
                    throw new BugInCF("Unhandled constraint type: " + constraint);
                }
                toProcess.addAll(newConstraints);
            }
            visited.add(constraint);
        }
    }
}
Also used : A2FReducer(org.checkerframework.framework.util.typeinference.constraint.A2FReducer) AFReducer(org.checkerframework.framework.util.typeinference.constraint.AFReducer) F2AReducer(org.checkerframework.framework.util.typeinference.constraint.F2AReducer) BugInCF(org.checkerframework.javacutil.BugInCF) AFConstraint(org.checkerframework.framework.util.typeinference.constraint.AFConstraint) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) FIsAReducer(org.checkerframework.framework.util.typeinference.constraint.FIsAReducer)

Example 2 with AFConstraint

use of org.checkerframework.framework.util.typeinference.constraint.AFConstraint in project checker-framework by typetools.

the class DefaultTypeArgumentInference method createInitialAssignmentConstraints.

/**
 * Create a set of constraints between return type and any type to which it is assigned. Reduce
 * these set of constraints and remove any that is not an equality (FIsA) constraint.
 */
protected Set<FIsA> createInitialAssignmentConstraints(final AnnotatedTypeMirror assignedTo, final AnnotatedTypeMirror boxedReturnType, final AnnotatedTypeFactory typeFactory, final Set<TypeVariable> targets) {
    final Set<FIsA> result = new LinkedHashSet<>();
    if (assignedTo != null) {
        final Set<AFConstraint> reducedConstraints = new LinkedHashSet<>();
        final Queue<AFConstraint> constraints = new ArrayDeque<>();
        constraints.add(new F2A(boxedReturnType, assignedTo));
        reduceAfConstraints(typeFactory, reducedConstraints, constraints, targets);
        for (final AFConstraint reducedConstraint : reducedConstraints) {
            if (reducedConstraint instanceof FIsA) {
                result.add((FIsA) reducedConstraint);
            }
        }
    }
    return result;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) FIsA(org.checkerframework.framework.util.typeinference.constraint.FIsA) F2A(org.checkerframework.framework.util.typeinference.constraint.F2A) AFConstraint(org.checkerframework.framework.util.typeinference.constraint.AFConstraint) ArrayDeque(java.util.ArrayDeque)

Example 3 with AFConstraint

use of org.checkerframework.framework.util.typeinference.constraint.AFConstraint in project checker-framework by typetools.

the class DefaultTypeArgumentInference method createArgumentAFConstraints.

/**
 * Step 1: Create a constraint {@code Ai << Fi} for each Argument(Ai) to formal parameter(Fi).
 * Remove any constraint that does not involve a type parameter to be inferred. Reduce the
 * remaining constraints so that Fi = Tj where Tj is a type parameter with an argument to be
 * inferred. Return the resulting constraint set.
 *
 * @param typeFactory AnnotatedTypeFactory
 * @param argTypes list of annotated types corresponding to the arguments to the method
 * @param methodType annotated type of the method
 * @param targets type variables to be inferred
 * @param useNullArguments whether or not null method arguments should be considered
 * @return a set of argument constraints
 */
protected Set<AFConstraint> createArgumentAFConstraints(final AnnotatedTypeFactory typeFactory, final List<AnnotatedTypeMirror> argTypes, final AnnotatedExecutableType methodType, final Set<TypeVariable> targets, boolean useNullArguments) {
    final List<AnnotatedTypeMirror> paramTypes = AnnotatedTypes.expandVarArgsParametersFromTypes(methodType, argTypes);
    if (argTypes.size() != paramTypes.size()) {
        throw new BugInCF(StringsPlume.joinLines("Mismatch between formal parameter count and argument count.", "paramTypes=" + StringsPlume.join(",", paramTypes), "argTypes=" + StringsPlume.join(",", argTypes)));
    }
    final int numberOfParams = paramTypes.size();
    final ArrayDeque<AFConstraint> afConstraints = new ArrayDeque<>(numberOfParams);
    for (int i = 0; i < numberOfParams; i++) {
        if (!useNullArguments && argTypes.get(i).getKind() == TypeKind.NULL) {
            continue;
        }
        afConstraints.add(new A2F(argTypes.get(i), paramTypes.get(i)));
    }
    final Set<AFConstraint> reducedConstraints = new LinkedHashSet<>();
    reduceAfConstraints(typeFactory, reducedConstraints, afConstraints, targets);
    return reducedConstraints;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) A2F(org.checkerframework.framework.util.typeinference.constraint.A2F) BugInCF(org.checkerframework.javacutil.BugInCF) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) AFConstraint(org.checkerframework.framework.util.typeinference.constraint.AFConstraint) TUConstraint(org.checkerframework.framework.util.typeinference.constraint.TUConstraint) AFConstraint(org.checkerframework.framework.util.typeinference.constraint.AFConstraint) ArrayDeque(java.util.ArrayDeque)

Example 4 with AFConstraint

use of org.checkerframework.framework.util.typeinference.constraint.AFConstraint in project checker-framework by typetools.

the class DefaultTypeArgumentInference method infer.

/**
 * This algorithm works as follows:
 *
 * <ul>
 *   <!-- ul rather than ol because of many cross-references within the text -->
 *   <li>1. Build Argument Constraints -- create a set of constraints using the arguments to the
 *       type parameter declarations, the formal parameters, and the arguments to the method call
 *   <li>2. Solve Argument Constraints -- Create two solutions from the arguments.
 *       <ol>
 *         <li>Equality Arg Solution: Solution inferred from arguments used in an invariant
 *             position (i.e. from equality constraints)
 *         <li>Supertypes Arg Solution: Solution inferred from constraints in which the parameter
 *             is a supertype of argument types. These are kept separate and merged later.
 *       </ol>
 *       Note: If there is NO assignment context we just combine the results from 2.a and 2.b,
 *       giving preference to those in 2.a, and return the result.
 *   <li>3. Build and Solve Initial Assignment Constraints -- Create a set of constraints from the
 *       assignment context WITHOUT substituting either solution from step 2.
 *   <li>4. Combine the solutions from steps 2.b and 3. This handles cases like the following:
 *       <pre>{@code
 * <T> List<T> method(T t1) {}
 * List<@Nullable String> nl = method("");
 * }</pre>
 *       If we use just the arguments to infer T we will infer @NonNull String (since the lub of
 *       all arguments would be @NonNull String). However, this would cause the assignment to
 *       fail. Instead, since {@literal @NonNull String <: @Nullable String}, we can safely infer
 *       T to be @Nullable String and both the argument types and the assignment types are
 *       compatible. In step 4, we combine the results of Step 2.b (which came from lubbing
 *       argument and argument component types) with the solution from equality constraints via
 *       the assignment context.
 *       <p>Note, we always give preference to the results inferred from method arguments if there
 *       is a conflict between the steps 2 and 4. For example:
 *       <pre>{@code
 * <T> List<T> method(T t1) {}
 * List<@NonNull String> nl = method(null);
 * }</pre>
 *       In the above example, the null argument requires that T must be @Nullable String. But the
 *       assignment context requires that the T must be @NonNull String. But, in this case if we
 *       use @NonNull String the argument "null" is invalid. In this case, we use @Nullable String
 *       and report an assignment because we ALWAYS favor the arguments over the assignment
 *       context.
 *   <li>5. Combine the result from 2.a and step 4, if there is a conflict use the result from
 *       step 2.a
 *       <p>Suppose we have the following:
 *       <pre>{@code
 * <T> void method(List<@NonNull T> t, @Initialized Tt) { ... }
 * List<@FBCBottom String> lBottom = ...;
 * method( lbBottom, "nonNullString" );
 * }</pre>
 *       From the first argument we can infer that T must be exactly @FBCBottom String but we
 *       cannot infer anything for the Nullness hierarchy. For the second argument we can infer
 *       that T is at most @NonNull String but we can infer nothing in the initialization
 *       hierarchy. In this step we combine these two results, always favoring the equality
 *       constraints if there is a conflict. For the above example we would infer the following:
 *       <pre>{@code
 * T => @FBCBottom @NonNull String
 * }</pre>
 *       Another case covered in this step is:
 *       <pre>{@code
 * <T> List<T> method(List<T> t1) {}
 * List<@NonNull String> nonNullList = new ArrayList<>();
 * List<@Nullable String> nl = method(nonNullList);
 * }</pre>
 *       The above assignment should fail because T is forced to be both @NonNull and @Nullable.
 *       In cases like these, we use @NonNull String becasue we always favor constraints from the
 *       arguments over the assignment context.
 *   <li>6. Infer from Assignment Context Finally, the JLS states that we should substitute the
 *       types we have inferred up until this point back into the original argument constraints.
 *       We should then combine the constraints we get from the assignment context and solve using
 *       the greatest lower bounds of all of the constraints of the form: {@literal F :> U} (these
 *       are referred to as "subtypes" in the ConstraintMap.TargetConstraints).
 *   <li>7. Merge the result from steps 5 and 6 giving preference to 5 (the argument constraints).
 *       Return the result.
 * </ul>
 */
private Map<TypeVariable, AnnotatedTypeMirror> infer(final AnnotatedTypeFactory typeFactory, final List<AnnotatedTypeMirror> argumentTypes, final AnnotatedTypeMirror assignedTo, final ExecutableElement methodElem, final AnnotatedExecutableType methodType, final Set<TypeVariable> targets, final boolean useNullArguments) {
    // 1.  Step 1 - Build up argument constraints
    // The AFConstraints for arguments are used also in the
    Set<AFConstraint> afArgumentConstraints = createArgumentAFConstraints(typeFactory, argumentTypes, methodType, targets, useNullArguments);
    // 2. Step 2 - Solve the constraints.
    Pair<InferenceResult, InferenceResult> argInference = inferFromArguments(typeFactory, afArgumentConstraints, targets);
    // result 2.a
    final InferenceResult fromArgEqualities = argInference.first;
    // result 2.b
    final InferenceResult fromArgSubandSupers = argInference.second;
    clampToLowerBound(fromArgSubandSupers, methodType.getTypeVariables(), typeFactory);
    // a variable, assignedTo is the type of that variable
    if (assignedTo == null) {
        fromArgEqualities.mergeSubordinate(fromArgSubandSupers);
        return fromArgEqualities.toAtmMap();
    }
    // else
    final AnnotatedTypeMirror declaredReturnType = methodType.getReturnType();
    final AnnotatedTypeMirror boxedReturnType;
    if (declaredReturnType == null) {
        boxedReturnType = null;
    } else if (declaredReturnType.getKind().isPrimitive()) {
        boxedReturnType = typeFactory.getBoxedType((AnnotatedPrimitiveType) declaredReturnType);
    } else {
        boxedReturnType = declaredReturnType;
    }
    final InferenceResult fromArguments = fromArgEqualities;
    if (!((MethodSymbol) methodElem).isConstructor()) {
        // Step 3 - Infer a solution from the equality constraints in the assignment context
        InferenceResult fromAssignmentEqualities = inferFromAssignmentEqualities(assignedTo, boxedReturnType, targets, typeFactory);
        // Step 4 - Combine the results from 2.b and step 3
        InferenceResult combinedSupertypesAndAssignment = combineSupertypeAndAssignmentResults(targets, typeFactory, fromAssignmentEqualities, fromArgSubandSupers);
        // Step 5 - Combine the result from 2.a and step 4, if there is a conflict use the
        // result from step 2.a
        fromArgEqualities.mergeSubordinate(combinedSupertypesAndAssignment);
        // constraints
        if (!fromArguments.isComplete(targets)) {
            InferenceResult fromAssignment = inferFromAssignment(assignedTo, boxedReturnType, methodType, afArgumentConstraints, fromArguments, targets, typeFactory);
            // Step 7 - Merge the argument and the assignment constraints
            fromArguments.mergeSubordinate(fromAssignment);
        }
    } else {
        fromArguments.mergeSubordinate(fromArgSubandSupers);
    }
    return fromArguments.toAtmMap();
}
Also used : InferenceResult(org.checkerframework.framework.util.typeinference.solver.InferenceResult) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) AFConstraint(org.checkerframework.framework.util.typeinference.constraint.AFConstraint)

Example 5 with AFConstraint

use of org.checkerframework.framework.util.typeinference.constraint.AFConstraint in project checker-framework by typetools.

the class DefaultTypeArgumentInference method createAssignmentConstraints.

/**
 * The first half of Step 6.
 *
 * <p>This method creates constraints:
 *
 * <ul>
 *   <li>between the bounds of types that are already inferred and their inferred arguments
 *   <li>between the assignment context and the return type of the method (with the previously
 *       inferred arguments substituted into these constraints)
 * </ul>
 */
public ConstraintMap createAssignmentConstraints(final AnnotatedTypeMirror assignedTo, final AnnotatedTypeMirror boxedReturnType, final AnnotatedExecutableType methodType, final Set<AFConstraint> afArgumentConstraints, final Map<TypeVariable, AnnotatedTypeMirror> inferredArgs, final Set<TypeVariable> targets, final AnnotatedTypeFactory typeFactory) {
    final ArrayDeque<AFConstraint> assignmentAfs = new ArrayDeque<>(2 * methodType.getTypeVariables().size() + afArgumentConstraints.size());
    for (AnnotatedTypeVariable typeParam : methodType.getTypeVariables()) {
        final TypeVariable target = typeParam.getUnderlyingType();
        final AnnotatedTypeMirror inferredType = inferredArgs.get(target);
        // the lower bound for all uninferred types Tu: Tu >> Bi and Lu >> Tu
        if (inferredType != null) {
            assignmentAfs.add(new A2F(inferredType, typeParam.getUpperBound()));
            assignmentAfs.add(new F2A(typeParam.getLowerBound(), inferredType));
        } else {
            assignmentAfs.add(new F2A(typeParam, typeParam.getUpperBound()));
            assignmentAfs.add(new A2F(typeParam.getLowerBound(), typeParam));
        }
    }
    for (AFConstraint argConstraint : afArgumentConstraints) {
        if (argConstraint instanceof F2A) {
            assignmentAfs.add(argConstraint);
        }
    }
    ArrayDeque<AFConstraint> substitutedAssignmentConstraints = new ArrayDeque<>(assignmentAfs.size() + 1);
    for (AFConstraint afConstraint : assignmentAfs) {
        substitutedAssignmentConstraints.add(afConstraint.substitute(inferredArgs));
    }
    final AnnotatedTypeMirror substitutedReturnType = TypeArgInferenceUtil.substitute(inferredArgs, boxedReturnType);
    substitutedAssignmentConstraints.add(new F2A(substitutedReturnType, assignedTo));
    final Set<AFConstraint> reducedConstraints = new LinkedHashSet<>();
    reduceAfConstraints(typeFactory, reducedConstraints, substitutedAssignmentConstraints, targets);
    final Set<TUConstraint> tuAssignmentConstraints = afToTuConstraints(reducedConstraints, targets);
    addConstraintsBetweenTargets(tuAssignmentConstraints, targets, true, typeFactory);
    return constraintMapBuilder.build(targets, tuAssignmentConstraints, typeFactory);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) F2A(org.checkerframework.framework.util.typeinference.constraint.F2A) TypeVariable(javax.lang.model.type.TypeVariable) AnnotatedTypeVariable(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable) A2F(org.checkerframework.framework.util.typeinference.constraint.A2F) TUConstraint(org.checkerframework.framework.util.typeinference.constraint.TUConstraint) AnnotatedTypeVariable(org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) AFConstraint(org.checkerframework.framework.util.typeinference.constraint.AFConstraint) ArrayDeque(java.util.ArrayDeque)

Aggregations

AFConstraint (org.checkerframework.framework.util.typeinference.constraint.AFConstraint)5 LinkedHashSet (java.util.LinkedHashSet)4 ArrayDeque (java.util.ArrayDeque)3 AnnotatedTypeMirror (org.checkerframework.framework.type.AnnotatedTypeMirror)3 A2F (org.checkerframework.framework.util.typeinference.constraint.A2F)2 F2A (org.checkerframework.framework.util.typeinference.constraint.F2A)2 TUConstraint (org.checkerframework.framework.util.typeinference.constraint.TUConstraint)2 BugInCF (org.checkerframework.javacutil.BugInCF)2 HashSet (java.util.HashSet)1 TypeVariable (javax.lang.model.type.TypeVariable)1 AnnotatedTypeVariable (org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable)1 A2FReducer (org.checkerframework.framework.util.typeinference.constraint.A2FReducer)1 AFReducer (org.checkerframework.framework.util.typeinference.constraint.AFReducer)1 F2AReducer (org.checkerframework.framework.util.typeinference.constraint.F2AReducer)1 FIsA (org.checkerframework.framework.util.typeinference.constraint.FIsA)1 FIsAReducer (org.checkerframework.framework.util.typeinference.constraint.FIsAReducer)1 InferenceResult (org.checkerframework.framework.util.typeinference.solver.InferenceResult)1