package org.checkerframework.framework.util.typeinference; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNullType; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.type.TypeHierarchy; import org.checkerframework.framework.util.AnnotationMirrorMap; import org.checkerframework.framework.util.AnnotationMirrorSet; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.TypesUtils; /** A class used to determine the greatest lower bounds for a set of AnnotatedTypeMirrors. */ public class GlbUtil { /** * Note: This method can be improved for wildcards and type variables. * * @return the greatest lower bound of typeMirrors. If any of the type mirrors are incomparable, * use an AnnotatedNullType that will contain the greatest lower bounds of the primary * annotations of typeMirrors. */ public static AnnotatedTypeMirror glbAll( final Map<AnnotatedTypeMirror, AnnotationMirrorSet> typeMirrors, final AnnotatedTypeFactory typeFactory) { final QualifierHierarchy qualifierHierarchy = typeFactory.getQualifierHierarchy(); if (typeMirrors.isEmpty()) { return null; } // dtermine the greatest lower bounds for the primary annotations AnnotationMirrorMap<AnnotationMirror> glbPrimaries = new AnnotationMirrorMap<>(); for (Entry<AnnotatedTypeMirror, AnnotationMirrorSet> tmEntry : typeMirrors.entrySet()) { final AnnotationMirrorSet typeAnnoHierarchies = tmEntry.getValue(); final AnnotatedTypeMirror type = tmEntry.getKey(); for (AnnotationMirror top : typeAnnoHierarchies) { //TODO: When all of the typeMirrors are either wildcards or type variables than the greatest lower bound //TODO: should involve handling the bounds individually rather than using the effective annotation //TODO: We are doing this for expediency final AnnotationMirror typeAnno = type.getEffectiveAnnotationInHierarchy(top); final AnnotationMirror currentAnno = glbPrimaries.get(top); if (typeAnno != null && currentAnno != null) { glbPrimaries.put( top, qualifierHierarchy.greatestLowerBound(currentAnno, typeAnno)); } else if (typeAnno != null) { glbPrimaries.put(top, typeAnno); } } } final List<AnnotatedTypeMirror> glbTypes = new ArrayList<>(); // create a copy of all of the types and apply the glb primary annotation final AnnotationMirrorSet values = new AnnotationMirrorSet(glbPrimaries.values()); for (AnnotatedTypeMirror type : typeMirrors.keySet()) { if (type.getKind() != TypeKind.TYPEVAR || !qualifierHierarchy.isSubtype(type.getEffectiveAnnotations(), values)) { final AnnotatedTypeMirror copy = type.deepCopy(); copy.replaceAnnotations(values); glbTypes.add(copy); } else { // if the annotations came from the upper bound of this typevar // we do NOT want to place them as primary annotations (and destroy the // type vars lower bound) glbTypes.add(type); } } final TypeHierarchy typeHierarchy = typeFactory.getTypeHierarchy(); // sort placing supertypes first sortForGlb(glbTypes, typeFactory); // find the lowest type in the list that is not an AnnotatedNullType AnnotatedTypeMirror glbType = glbTypes.get(0); int index = 1; while (index < glbTypes.size()) { // avoid using null if possible, since constraints form the lower bound will often have NULL types if (glbType.getKind() != TypeKind.NULL) { glbType = glbTypes.get(index); } index += 1; } // if the lowest type is a subtype of all glbTypes then it is the GLB, otherwise // there are two types in glbTypes that are incomparable and we need to use bottom (AnnotatedNullType) boolean incomparable = false; for (final AnnotatedTypeMirror type : glbTypes) { if (!incomparable && type.getKind() != TypeKind.NULL && (!TypesUtils.isErasedSubtype( typeFactory.getContext().getTypeUtils(), glbType.getUnderlyingType(), type.getUnderlyingType()) || !typeHierarchy.isSubtype(glbType, type))) { incomparable = true; } } // we had two incomparable types in glbTypes if (incomparable) { return createBottom(typeFactory, glbType.getEffectiveAnnotations()); } return glbType; } /** Returns an AnnotatedNullType with the given annotations as primaries */ private static AnnotatedNullType createBottom( final AnnotatedTypeFactory typeFactory, final Set<? extends AnnotationMirror> annos) { return typeFactory.getAnnotatedNullType(annos); } /** * Sort the list of type mirrors, placing supertypes first and subtypes last. * * <p>E.g. the list: {@code ArrayList<String>, List<String>, AbstractList<String>} becomes: * {@code List<String>, AbstractList<String>, ArrayList<String>} */ public static void sortForGlb( final List<? extends AnnotatedTypeMirror> typeMirrors, final AnnotatedTypeFactory typeFactory) { final QualifierHierarchy qualifierHierarchy = typeFactory.getQualifierHierarchy(); final Types types = typeFactory.getProcessingEnv().getTypeUtils(); Collections.sort( typeMirrors, new Comparator<AnnotatedTypeMirror>() { @Override public int compare(AnnotatedTypeMirror type1, AnnotatedTypeMirror type2) { final TypeMirror underlyingType1 = type1.getUnderlyingType(); final TypeMirror underlyingType2 = type2.getUnderlyingType(); if (types.isSameType(underlyingType1, underlyingType2)) { return compareAnnotations(qualifierHierarchy, type1, type2); } if (types.isSubtype(underlyingType1, underlyingType2)) { return 1; } // if they're incomparable or type2 is a subtype of type1 return -1; } private int compareAnnotations( final QualifierHierarchy qualHierarchy, final AnnotatedTypeMirror type1, final AnnotatedTypeMirror type2) { if (AnnotationUtils.areSame( type1.getAnnotations(), type2.getAnnotations())) { return 0; } if (qualHierarchy.isSubtype( type1.getAnnotations(), type2.getAnnotations())) { return 1; } else { return -1; } } }); } }