blob: 6784c4a1b09a693a620e1391c7a84ce25b439aa4 [file] [log] [blame]
package org.checkerframework.framework.util;
import java.lang.annotation.Annotation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.interning.qual.Interned;
import org.checkerframework.checker.nullness.qual.KeyFor;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
import org.checkerframework.checker.signature.qual.CanonicalName;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.framework.qual.AnnotatedFor;
import org.checkerframework.framework.qual.PolymorphicQualifier;
import org.checkerframework.framework.qual.SubtypeOf;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TypeSystemError;
import org.plumelib.util.StringsPlume;
/**
* This is the default implementation of {@link QualifierKindHierarchy}.
*
* <p>By default, the subtyping information and information about polymorphic qualifiers is read
* from meta-annotations on the annotation classes. This information is used to infer further
* information such as top and bottom qualifiers. Subclasses can override the following methods to
* change this behavior:
*
* <ul>
* <li>{@link #createQualifierKinds(Collection)}
* <li>{@link #createDirectSuperMap()}
* <li>{@link #initializePolymorphicQualifiers()}
* <li>{@link #initializeQualifierKindFields(Map)}
* <li>{@link #createLubsMap()}
* <li>{@link #createGlbsMap()}
* </ul>
*
* {@link DefaultQualifierKindHierarchy.DefaultQualifierKind} is the implementation used for {@link
* QualifierKind} by this class.
*/
@AnnotatedFor("nullness")
public class DefaultQualifierKindHierarchy implements QualifierKindHierarchy {
/**
* A mapping from canonical name of a qualifier class to the QualifierKind object representing
* that class.
*/
protected final Map<@Interned @CanonicalName String, DefaultQualifierKind> nameToQualifierKind;
/**
* A list of all {@link QualifierKind}s for this DefaultQualifierKindHierarchy, sorted in
* ascending order.
*/
protected final List<DefaultQualifierKind> qualifierKinds;
/** All the qualifier kinds that are the top qualifier in their hierarchy. */
private final Set<DefaultQualifierKind> tops;
/** All the qualifier kinds that are the bottom qualifier in their hierarchy. */
private final Set<DefaultQualifierKind> bottoms;
/**
* Holds the lub of qualifier kinds. {@code lubs.get(kind1).get(kind2)} returns the lub of kind1
* and kind2.
*/
private final Map<QualifierKind, Map<QualifierKind, QualifierKind>> lubs;
/**
* Holds the glb of qualifier kinds. {@code glbs.get(kind1).get(kind2)} returns the glb of kind1
* and kind2.
*/
private final Map<QualifierKind, Map<QualifierKind, QualifierKind>> glbs;
@Override
public Set<? extends QualifierKind> getTops() {
return tops;
}
@Override
public Set<? extends QualifierKind> getBottoms() {
return bottoms;
}
@Override
public @Nullable QualifierKind leastUpperBound(QualifierKind q1, QualifierKind q2) {
@SuppressWarnings("nullness:dereference.of.nullable") // All QualifierKinds are keys in lubs.
QualifierKind result = lubs.get(q1).get(q2);
return result;
}
@Override
public @Nullable QualifierKind greatestLowerBound(QualifierKind q1, QualifierKind q2) {
@SuppressWarnings("nullness:dereference.of.nullable") // All QualifierKinds are keys in glbs.
QualifierKind result = glbs.get(q1).get(q2);
return result;
}
@Override
public List<? extends QualifierKind> allQualifierKinds() {
return qualifierKinds;
}
@Override
public @Nullable QualifierKind getQualifierKind(@CanonicalName String name) {
return nameToQualifierKind.get(name);
}
/**
* Creates a {@link DefaultQualifierKindHierarchy}. Also, creates and initializes all its
* qualifier kinds.
*
* @param qualifierClasses all the classes of qualifiers supported by this hierarchy
*/
public DefaultQualifierKindHierarchy(Collection<Class<? extends Annotation>> qualifierClasses) {
this(qualifierClasses, null, null);
}
/**
* Creates a {@link DefaultQualifierKindHierarchy}. Also, creates and initializes all its
* qualifier kinds.
*
* <p>For some type systems, qualifiers may be added at run time, so the {@link SubtypeOf}
* meta-annotation on the bottom qualifier class cannot specify all other qualifiers. For those
* type systems, use this constructor. Otherwise, use {@link
* #DefaultQualifierKindHierarchy(Collection)}.
*
* @param qualifierClasses all the classes of qualifiers supported by this hierarchy
* @param bottom the bottom qualifier of this hierarchy
*/
public DefaultQualifierKindHierarchy(
Collection<Class<? extends Annotation>> qualifierClasses,
Class<? extends Annotation> bottom) {
this(qualifierClasses, bottom, null);
}
/**
* Private constructor that sets the bottom qualifier if {@code bottom} is nonnull.
*
* @param qualifierClasses all the classes of qualifiers supported by this hierarchy
* @param bottom the bottom qualifier of this hierarchy or null if bottom can be inferred from the
* meta-annotations
* @param voidParam void parameter to differentiate from {@link
* #DefaultQualifierKindHierarchy(Collection, Class)}
*/
private DefaultQualifierKindHierarchy(
Collection<Class<? extends Annotation>> qualifierClasses,
@Nullable Class<? extends Annotation> bottom,
@SuppressWarnings("UnusedVariable") Void voidParam) {
this.nameToQualifierKind = createQualifierKinds(qualifierClasses);
this.qualifierKinds = new ArrayList<>(nameToQualifierKind.values());
Collections.sort(qualifierKinds);
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap = createDirectSuperMap();
if (bottom != null) {
setBottom(bottom, directSuperMap);
}
this.tops = createTopsSet(directSuperMap);
this.bottoms = createBottomsSet(directSuperMap);
initializePolymorphicQualifiers();
initializeQualifierKindFields(directSuperMap);
this.lubs = createLubsMap();
this.glbs = createGlbsMap();
verifyHierarchy(directSuperMap);
}
/**
* Verifies that the {@link DefaultQualifierKindHierarchy} is a valid hierarchy.
*
* @param directSuperMap mapping from qualifier to its direct supertypes; used to verify that a
* polymorphic annotation does not have a {@link SubtypeOf} meta-annotation
* @throws TypeSystemError if the hierarchy isn't valid
*/
@RequiresNonNull({"this.qualifierKinds", "this.tops", "this.bottoms"})
protected void verifyHierarchy(
@UnderInitialization DefaultQualifierKindHierarchy this,
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap) {
for (DefaultQualifierKind qualifierKind : qualifierKinds) {
boolean isPoly = qualifierKind.isPoly();
boolean hasSubtypeOfAnno = directSuperMap.containsKey(qualifierKind);
if (isPoly && hasSubtypeOfAnno) {
// Polymorphic qualifiers with upper and lower bounds are currently not supported.
throw new TypeSystemError(
"AnnotatedTypeFactory: "
+ qualifierKind
+ " is polymorphic and specifies super qualifiers.%n"
+ "Remove the @PolymorphicQualifier or @SubtypeOf annotation from it.");
} else if (!isPoly && !hasSubtypeOfAnno) {
throw new TypeSystemError(
"AnnotatedTypeFactory: %s does not specify its super qualifiers.%n"
+ "Add an @SubtypeOf or @PolymorphicQualifier annotation to it,%n"
+ "or if it is an alias, exclude it from `createSupportedTypeQualifiers()`.",
qualifierKind);
} else if (isPoly) {
if (qualifierKind.top == null) {
throw new TypeSystemError(
"PolymorphicQualifier, %s, has to specify a type hierarchy in its"
+ " @PolymorphicQualifier meta-annotation, if more than one exists; top types:"
+ " [%s].",
qualifierKind, StringsPlume.join(", ", tops));
} else if (!tops.contains(qualifierKind.top)) {
throw new TypeSystemError(
"Polymorphic qualifier %s has invalid top %s. Top qualifiers: %s",
qualifierKind, qualifierKind.top, StringsPlume.join(", ", tops));
}
}
}
if (bottoms.size() != tops.size()) {
throw new TypeSystemError(
"Number of tops not equal to number of bottoms: Tops: [%s] Bottoms: [%s]",
StringsPlume.join(", ", tops), StringsPlume.join(", ", bottoms));
}
}
/**
* Creates all QualifierKind objects for the given qualifier classes and adds them to
* qualifierClassMap. This method does not initialize all fields in the {@link QualifierKind};
* that is done by {@link #initializeQualifierKindFields(Map)}.
*
* @param qualifierClasses classes of annotations that are type qualifiers
* @return a mapping from the canonical name of an annotation class to {@link QualifierKind}
*/
protected Map<@Interned @CanonicalName String, DefaultQualifierKind> createQualifierKinds(
@UnderInitialization DefaultQualifierKindHierarchy this,
Collection<Class<? extends Annotation>> qualifierClasses) {
TreeMap<@Interned @CanonicalName String, DefaultQualifierKind> nameToQualifierKind =
new TreeMap<>();
for (Class<? extends Annotation> clazz : qualifierClasses) {
@SuppressWarnings("interning") // uniqueness is tested immediately below
@Interned DefaultQualifierKind qualifierKind = new DefaultQualifierKind(clazz);
if (nameToQualifierKind.containsKey(qualifierKind.getName())) {
throw new TypeSystemError("Duplicate QualifierKind " + qualifierKind.getName());
}
nameToQualifierKind.put(qualifierKind.getName(), qualifierKind);
}
return Collections.unmodifiableMap(nameToQualifierKind);
}
/**
* Creates a mapping from a {@link QualifierKind} to a set of its direct super qualifier kinds.
* The direct super qualifier kinds do not contain the qualifier itself. This mapping is used to
* create the bottom set, to create the top set, and by {@link
* #initializeQualifierKindFields(Map)}.
*
* <p>This implementation uses the {@link SubtypeOf} meta-annotation. Subclasses may override this
* method to create the direct super map some other way.
*
* <p>Note that this method is called from the constructor when {@link #nameToQualifierKind} and
* {@link #qualifierKinds} are the only fields that have nonnull values. This method is not
* static, so it can be overridden by subclasses.
*
* @return a mapping from each {@link QualifierKind} to a set of its direct super qualifiers
*/
@RequiresNonNull({"this.nameToQualifierKind", "this.qualifierKinds"})
protected Map<DefaultQualifierKind, Set<DefaultQualifierKind>> createDirectSuperMap(
@UnderInitialization DefaultQualifierKindHierarchy this) {
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap = new TreeMap<>();
for (DefaultQualifierKind qualifierKind : qualifierKinds) {
SubtypeOf subtypeOfMetaAnno =
qualifierKind.getAnnotationClass().getAnnotation(SubtypeOf.class);
if (subtypeOfMetaAnno == null) {
// qualifierKind has no @SubtypeOf: it must be top or polymorphic
continue;
}
Set<DefaultQualifierKind> directSupers = new TreeSet<>();
for (Class<? extends Annotation> superClazz : subtypeOfMetaAnno.value()) {
String superName = QualifierKindHierarchy.annotationClassName(superClazz);
DefaultQualifierKind superQualifier = nameToQualifierKind.get(superName);
if (superQualifier == null) {
throw new TypeSystemError(
"%s @Subtype argument %s isn't in the hierarchy. Qualifiers: [%s]",
qualifierKind, superName, StringsPlume.join(", ", qualifierKinds));
}
directSupers.add(superQualifier);
}
directSuperMap.put(qualifierKind, directSupers);
}
return directSuperMap;
}
/**
* This method sets bottom to the given class and modifies {@code directSuperMap} to add all
* leaves to its super qualifier kinds. Leaves are qualifier kinds that are not super qualifier
* kinds of another qualifier kind and are not polymorphic.
*
* @param bottom the class of the bottom qualifier in the hierarchy
* @param directSuperMap a mapping from a {@link QualifierKind} to a set of its direct super
* qualifiers; side-effected by this method
*/
@RequiresNonNull({"this.nameToQualifierKind", "this.qualifierKinds"})
private void setBottom(
@UnderInitialization DefaultQualifierKindHierarchy this,
Class<? extends Annotation> bottom,
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap) {
DefaultQualifierKind bottomKind =
nameToQualifierKind.get(QualifierKindHierarchy.annotationClassName(bottom));
if (bottomKind == null) {
throw new TypeSystemError(
"QualifierKindHierarchy#setBottom: %s is not in the hierarchy",
bottom.getCanonicalName());
}
Set<DefaultQualifierKind> leaves = new TreeSet<>(qualifierKinds);
leaves.remove(bottomKind);
directSuperMap.forEach((sub, supers) -> leaves.removeAll(supers));
Set<DefaultQualifierKind> bottomDirectSuperQuals = directSuperMap.get(bottomKind);
if (bottomDirectSuperQuals == null) {
directSuperMap.put(bottomKind, leaves);
} else {
bottomDirectSuperQuals.addAll(leaves);
}
}
/**
* Creates the set of top {@link QualifierKind}s by searching {@code directSuperMap} for qualifier
* kinds without any direct super qualifier kinds.
*
* <p>Subclasses should override {@link #createDirectSuperMap} to change the tops and not this
* method, because other methods expect the directSuperMap to be complete.
*
* @param directSuperMap a mapping from a {@link QualifierKind} to a set of its direct super
* qualifier kinds; created by {@link #createDirectSuperMap()}
* @return the set of top {@link QualifierKind}s
*/
private Set<DefaultQualifierKind> createTopsSet(
@UnderInitialization DefaultQualifierKindHierarchy this,
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap) {
Set<DefaultQualifierKind> tops = new TreeSet<>();
directSuperMap.forEach(
(qualifierKind, superQuals) -> {
if (superQuals.isEmpty()) {
tops.add(qualifierKind);
}
});
return tops;
}
/**
* Creates the set of bottom {@link QualifierKind}s by searching {@code directSuperMap} for
* qualifiers that are not a direct super qualifier kind of another qualifier kind.
*
* <p>Subclasses should override {@link #createDirectSuperMap} or {@link #setBottom} to change the
* bottoms and not this method, because other methods expect the directSuperMap to be complete.
*
* @param directSuperMap a mapping from a {@link QualifierKind} to a set of its direct super
* qualifier kinds; created by {@link #createDirectSuperMap()}
* @return the set of bottom {@link QualifierKind}s
*/
private Set<DefaultQualifierKind> createBottomsSet(
@UnderInitialization DefaultQualifierKindHierarchy this,
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap) {
Set<DefaultQualifierKind> bottoms = new HashSet<>(directSuperMap.keySet());
for (Set<DefaultQualifierKind> superKinds : directSuperMap.values()) {
bottoms.removeAll(superKinds);
}
return bottoms;
}
/**
* Iterates over all the qualifier kinds and adds all polymorphic qualifier kinds to
* polymorphicQualifiers. Also sets {@link DefaultQualifierKind#poly} and {@link
* DefaultQualifierKind#top} for the polymorphic qualifiers, and sets {@link
* DefaultQualifierKind#poly} for the top qualifiers.
*
* <p>Requires that tops has been initialized.
*/
@RequiresNonNull({"this.nameToQualifierKind", "this.qualifierKinds", "this.tops"})
protected void initializePolymorphicQualifiers(
@UnderInitialization DefaultQualifierKindHierarchy this) {
for (DefaultQualifierKind qualifierKind : qualifierKinds) {
Class<? extends Annotation> clazz = qualifierKind.getAnnotationClass();
PolymorphicQualifier polyMetaAnno = clazz.getAnnotation(PolymorphicQualifier.class);
if (polyMetaAnno == null) {
continue;
}
qualifierKind.poly = qualifierKind;
String topName = QualifierKindHierarchy.annotationClassName(polyMetaAnno.value());
if (nameToQualifierKind.containsKey(topName)) {
qualifierKind.top = nameToQualifierKind.get(topName);
} else if (topName.equals(Annotation.class.getCanonicalName())) {
// Annotation.class is the default value of PolymorphicQualifier. If it is used,
// then there must be exactly one top.
if (tops.size() == 1) {
qualifierKind.top = tops.iterator().next();
} else {
throw new TypeSystemError(
"Polymorphic qualifier %s did not specify a top annotation class. Tops: [%s]",
qualifierKind, StringsPlume.join(", ", tops));
}
} else {
throw new TypeSystemError(
"Polymorphic qualifier %s's top, %s, is not a qualifier.", qualifierKind, topName);
}
qualifierKind.strictSuperTypes = Collections.singleton(qualifierKind.top);
qualifierKind.top.poly = qualifierKind;
}
}
/**
* For each qualifier kind in {@code directSuperMap}, initializes {@link
* DefaultQualifierKind#strictSuperTypes}, {@link DefaultQualifierKind#top}, {@link
* DefaultQualifierKind#bottom}, and {@link DefaultQualifierKind#poly}.
*
* <p>Requires tops, bottoms, and polymorphicQualifiers to be initialized.
*
* @param directSuperMap a mapping from a {@link QualifierKind} to a set of its direct super
* qualifier kinds; created by {@link #createDirectSuperMap()}
*/
@RequiresNonNull({"this.qualifierKinds", "this.tops", "this.bottoms"})
protected void initializeQualifierKindFields(
@UnderInitialization DefaultQualifierKindHierarchy this,
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap) {
for (DefaultQualifierKind qualifierKind : directSuperMap.keySet()) {
if (!qualifierKind.isPoly()) {
qualifierKind.strictSuperTypes = findAllTheSupers(qualifierKind, directSuperMap);
}
}
for (DefaultQualifierKind qualifierKind : qualifierKinds) {
for (DefaultQualifierKind top : tops) {
if (qualifierKind.isSubtypeOf(top)) {
if (qualifierKind.top == null) {
qualifierKind.top = top;
} else if (qualifierKind.top != top) {
throw new TypeSystemError(
"Multiple tops found for qualifier %s. Tops: %s and %s.",
qualifierKind, top, qualifierKind.top);
}
}
}
if (qualifierKind.top == null) {
throw new TypeSystemError(
"Qualifier %s isn't a subtype of any top. tops = %s", qualifierKind, tops);
}
qualifierKind.poly = qualifierKind.top.poly;
}
for (DefaultQualifierKind qualifierKind : qualifierKinds) {
for (DefaultQualifierKind bot : bottoms) {
if (bot.top != qualifierKind.top) {
continue;
}
if (qualifierKind.bottom == null) {
qualifierKind.bottom = bot;
} else if (qualifierKind.top != bot) {
throw new TypeSystemError(
"Multiple bottoms found for qualifier %s. Bottoms: %s and %s.",
qualifierKind, bot, qualifierKind.bottom);
}
if (qualifierKind.isPoly()) {
assert bot.strictSuperTypes != null
: "@AssumeAssertion(nullness): strictSuperTypes should be nonnull.";
bot.strictSuperTypes.add(qualifierKind);
}
}
if (qualifierKind.bottom == null) {
throw new TypeSystemError(
"Cannot find a bottom qualifier for %s. bottoms = %s", qualifierKind, bottoms);
}
}
}
/**
* Returns the set of all qualifier kinds that are a strict supertype of {@code qualifierKind}.
*
* @param qualifierKind the qualifier kind whose super types should be returned
* @param directSuperMap a mapping from a {@link QualifierKind} to a set of its direct super
* qualifier kinds; created by {@link #createDirectSuperMap()}
* @return the set of all qualifier kinds that are a strict supertype of {@code qualifierKind}
*/
private Set<QualifierKind> findAllTheSupers(
@UnderInitialization DefaultQualifierKindHierarchy this,
@KeyFor("#2") QualifierKind qualifierKind,
Map<DefaultQualifierKind, Set<DefaultQualifierKind>> directSuperMap) {
Set<QualifierKind> allSupers = new TreeSet<>(directSuperMap.get(qualifierKind));
// Visit every super qualifier kind and add its super qualifier kinds to allSupers.
Queue<DefaultQualifierKind> toVisit = new ArrayDeque<>(directSuperMap.get(qualifierKind));
Set<DefaultQualifierKind> visited = new HashSet<>();
while (!toVisit.isEmpty()) {
DefaultQualifierKind superQualKind = toVisit.remove();
if (superQualKind == qualifierKind) {
throw new TypeSystemError("Cycle in hierarchy: %s", qualifierKind);
}
if (!visited.add(superQualKind) || superQualKind.isPoly()) {
continue;
}
Set<DefaultQualifierKind> superSuperQuals = directSuperMap.get(superQualKind);
if (superSuperQuals == null) {
throw new TypeSystemError(superQualKind + " is not a key in the directSuperMap");
}
toVisit.addAll(superSuperQuals);
allSupers.addAll(superSuperQuals);
}
return allSupers;
}
/**
* Creates the lub of qualifier kinds. {@code lubs.get(kind1).get(kind2)} returns the lub of kind1
* and kind2.
*
* @return a mapping of lubs
*/
@RequiresNonNull("this.qualifierKinds")
protected Map<QualifierKind, Map<QualifierKind, QualifierKind>> createLubsMap(
@UnderInitialization DefaultQualifierKindHierarchy this) {
Map<QualifierKind, Map<QualifierKind, QualifierKind>> lubs = new HashMap<>();
for (QualifierKind qual1 : qualifierKinds) {
for (QualifierKind qual2 : qualifierKinds) {
if (qual1.getTop() != qual2.getTop()) {
continue;
}
QualifierKind lub = findLub(qual1, qual2);
addToMapOfMap(lubs, qual1, qual2, lub, "lub");
addToMapOfMap(lubs, qual2, qual1, lub, "lub");
}
}
return lubs;
}
/**
* Returns the least upper bound of {@code qual1} and {@code qual2}.
*
* @param qual1 a qualifier kind
* @param qual2 a qualifier kind
* @return the least upper bound of {@code qual1} and {@code qual2}
*/
private QualifierKind findLub(
@UnderInitialization DefaultQualifierKindHierarchy this,
QualifierKind qual1,
QualifierKind qual2) {
if (qual1 == qual2) {
return qual1;
} else if (qual1.isSubtypeOf(qual2)) {
return qual2;
} else if (qual2.isSubtypeOf(qual1)) {
return qual1;
}
Set<QualifierKind> allSuperTypes = new TreeSet<>(qual1.getStrictSuperTypes());
Set<? extends QualifierKind> qual2StrictSuperTypes = qual2.getStrictSuperTypes();
allSuperTypes.retainAll(qual2StrictSuperTypes);
Set<? extends QualifierKind> lubs = findLowestQualifiers(allSuperTypes);
if (lubs.size() != 1) {
throw new TypeSystemError(
"lub(%s, %s) should have size 1: [%s]", qual1, qual2, StringsPlume.join(", ", lubs));
}
QualifierKind lub = lubs.iterator().next();
if (lub.isPoly() && !qual1.isPoly() && !qual2.isPoly()) {
throw new TypeSystemError("lub(%s, %s) can't be poly: %s", qual1, qual2, lub);
}
return lub;
}
/**
* Returns the lowest qualifiers in the passed set.
*
* @param qualifierKinds a set of qualifiers
* @return the lowest qualifiers in the passed set
*/
protected static Set<QualifierKind> findLowestQualifiers(Set<QualifierKind> qualifierKinds) {
Set<QualifierKind> lowestQualifiers = new TreeSet<>(qualifierKinds);
for (QualifierKind a1 : qualifierKinds) {
lowestQualifiers.removeIf(a2 -> a1 != a2 && a1.isSubtypeOf(a2));
}
return lowestQualifiers;
}
/**
* Creates the glb of qualifier kinds. {@code glbs.get(kind1).get(kind2)} returns the glb of kind1
* and kind2.
*
* @return a mapping of glb
*/
@RequiresNonNull("this.qualifierKinds")
protected Map<QualifierKind, Map<QualifierKind, QualifierKind>> createGlbsMap(
@UnderInitialization DefaultQualifierKindHierarchy this) {
Map<QualifierKind, Map<QualifierKind, QualifierKind>> glbs = new TreeMap<>();
for (QualifierKind qual1 : qualifierKinds) {
for (QualifierKind qual2 : qualifierKinds) {
if (qual1.getTop() != qual2.getTop()) {
continue;
}
QualifierKind glb = findGlb(qual1, qual2);
addToMapOfMap(glbs, qual1, qual2, glb, "glb");
addToMapOfMap(glbs, qual2, qual1, glb, "glb");
}
}
return glbs;
}
/**
* Returns the greatest lower bound of {@code qual1} and {@code qual2}.
*
* @param qual1 a qualifier kind
* @param qual2 a qualifier kind
* @return the greatest lower bound of {@code qual1} and {@code qual2}
*/
@RequiresNonNull("this.qualifierKinds")
private QualifierKind findGlb(
@UnderInitialization DefaultQualifierKindHierarchy this,
QualifierKind qual1,
QualifierKind qual2) {
if (qual1 == qual2) {
return qual1;
} else if (qual1.isSubtypeOf(qual2)) {
return qual1;
} else if (qual2.isSubtypeOf(qual1)) {
return qual2;
}
Set<QualifierKind> allSubTypes = new TreeSet<>();
for (QualifierKind qualifierKind : qualifierKinds) {
if (qualifierKind.isSubtypeOf(qual1) && qualifierKind.isSubtypeOf(qual2)) {
allSubTypes.add(qualifierKind);
}
}
Set<QualifierKind> glbs = findHighestQualifiers(allSubTypes);
if (glbs.size() != 1) {
throw new TypeSystemError(
"glb(%s, %s) should have size 1: [%s]", qual1, qual2, StringsPlume.join(", ", glbs));
}
QualifierKind glb = glbs.iterator().next();
if (glb.isPoly() && !qual1.isPoly() && !qual2.isPoly()) {
throw new TypeSystemError("glb(%s, %s) can't be poly: %s", qual1, qual2, glb);
}
return glb;
}
/**
* Returns the highest qualifiers in the passed set.
*
* @param qualifierKinds a set of qualifiers
* @return the highest qualifiers in the passed set
*/
protected static Set<QualifierKind> findHighestQualifiers(Set<QualifierKind> qualifierKinds) {
Set<QualifierKind> highestQualifiers = new TreeSet<>(qualifierKinds);
for (QualifierKind a1 : qualifierKinds) {
highestQualifiers.removeIf(a2 -> a1 != a2 && a2.isSubtypeOf(a1));
}
return highestQualifiers;
}
/**
* Add Key: qual1, Value: (Key: qual2, Value: value) to {@code map}. If already in map, throw an
* exception if value is different.
*
* @param map mapping to side-effect
* @param qual1 the first qualifier kind
* @param qual2 the second qualifier kind
* @param value the value to add
* @param operationName "lub" or "glb"; used only for error messages
*/
private static void addToMapOfMap(
Map<QualifierKind, Map<QualifierKind, QualifierKind>> map,
QualifierKind qual1,
QualifierKind qual2,
QualifierKind value,
String operationName) {
Map<QualifierKind, QualifierKind> qual1Map = map.computeIfAbsent(qual1, k -> new HashMap<>());
QualifierKind existingValue = qual1Map.get(qual2);
if (existingValue == null) {
qual1Map.put(qual2, value);
} else {
if (existingValue != value) {
throw new TypeSystemError(
"Multiple %ss for qualifiers %s and %s. Found map %s and %s",
operationName, qual1, qual2, value, existingValue);
}
}
}
/**
* The default implementation of {@link QualifierKind}.
*
* <p>The fields in this class that refer to {@link QualifierKind}s are set when creating the
* {@link DefaultQualifierKindHierarchy}. So the getter methods for these fields should not be
* called until after the {@code DefaultQualifierKindHierarchy} is created.
*/
@AnnotatedFor("nullness")
public @Interned static class DefaultQualifierKind implements QualifierKind {
/** The canonical name of the annotation class of this. */
private final @Interned @CanonicalName String name;
/** The annotation class for this. */
private final Class<? extends Annotation> clazz;
/** True if the annotation class of this has annotation elements/arguments. */
private final boolean hasElements;
/** The top of the hierarchy to which this belongs. */
// Set while creating the QualifierKindHierarchy.
protected @MonotonicNonNull DefaultQualifierKind top;
/** The bottom of the hierarchy to which this belongs. */
// Set while creating the QualifierKindHierarchy.
protected @MonotonicNonNull DefaultQualifierKind bottom;
/** The polymorphic qualifier of the hierarchy to which this belongs. */
// Set while creating the QualifierKindHierarchy.
protected @Nullable DefaultQualifierKind poly;
/**
* All the qualifier kinds that are a strict super qualifier kind of this. Does not include this
* qualifier kind itself.
*/
// Set while creating the QualifierKindHierarchy.
protected @MonotonicNonNull Set<QualifierKind> strictSuperTypes;
/**
* Creates a {@link DefaultQualifierKind} for the given annotation class.
*
* @param clazz annotation class for a qualifier
*/
DefaultQualifierKind(Class<? extends Annotation> clazz) {
this.clazz = clazz;
this.hasElements = clazz.getDeclaredMethods().length != 0;
this.name = QualifierKindHierarchy.annotationClassName(clazz).intern();
this.poly = null;
}
@Override
public @Interned @CanonicalName String getName() {
return name;
}
@Override
public Class<? extends Annotation> getAnnotationClass() {
return clazz;
}
@Override
public QualifierKind getTop() {
if (top == null) {
throw new BugInCF(
"DefaultQualifierKindHierarchy#getTop: Top is null for QualifierKind %s."
+ " Don't call this method during initialization of DefaultQualifierKindHierarchy.",
name);
}
return top;
}
@Override
public boolean isTop() {
return this.top == this;
}
@Override
public QualifierKind getBottom() {
if (bottom == null) {
throw new BugInCF(
"DefaultQualifierKind#getBottom:Bottom is null for QualifierKind %s. Don't call this"
+ " method during initialization of DefaultQualifierKindHierarchy.",
name);
}
return bottom;
}
@Override
public boolean isBottom() {
return this.bottom == this;
}
@Override
public @Nullable QualifierKind getPolymorphic() {
return poly;
}
@Pure
@Override
public boolean isPoly() {
return this.poly == this;
}
@Override
public boolean hasElements() {
return hasElements;
}
@Override
public Set<? extends QualifierKind> getStrictSuperTypes() {
if (strictSuperTypes == null) {
throw new BugInCF(
"DefaultQualifierKind#getStrictSuperTypes: strictSuperTypes was null. Don't call this"
+ " method during initialization of DefaultQualifierKindHierarchy.");
}
return strictSuperTypes;
}
@Override
public boolean isInSameHierarchyAs(QualifierKind other) {
return this.top == other.getTop();
}
@Override
public boolean isSubtypeOf(QualifierKind superQualKind) {
if (strictSuperTypes == null) {
throw new BugInCF(
"DefaultQualifierKind#isSubtypeOf: strictSuperTypes was null. Don't call this method"
+ " during initialization of DefaultQualifierKindHierarchy.");
}
return this == superQualKind || strictSuperTypes.contains(superQualKind);
}
@Override
public String toString() {
return clazz.getSimpleName();
}
}
}