blob: 3fcc5f9c606cc5cfa81edcb66bfc84f1d8ac4ff1 [file] [log] [blame]
package org.checkerframework.framework.util.typeinference.solver;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.util.AnnotationMirrorSet;
import org.checkerframework.framework.util.typeinference.constraint.TIsU;
import org.checkerframework.framework.util.typeinference.constraint.TSuperU;
import org.checkerframework.framework.util.typeinference.constraint.TUConstraint;
import org.checkerframework.javacutil.TypeAnnotationUtils;
/** Converts a set of TUConstraints into a ConstraintMap. */
public class ConstraintMapBuilder {
/**
* Let Ti be a the ith target being inferred Let ATV(i) be the annotated type variable that
* represents as use of Ti which may or may not have primary annotations. Let ATM be an annotated
* type mirror that may or may not be target Tx, or have a component target Tx Let Ai be the type
* argument we are trying to infer for Ti
*
* <p>We have a set of constraints of the form: {@code ATV(i) <?> ATM}
*
* <p>Where {@code <?>} is either a subtype ({@code <:}), supertype ({@code :>}), or equality
* relationship ({@code =}).
*
* <p>Regardless of what {@code <?>} is, a constraint will only imply constraints on Ai in a given
* hierarchy if ATV(i) does NOT have a primary annotation in that hierarchy. That is:
*
* <p>E.g. Let ATV(i) be @NonNull Ti, the constraints @NonNull Ti = @NonNull @Initialized String
* does not imply any primary annotation in the Nullness hierarchy for type argument Ai because
* the Annotated type mirror has a primary annotation in the NUllness hierarchy.
*
* <p>However, it does imply that Ai has a primary annotation of @Initialized since ATV(i) has no
* primary annotation in the initialization hierarchy.
*
* <p>Note, constraints come in 2 forms:
*
* <ul>
* <li>between a target and a concrete AnnotatedTypeMirror. E.g., As seen above {@code (@NonNull
* Ti = @NonNull @Initialized String)}
* <li>between two targets E.g., {@code (@NonNull Ti = Tj)}
* </ul>
*/
public ConstraintMap build(
Set<TypeVariable> targets, Set<TUConstraint> constraints, AnnotatedTypeFactory typeFactory) {
final QualifierHierarchy qualifierHierarchy = typeFactory.getQualifierHierarchy();
final AnnotationMirrorSet tops =
new AnnotationMirrorSet(qualifierHierarchy.getTopAnnotations());
final ConstraintMap result = new ConstraintMap(targets);
final AnnotationMirrorSet tAnnos = new AnnotationMirrorSet();
final AnnotationMirrorSet uAnnos = new AnnotationMirrorSet();
final AnnotationMirrorSet hierarchiesInRelation = new AnnotationMirrorSet();
for (TUConstraint constraint : constraints) {
tAnnos.clear();
uAnnos.clear();
hierarchiesInRelation.clear();
final AnnotatedTypeVariable typeT = constraint.typeVariable;
final AnnotatedTypeMirror typeU = constraint.relatedType;
// If typeU is from an argument to the method, then treat typeU as an ordinary type even
// if it is a target type variable. This is for the case where the inferred type is the
// declared type parameter. For example,
// public <T> T get(T t) {
// return this.get(t);
// }
// The inferred type of T should be T.
if (!constraint.uIsArg
&& typeU.getKind() == TypeKind.TYPEVAR
&& targets.contains(
(TypeVariable) TypeAnnotationUtils.unannotatedType(typeU.getUnderlyingType()))) {
if (typeT.getAnnotations().isEmpty() && typeU.getAnnotations().isEmpty()) {
hierarchiesInRelation.addAll(tops);
} else {
for (AnnotationMirror top : tops) {
final AnnotationMirror tAnno = typeT.getAnnotationInHierarchy(top);
final AnnotationMirror uAnno = typeU.getAnnotationInHierarchy(top);
if (tAnno == null) {
if (uAnno == null) {
hierarchiesInRelation.add(top);
} else {
tAnnos.add(uAnno);
}
} else {
if (uAnno == null) {
uAnnos.add(tAnno);
} else {
// This tells us nothing, they both should be equal but either way
// we gain no information if both type vars have annotations
}
}
}
// If we have a case where Ti = @NonNull Tj we know that for the @Initialization hierarchy
// Ti = TJ and we know that for the @Nullable hierarchy Ti = @NonNull <some other type>.
// This step saves @NonNull annotation.
// This case also covers the case where i = j.
if (!tAnnos.isEmpty()) {
addToPrimaryRelationship(
(TypeVariable) TypeAnnotationUtils.unannotatedType(typeT.getUnderlyingType()),
constraint,
result,
tAnnos,
qualifierHierarchy);
}
if (!uAnnos.isEmpty()) {
addToPrimaryRelationship(
(TypeVariable) TypeAnnotationUtils.unannotatedType(typeU.getUnderlyingType()),
constraint,
result,
uAnnos,
qualifierHierarchy);
}
}
// This is the case where we have a relationship between two different targets (Ti
// <?> Tj and i != j)
if (!typeFactory.types.isSameType(
TypeAnnotationUtils.unannotatedType(typeT.getUnderlyingType()),
TypeAnnotationUtils.unannotatedType(typeU.getUnderlyingType()))) {
addToTargetRelationship(
(TypeVariable) TypeAnnotationUtils.unannotatedType(typeT.getUnderlyingType()),
(TypeVariable) TypeAnnotationUtils.unannotatedType(typeU.getUnderlyingType()),
result,
constraint,
hierarchiesInRelation);
}
} else {
for (AnnotationMirror top : tops) {
final AnnotationMirror tAnno = typeT.getAnnotationInHierarchy(top);
if (tAnno == null) {
hierarchiesInRelation.add(top);
}
}
addToTypeRelationship(
(TypeVariable) TypeAnnotationUtils.unannotatedType(typeT.getUnderlyingType()),
typeU,
result,
constraint,
hierarchiesInRelation);
}
}
return result;
}
private void addToTargetRelationship(
TypeVariable typeT,
TypeVariable typeU,
ConstraintMap result,
TUConstraint constraint,
AnnotationMirrorSet hierarchiesInRelation) {
if (constraint instanceof TIsU) {
result.addTargetEquality(typeT, typeU, hierarchiesInRelation);
} else if (constraint instanceof TSuperU) {
result.addTargetSupertype(typeT, typeU, hierarchiesInRelation);
} else {
result.addTargetSubtype(typeT, typeU, hierarchiesInRelation);
}
}
public void addToPrimaryRelationship(
TypeVariable typeVariable,
TUConstraint constraint,
ConstraintMap result,
AnnotationMirrorSet annotationMirrors,
QualifierHierarchy qualifierHierarchy) {
if (constraint instanceof TIsU) {
result.addPrimaryEqualities(typeVariable, qualifierHierarchy, annotationMirrors);
} else if (constraint instanceof TSuperU) {
result.addPrimarySupertype(typeVariable, qualifierHierarchy, annotationMirrors);
} else {
result.addPrimarySubtypes(typeVariable, qualifierHierarchy, annotationMirrors);
}
}
public void addToTypeRelationship(
TypeVariable target,
AnnotatedTypeMirror type,
ConstraintMap result,
TUConstraint constraint,
AnnotationMirrorSet hierarchies) {
if (constraint instanceof TIsU) {
result.addTypeEqualities(target, type, hierarchies);
} else if (constraint instanceof TSuperU) {
result.addTypeSupertype(target, type, hierarchies);
} else {
result.addTypeSubtype(target, type, hierarchies);
}
}
}