use of org.checkerframework.framework.util.typeinference.solver.InferenceResult in project checker-framework by typetools.
the class DefaultTypeArgumentInference method inferFromAssignment.
/**
* The Second half of step 6. Use the assignment context to infer a result.
*/
private InferenceResult inferFromAssignment(final AnnotatedTypeMirror assignedTo, final AnnotatedTypeMirror boxedReturnType, final AnnotatedExecutableType methodType, final Set<AFConstraint> afArgumentConstraints, final InferenceResult inferredArgs, final Set<TypeVariable> targets, final AnnotatedTypeFactory typeFactory) {
ConstraintMap assignmentConstraints = createAssignmentConstraints(assignedTo, boxedReturnType, methodType, afArgumentConstraints, inferredArgs.toAtmMap(), targets, typeFactory);
InferenceResult equalitiesResult = equalitiesSolver.solveEqualities(targets, assignmentConstraints, typeFactory);
Set<TypeVariable> remainingTargets = equalitiesResult.getRemainingTargets(targets, true);
InferenceResult subtypesResult = subtypesSolver.solveFromSubtypes(remainingTargets, assignmentConstraints, typeFactory);
equalitiesResult.mergeSubordinate(subtypesResult);
return equalitiesResult;
}
use of org.checkerframework.framework.util.typeinference.solver.InferenceResult in project checker-framework by typetools.
the class DefaultTypeArgumentInference method inferFromArguments.
/**
* Step 2. Infer type arguments from the equality (TisU) and the supertype (TSuperU) constraints
* of the methods arguments.
*/
private Pair<InferenceResult, InferenceResult> inferFromArguments(final AnnotatedTypeFactory typeFactory, final Set<AFConstraint> afArgumentConstraints, final Set<TypeVariable> targets) {
Set<TUConstraint> tuArgConstraints = afToTuConstraints(afArgumentConstraints, targets);
addConstraintsBetweenTargets(tuArgConstraints, targets, false, typeFactory);
ConstraintMap argConstraints = constraintMapBuilder.build(targets, tuArgConstraints, typeFactory);
InferenceResult inferredFromArgEqualities = equalitiesSolver.solveEqualities(targets, argConstraints, typeFactory);
Set<TypeVariable> remainingTargets = inferredFromArgEqualities.getRemainingTargets(targets, true);
InferenceResult fromSupertypes = supertypesSolver.solveFromSupertypes(remainingTargets, argConstraints, typeFactory);
InferenceResult fromSubtypes = subtypesSolver.solveFromSubtypes(remainingTargets, argConstraints, typeFactory);
fromSupertypes.mergeSubordinate(fromSubtypes);
return Pair.of(inferredFromArgEqualities, fromSupertypes);
}
use of org.checkerframework.framework.util.typeinference.solver.InferenceResult in project checker-framework by typetools.
the class DefaultTypeArgumentInference method combineSupertypeAndAssignmentResults.
/**
* Step 4. Combine the results from using the Supertype constraints the Equality constraints
* from the assignment context.
*/
private InferenceResult combineSupertypeAndAssignmentResults(Set<TypeVariable> targets, AnnotatedTypeFactory typeFactory, InferenceResult equalityResult, InferenceResult supertypeResult) {
final TypeHierarchy typeHierarchy = typeFactory.getTypeHierarchy();
final InferenceResult result = new InferenceResult();
for (final TypeVariable target : targets) {
final InferredValue equalityInferred = equalityResult.get(target);
final InferredValue supertypeInferred = supertypeResult.get(target);
final InferredValue outputValue;
if (equalityInferred != null && equalityInferred instanceof InferredType) {
if (supertypeInferred != null && supertypeInferred instanceof InferredType) {
AnnotatedTypeMirror superATM = ((InferredType) supertypeInferred).type;
AnnotatedTypeMirror equalityATM = ((InferredType) equalityInferred).type;
if (TypesUtils.isErasedSubtype(equalityATM.getUnderlyingType(), superATM.getUnderlyingType(), typeFactory.getContext().getTypeUtils())) {
// If the underlying type of equalityATM is a subtype of the underlying
// type of superATM, then the call to isSubtype below will issue an error.
// So call asSuper so that the isSubtype call below works correctly.
equalityATM = AnnotatedTypes.asSuper(typeFactory, equalityATM, superATM);
}
if (typeHierarchy.isSubtype(superATM, equalityATM)) {
outputValue = equalityInferred;
} else {
outputValue = supertypeInferred;
}
} else {
outputValue = equalityInferred;
}
} else {
if (supertypeInferred != null) {
outputValue = supertypeInferred;
} else {
outputValue = null;
}
}
if (outputValue != null) {
result.put(target, outputValue);
}
}
return result;
}
use of org.checkerframework.framework.util.typeinference.solver.InferenceResult 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.type.incompatible 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();
}
Aggregations