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; /** 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.getKind() == TypeKind.TYPEVAR && targets.contains(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( typeT.getUnderlyingType(), constraint, result, tAnnos, qualifierHierarchy); } if (!uAnnos.isEmpty()) { addToPrimaryRelationship( (TypeVariable) 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 (!typeT.getUnderlyingType().equals(typeU.getUnderlyingType())) { addToTargetRelationship( typeT.getUnderlyingType(), (TypeVariable) typeU.getUnderlyingType(), result, constraint, hierarchiesInRelation); } } else { for (AnnotationMirror top : tops) { final AnnotationMirror tAnno = typeT.getAnnotationInHierarchy(top); if (tAnno == null) { hierarchiesInRelation.add(top); } } addToTypeRelationship( 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); } } }