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);
}
}
}
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;
}
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;
}
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();
}
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);
}
Aggregations