package org.checkerframework.framework.util.typeinference.solver; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeVariable; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.util.AnnotationMirrorSet; import org.checkerframework.framework.util.typeinference.solver.TargetConstraints.Equalities; import org.checkerframework.framework.util.typeinference.solver.TargetConstraints.Subtypes; import org.checkerframework.framework.util.typeinference.solver.TargetConstraints.Supertypes; /** * ConstraintMap holds simplified versions of the TUConstraints for ALL type variable for which we * are inferring an argument. The ConstraintMap is edited on the fly as the various solvers work * (unlike the AF/TU Constraints which are immutable). * * <p>This really consists of these things: * * <ol> * <li>a Map({@code target ⇒ constraints for target}) * <li>Methods to easily build up the constraints in the map * <li>A getter for the constraints of individual targets. * </ol> * * Note: This class, along with TargetConstraints, uses a lot of mutable state and few * setters/getters be careful. This choice was made as it makes the resulting code more readable. */ public class ConstraintMap { private final Map<TypeVariable, TargetConstraints> targetToRecords = new LinkedHashMap<>(); public ConstraintMap(Set<TypeVariable> targets) { for (final TypeVariable target : targets) { targetToRecords.put(target, new TargetConstraints(target)); } } public ConstraintMap(final ConstraintMap toCopy) { this.targetToRecords.putAll(toCopy.targetToRecords); } /** Gets the equality, subtypes, and supertypes constraints for a particular target */ public TargetConstraints getConstraints(final TypeVariable target) { return targetToRecords.get(target); } /** * @return the set of all targets passed to the constructor of this constraint map (a target * will appear in this list whether or not it has any constraints added) */ public Set<TypeVariable> getTargets() { return targetToRecords.keySet(); } /** * Add a constraint indicating that the equivalent is equal to target in the given qualifier * hierarchies */ public void addTargetEquality( final TypeVariable target, final TypeVariable equivalent, AnnotationMirrorSet hierarchies) { final Equalities equalities = targetToRecords.get(target).equalities; final AnnotationMirrorSet equivalentTops = equalities.targets.get(equivalent); if (equivalentTops == null) { equalities.targets.put(equivalent, new AnnotationMirrorSet(hierarchies)); } else { equivalentTops.addAll(hierarchies); } } /** * Add a constraint indicating that target has primary annotations equal to the given * annotations */ public void addPrimaryEqualities( final TypeVariable target, QualifierHierarchy qualHierarchy, final AnnotationMirrorSet annos) { final Equalities equalities = targetToRecords.get(target).equalities; for (final AnnotationMirror anno : annos) { final AnnotationMirror top = qualHierarchy.getTopAnnotation(anno); if (!equalities.primaries.containsKey(top)) { equalities.primaries.put(top, anno); } } } /** * Add a constraint indicating that target is a supertype of subtype in the given qualifier * hierarchies * * @param hierarchies a set of TOP annotations */ public void addTargetSupertype( final TypeVariable target, final TypeVariable subtype, AnnotationMirrorSet hierarchies) { final Supertypes supertypes = targetToRecords.get(target).supertypes; final AnnotationMirrorSet supertypeTops = supertypes.targets.get(subtype); if (supertypeTops == null) { supertypes.targets.put(subtype, new AnnotationMirrorSet(hierarchies)); } else { supertypeTops.addAll(hierarchies); } } /** * Add a constraint indicating that target is a supertype of subtype in the given qualifier * hierarchies * * @param hierarchies a set of TOP annotations */ public void addTypeSupertype( final TypeVariable target, final AnnotatedTypeMirror subtype, AnnotationMirrorSet hierarchies) { final Supertypes supertypes = targetToRecords.get(target).supertypes; final AnnotationMirrorSet supertypeTops = supertypes.types.get(subtype); if (supertypeTops == null) { supertypes.types.put(subtype, new AnnotationMirrorSet(hierarchies)); } else { supertypeTops.addAll(hierarchies); } } /** * Add a constraint indicating that target's primary annotations are subtypes of the given * annotations */ public void addPrimarySupertype( final TypeVariable target, QualifierHierarchy qualifierHierarchy, final AnnotationMirrorSet annos) { final Supertypes supertypes = targetToRecords.get(target).supertypes; for (final AnnotationMirror anno : annos) { final AnnotationMirror top = qualifierHierarchy.getTopAnnotation(anno); AnnotationMirrorSet entries = supertypes.primaries.get(top); if (entries == null) { entries = new AnnotationMirrorSet(); supertypes.primaries.put(top, entries); } entries.add(anno); } } /** * Add a constraint indicating that target is a subtype of supertype in the given qualifier * hierarchies * * @param hierarchies a set of TOP annotations */ public void addTargetSubtype( final TypeVariable target, final TypeVariable supertype, AnnotationMirrorSet hierarchies) { final Subtypes subtypes = targetToRecords.get(target).subtypes; final AnnotationMirrorSet subtypesTops = subtypes.targets.get(supertype); if (subtypesTops == null) { subtypes.targets.put(supertype, new AnnotationMirrorSet(hierarchies)); } else { subtypesTops.addAll(hierarchies); } } /** * Add a constraint indicating that target is a subtype of supertype in the given qualifier * hierarchies * * @param hierarchies a set of TOP annotations */ public void addTypeSubtype( final TypeVariable target, final AnnotatedTypeMirror supertype, AnnotationMirrorSet hierarchies) { final Subtypes subtypes = targetToRecords.get(target).subtypes; final AnnotationMirrorSet subtypesTops = subtypes.targets.get(supertype); if (subtypesTops == null) { subtypes.types.put(supertype, new AnnotationMirrorSet(hierarchies)); } else { subtypesTops.addAll(hierarchies); } } /** * Add a constraint indicating that target's primary annotations are subtypes of the given * annotations */ public void addPrimarySubtypes( final TypeVariable target, QualifierHierarchy qualifierHierarchy, final AnnotationMirrorSet annos) { final Subtypes subtypes = targetToRecords.get(target).subtypes; for (final AnnotationMirror anno : annos) { final AnnotationMirror top = qualifierHierarchy.getTopAnnotation(anno); AnnotationMirrorSet entries = subtypes.primaries.get(top); if (entries == null) { entries = new AnnotationMirrorSet(); subtypes.primaries.put(top, entries); } entries.add(anno); } } /** * Add a constraint indicating that target is equal to type in the given hierarchies * * @param hierarchies a set of TOP annotations */ public void addTypeEqualities( TypeVariable target, AnnotatedTypeMirror type, AnnotationMirrorSet hierarchies) { final Equalities equalities = targetToRecords.get(target).equalities; final AnnotationMirrorSet equalityTops = equalities.types.get(type); if (equalityTops == null) { equalities.types.put(type, new AnnotationMirrorSet(hierarchies)); } else { equalityTops.addAll(hierarchies); } } }