| package org.checkerframework.framework.type; |
| |
| import java.util.List; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.type.TypeVariable; |
| import javax.lang.model.util.Types; |
| import org.checkerframework.common.basetype.BaseTypeChecker; |
| import org.checkerframework.framework.qual.Covariant; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNullType; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedUnionType; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType; |
| import org.checkerframework.framework.type.visitor.AbstractAtmComboVisitor; |
| import org.checkerframework.framework.util.AnnotatedTypes; |
| import org.checkerframework.framework.util.AtmCombo; |
| import org.checkerframework.javacutil.AnnotationUtils; |
| import org.checkerframework.javacutil.BugInCF; |
| import org.checkerframework.javacutil.TreeUtils; |
| import org.checkerframework.javacutil.TypesUtils; |
| |
| /** |
| * Default implementation of TypeHierarchy that implements the JLS specification with minor |
| * deviations as outlined by the Checker Framework manual. Changes to the JLS include forbidding |
| * covariant array types, raw types, and allowing covariant type arguments depending on various |
| * options passed to DefaultTypeHierarchy. |
| * |
| * <p>Subtyping rules of the JLS can be found in <a |
| * href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10">section 4.10, |
| * "Subtyping"</a>. |
| * |
| * <p>Note: The visit methods of this class must be public but it is intended to be used through a |
| * TypeHierarchy interface reference which will only allow isSubtype to be called. Clients should |
| * not call the visit methods. |
| */ |
| public class DefaultTypeHierarchy extends AbstractAtmComboVisitor<Boolean, Void> |
| implements TypeHierarchy { |
| // used for processingEnvironment when needed |
| protected final BaseTypeChecker checker; |
| |
| protected final QualifierHierarchy qualifierHierarchy; |
| protected final StructuralEqualityComparer equalityComparer; |
| |
| protected final boolean ignoreRawTypes; |
| protected final boolean invariantArrayComponents; |
| |
| // TODO: Incorporate feedback from David/Suzanne |
| // IMPORTANT_NOTE: |
| // |
| // For MultigraphQualifierHierarchies, we check the subtyping relationship of each annotation |
| // hierarchy individually. This is done because when comparing a pair of type variables, |
| // sometimes you need to traverse and compare the bounds of two type variables. Other times it |
| // is incorrect to compare the bounds. These two cases can occur simultaneously when comparing |
| // two hierarchies at once. In this case, comparing both hierarchies simultaneously will lead |
| // to an error. More detail is given below. |
| // |
| // Recall, type variables may or may not have a primary annotation for each individual |
| // hierarchy. When comparing |
| // two type variables for a specific hierarchy we have five possible cases: |
| // case 1: only one of the type variables has a primary annotation |
| // case 2a: both type variables have primary annotations and they are uses of the same type |
| // parameter |
| // case 2b: both type variables have primary annotations and they are uses of different |
| // type parameters |
| // case 3a: neither type variable has a primary annotation and they are uses of the same |
| // type parameter |
| // case 3b: neither type variable has a primary annotation and they are uses of different |
| // type parameters |
| // |
| // Case 1, 2b, and 3b require us to traverse both type variables bounds to ensure that the |
| // subtype's upper bound is a subtype of the supertype's lower bound. Cases 2a requires only |
| // that we check that the primary annotation on the subtype is a subtype of the primary |
| // annotation on the supertype. In case 3a, we can just return true, since two |
| // non-primary-annotated uses of the same type parameter are equivalent. In this case it would |
| // be an error to check the bounds because the check would only return true when the bounds are |
| // exact but it should always return true. |
| // |
| // A problem occurs when, one hierarchy matches cases 1, 2b, or 3b and the other matches 3a. In |
| // the first set of cases we MUST check the type variables' bounds. In case 3a we MUST NOT |
| // check the bounds. e.g. |
| // |
| // Suppose I have a hierarchy with two tops @A1 and @B1. Let @A0 <: @A1 and @B0 <: @B1. |
| // @A1 T t1; T t2; |
| // t1 = t2; |
| // |
| // To typecheck "t1 = t2;" in the hierarchy topped by @A1, we need to descend into the bounds of |
| // t1 and t2 (where t1's bounds will be overridden by @A1). However, for hierarchy B we need |
| // only recognize that since neither variable has a primary annotation, the types are equivalent |
| // and no traversal is needed. If we tried to do these two actions simultaneously, in every |
| // visit and isSubtype call, we would have to check to see that the @B hierarchy has been |
| // handled and ignore those annotations. |
| // |
| // Solutions: |
| // We could handle this problem by keeping track of which hierarchies have already been taken |
| // care of. We could then check each hierarchy before making comparisons. But this would lead |
| // to complicated plumbing that would be hard to understand. |
| // The chosen solution is to only check one hierarchy at a time. One trade-off to this approach |
| // is that we have to re-traverse the types for each hierarchy being checked. |
| // |
| // The field currentTop identifies the hierarchy for which the types are currently being checked. |
| // Final note: all annotation comparisons are done via isPrimarySubtype, isBottom, and |
| // isAnnoSubtype in order to ensure that we first get the annotations in the hierarchy of |
| // currentTop before passing annotations to qualifierHierarchy. |
| /** The top annotation of the hierarchy currently being checked. */ |
| protected AnnotationMirror currentTop; |
| |
| /** Stores the result of isSubtype, if that result is true. */ |
| protected final SubtypeVisitHistory isSubtypeVisitHistory; |
| |
| /** |
| * Stores the result of {@link #areEqualInHierarchy(AnnotatedTypeMirror, AnnotatedTypeMirror)} for |
| * type arguments. Prevents infinite recursion on types that refer to themselves. (Stores both |
| * true and false results.) |
| */ |
| protected final StructuralEqualityVisitHistory areEqualVisitHistory; |
| |
| /** The Covariant.value field/element. */ |
| final ExecutableElement covariantValueElement; |
| |
| /** |
| * Creates a DefaultTypeHierarchy. |
| * |
| * @param checker the type-checker that is associated with this |
| * @param qualifierHierarchy the qualiifer hierarchy that is associated with this |
| * @param ignoreRawTypes whether to ignore raw types |
| * @param invariantArrayComponents whether to make array subtyping invariant with respect to array |
| * component types |
| */ |
| public DefaultTypeHierarchy( |
| final BaseTypeChecker checker, |
| final QualifierHierarchy qualifierHierarchy, |
| boolean ignoreRawTypes, |
| boolean invariantArrayComponents) { |
| this.checker = checker; |
| this.qualifierHierarchy = qualifierHierarchy; |
| this.isSubtypeVisitHistory = new SubtypeVisitHistory(); |
| this.areEqualVisitHistory = new StructuralEqualityVisitHistory(); |
| this.equalityComparer = createEqualityComparer(); |
| |
| this.ignoreRawTypes = ignoreRawTypes; |
| this.invariantArrayComponents = invariantArrayComponents; |
| |
| covariantValueElement = |
| TreeUtils.getMethod(Covariant.class, "value", 0, checker.getProcessingEnvironment()); |
| } |
| |
| /** |
| * Create the equality comparer. |
| * |
| * @return the equality comparer |
| */ |
| protected StructuralEqualityComparer createEqualityComparer() { |
| return new StructuralEqualityComparer(areEqualVisitHistory); |
| } |
| |
| /** |
| * Returns true if subtype {@literal <:} supertype. |
| * |
| * <p>This implementation iterates over all top annotations and invokes {@link |
| * #isSubtype(AnnotatedTypeMirror, AnnotatedTypeMirror, AnnotationMirror)}. Most type systems |
| * should not override this method, but instead override {@link #isSubtype(AnnotatedTypeMirror, |
| * AnnotatedTypeMirror, AnnotationMirror)} or some of the {@code visitXXX} methods. |
| * |
| * @param subtype expected subtype |
| * @param supertype expected supertype |
| * @return true if subtype is a subtype of supertype or equal to it |
| */ |
| @Override |
| public boolean isSubtype(final AnnotatedTypeMirror subtype, final AnnotatedTypeMirror supertype) { |
| for (final AnnotationMirror top : qualifierHierarchy.getTopAnnotations()) { |
| if (!isSubtype(subtype, supertype, top)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns true if {@code subtype <: supertype}, but only for the hierarchy of which {@code top} |
| * is the top. |
| * |
| * @param subtype expected subtype |
| * @param supertype expected supertype |
| * @param top the top of the hierarchy for which we want to make a comparison |
| * @return true if {@code subtype} is a subtype of, or equal to, {@code supertype} in the |
| * qualifier hierarchy whose top is {@code top} |
| */ |
| protected boolean isSubtype( |
| final AnnotatedTypeMirror subtype, |
| final AnnotatedTypeMirror supertype, |
| final AnnotationMirror top) { |
| assert top != null; |
| currentTop = top; |
| return AtmCombo.accept(subtype, supertype, null, this); |
| } |
| |
| /** |
| * Returns error message for the case when two types shouldn't be compared. |
| * |
| * @return error message for the case when two types shouldn't be compared |
| */ |
| @Override |
| protected String defaultErrorMessage( |
| final AnnotatedTypeMirror subtype, final AnnotatedTypeMirror supertype, final Void p) { |
| return "Incomparable types (" |
| + subtype |
| + ", " |
| + supertype |
| + ") visitHistory = " |
| + isSubtypeVisitHistory; |
| } |
| |
| /** |
| * Compare the primary annotations of {@code subtype} and {@code supertype}. Neither type can be |
| * missing annotations. |
| * |
| * @param subtype a type that might be a subtype (with respect to primary annotations) |
| * @param supertype a type that might be a supertype (with respect to primary annotations) |
| * @return true if the primary annotation on subtype {@literal <:} primary annotation on supertype |
| * for the current top. |
| */ |
| protected boolean isPrimarySubtype(AnnotatedTypeMirror subtype, AnnotatedTypeMirror supertype) { |
| final AnnotationMirror subtypeAnno = subtype.getAnnotationInHierarchy(currentTop); |
| final AnnotationMirror supertypeAnno = supertype.getAnnotationInHierarchy(currentTop); |
| if (checker.getTypeFactory().hasQualifierParameterInHierarchy(supertype, currentTop) |
| && checker.getTypeFactory().hasQualifierParameterInHierarchy(subtype, currentTop)) { |
| // If the types have a class qualifier parameter, the qualifiers must be equivalent. |
| return qualifierHierarchy.isSubtype(subtypeAnno, supertypeAnno) |
| && qualifierHierarchy.isSubtype(supertypeAnno, subtypeAnno); |
| } |
| |
| return qualifierHierarchy.isSubtype(subtypeAnno, supertypeAnno); |
| } |
| |
| /** |
| * Like {@link #isSubtype(AnnotatedTypeMirror, AnnotatedTypeMirror)}, but uses a cache to prevent |
| * infinite recursion on recursive types. |
| * |
| * @param subtype a type that may be a subtype |
| * @param supertype a type that may be a supertype |
| * @return true if subtype {@literal <:} supertype |
| */ |
| protected boolean isSubtypeCaching( |
| final AnnotatedTypeMirror subtype, final AnnotatedTypeMirror supertype) { |
| if (isSubtypeVisitHistory.contains(subtype, supertype, currentTop)) { |
| // visitHistory only contains pairs in a subtype relationship. |
| return true; |
| } |
| |
| boolean result = isSubtype(subtype, supertype, currentTop); |
| // The call to put has no effect if result is false. |
| isSubtypeVisitHistory.put(subtype, supertype, currentTop, result); |
| return result; |
| } |
| |
| /** |
| * Are all the types in {@code subtypes} a subtype of {@code supertype}? |
| * |
| * <p>The underlying type mirrors of {@code subtypes} must be subtypes of the underlying type |
| * mirror of {@code supertype}. |
| */ |
| protected boolean areAllSubtypes( |
| final Iterable<? extends AnnotatedTypeMirror> subtypes, final AnnotatedTypeMirror supertype) { |
| for (final AnnotatedTypeMirror subtype : subtypes) { |
| if (!isSubtype(subtype, supertype, currentTop)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| protected boolean areEqualInHierarchy( |
| final AnnotatedTypeMirror type1, final AnnotatedTypeMirror type2) { |
| return equalityComparer.areEqualInHierarchy(type1, type2, currentTop); |
| } |
| |
| /** |
| * A declared type is considered a supertype of another declared type only if all of the type |
| * arguments of the declared type "contain" the corresponding type arguments of the subtype. |
| * Containment is described in <a |
| * href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.5.1">JLS section |
| * 4.5.1 "Type Arguments of Parameterized Types"</a>. |
| * |
| * @param inside a type argument of the "subtype" |
| * @param outside a type argument of the "supertype" |
| * @param canBeCovariant whether or not type arguments are allowed to be covariant |
| * @return true if inside is contained by outside, or if canBeCovariant == true and {@code inside |
| * <: outside} |
| */ |
| protected boolean isContainedBy( |
| final AnnotatedTypeMirror inside, final AnnotatedTypeMirror outside, boolean canBeCovariant) { |
| |
| if (ignoreUninferredTypeArgument(inside) || ignoreUninferredTypeArgument(outside)) { |
| areEqualVisitHistory.put(inside, outside, currentTop, true); |
| return true; |
| } |
| |
| if (outside.getKind() != TypeKind.WILDCARD |
| && !TypesUtils.isCaptured(outside.getUnderlyingType())) { |
| if (canBeCovariant) { |
| return isSubtype(inside, outside, currentTop); |
| } |
| return areEqualInHierarchy(inside, outside); |
| } |
| |
| AnnotatedTypeMirror outsideUpperBound; |
| AnnotatedTypeMirror outsideLowerBound; |
| if (outside.getKind() == TypeKind.WILDCARD) { |
| outsideUpperBound = ((AnnotatedWildcardType) outside).getExtendsBound(); |
| outsideLowerBound = ((AnnotatedWildcardType) outside).getSuperBound(); |
| } else if (TypesUtils.isCaptured(outside.getUnderlyingType())) { |
| outsideUpperBound = ((AnnotatedTypeVariable) outside).getUpperBound(); |
| outsideLowerBound = ((AnnotatedTypeVariable) outside).getLowerBound(); |
| } else { |
| throw new BugInCF("Expected a wildcard or captured type variable, but found " + outside); |
| } |
| Boolean previousResult = areEqualVisitHistory.get(inside, outside, currentTop); |
| if (previousResult != null) { |
| return previousResult; |
| } |
| |
| areEqualVisitHistory.put(inside, outside, currentTop, true); |
| boolean result = |
| isContainedWildcard(inside, outside, outsideUpperBound, outsideLowerBound, canBeCovariant); |
| areEqualVisitHistory.put(inside, outside, currentTop, result); |
| return result; |
| } |
| |
| private boolean isContainedWildcard( |
| AnnotatedTypeMirror inside, |
| AnnotatedTypeMirror outside, |
| AnnotatedTypeMirror outsideUpperBound, |
| AnnotatedTypeMirror outsideLowerBound, |
| boolean canBeCovariant) { |
| |
| if (inside.equals(outside)) { |
| // If they are equal, outside always contains inside. |
| return true; |
| } |
| |
| if (inside.getKind() == TypeKind.WILDCARD) { |
| outsideUpperBound = |
| checker |
| .getTypeFactory() |
| .widenToUpperBound(outsideUpperBound, (AnnotatedWildcardType) inside); |
| } |
| while (outsideUpperBound.getKind() == TypeKind.WILDCARD) { |
| if (ignoreUninferredTypeArgument(outsideUpperBound)) { |
| return true; |
| } |
| outsideUpperBound = ((AnnotatedWildcardType) outsideUpperBound).getExtendsBound(); |
| } |
| |
| if (!TypesUtils.isErasedSubtype( |
| inside.getUnderlyingType(), |
| outsideUpperBound.getUnderlyingType(), |
| inside.atypeFactory.types)) { |
| // TODO: subtype is a wildcard that should have been captured, so just check the primary |
| // annotations. |
| AnnotationMirror subAnno = inside.getEffectiveAnnotationInHierarchy(currentTop); |
| AnnotationMirror superAnno = outsideUpperBound.getAnnotationInHierarchy(currentTop); |
| return qualifierHierarchy.isSubtype(subAnno, superAnno); |
| } |
| |
| AnnotatedTypeMirror castedInside = |
| AnnotatedTypes.castedAsSuper(inside.atypeFactory, inside, outsideUpperBound); |
| if (!isSubtypeCaching(castedInside, outsideUpperBound)) { |
| return false; |
| } |
| |
| if (outside.getKind() == TypeKind.WILDCARD && outsideLowerBound.getKind() == TypeKind.TYPEVAR) { |
| // tests/all-systems/Issue1991.java crashes without this. |
| return true; |
| } |
| return canBeCovariant || isSubtypeCaching(outsideLowerBound, inside); |
| } |
| |
| /** |
| * Returns true if {@code type} is an uninferred type argument and if the checker should not issue |
| * warnings about uninferred type arguments. |
| * |
| * @param type type to check |
| * @return true if {@code type} is an uninferred type argument and if the checker should not issue |
| * warnings about uninferred type arguments |
| */ |
| private boolean ignoreUninferredTypeArgument(AnnotatedTypeMirror type) { |
| return type.atypeFactory.ignoreUninferredTypeArguments |
| && type.getKind() == TypeKind.WILDCARD |
| && ((AnnotatedWildcardType) type).isUninferredTypeArgument(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // The rest of this file is the visitor methods. It is a lot of methods, one for each |
| // combination of types. |
| |
| // ------------------------------------------------------------------------ |
| // Arrays as subtypes |
| |
| @Override |
| public Boolean visitArray_Array( |
| AnnotatedArrayType subtype, AnnotatedArrayType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype) |
| && (invariantArrayComponents |
| ? areEqualInHierarchy(subtype.getComponentType(), supertype.getComponentType()) |
| : isSubtype(subtype.getComponentType(), supertype.getComponentType(), currentTop)); |
| } |
| |
| @Override |
| public Boolean visitArray_Declared( |
| AnnotatedArrayType subtype, AnnotatedDeclaredType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitArray_Null(AnnotatedArrayType subtype, AnnotatedNullType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitArray_Intersection( |
| AnnotatedArrayType subtype, AnnotatedIntersectionType supertype, Void p) { |
| return isSubtype( |
| AnnotatedTypes.castedAsSuper(subtype.atypeFactory, subtype, supertype), |
| supertype, |
| currentTop); |
| } |
| |
| @Override |
| public Boolean visitArray_Wildcard( |
| AnnotatedArrayType subtype, AnnotatedWildcardType supertype, Void p) { |
| return visitWildcardSupertype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Declared as subtype |
| @Override |
| public Boolean visitDeclared_Array( |
| AnnotatedDeclaredType subtype, AnnotatedArrayType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitDeclared_Declared( |
| AnnotatedDeclaredType subtype, AnnotatedDeclaredType supertype, Void p) { |
| if (!isPrimarySubtype(subtype, supertype)) { |
| return false; |
| } |
| AnnotatedTypeFactory factory = subtype.atypeFactory; |
| if (factory.ignoreUninferredTypeArguments |
| && (factory.containsUninferredTypeArguments(subtype) |
| || factory.containsUninferredTypeArguments(supertype))) { |
| // Calling castedAsSuper may cause the uninferredTypeArguments to be lost. So, just |
| // return true here. |
| return true; |
| } |
| AnnotatedDeclaredType subtypeAsSuper = |
| AnnotatedTypes.castedAsSuper(subtype.atypeFactory, subtype, supertype); |
| |
| if (isSubtypeVisitHistory.contains(subtypeAsSuper, supertype, currentTop)) { |
| return true; |
| } |
| |
| final boolean result = |
| visitTypeArgs(subtypeAsSuper, supertype, subtype.wasRaw(), supertype.wasRaw()); |
| isSubtypeVisitHistory.put(subtypeAsSuper, supertype, currentTop, result); |
| |
| return result; |
| } |
| |
| /** |
| * A helper class for visitDeclared_Declared. There are subtypes of DefaultTypeHierarchy that need |
| * to customize the handling of type arguments. This method provides a convenient extension point. |
| */ |
| protected boolean visitTypeArgs( |
| final AnnotatedDeclaredType subtype, |
| final AnnotatedDeclaredType supertype, |
| final boolean subtypeRaw, |
| final boolean supertypeRaw) { |
| |
| final boolean ignoreTypeArgs = ignoreRawTypes && (subtypeRaw || supertypeRaw); |
| |
| if (ignoreTypeArgs) { |
| return true; |
| } |
| |
| final List<? extends AnnotatedTypeMirror> subtypeTypeArgs = subtype.getTypeArguments(); |
| final List<? extends AnnotatedTypeMirror> supertypeTypeArgs = supertype.getTypeArguments(); |
| |
| if (subtypeTypeArgs.size() != supertypeTypeArgs.size()) { |
| return false; |
| } |
| if (subtypeTypeArgs.isEmpty()) { |
| return true; |
| } |
| |
| final TypeElement supertypeElem = (TypeElement) supertype.getUnderlyingType().asElement(); |
| AnnotationMirror covariantAnno = |
| supertype.atypeFactory.getDeclAnnotation(supertypeElem, Covariant.class); |
| |
| List<Integer> covariantArgIndexes = |
| (covariantAnno == null) |
| ? null |
| : AnnotationUtils.getElementValueArray( |
| covariantAnno, covariantValueElement, Integer.class); |
| |
| for (int i = 0; i < supertypeTypeArgs.size(); i++) { |
| final AnnotatedTypeMirror superTypeArg = supertypeTypeArgs.get(i); |
| final AnnotatedTypeMirror subTypeArg = subtypeTypeArgs.get(i); |
| final boolean covariant = covariantArgIndexes != null && covariantArgIndexes.contains(i); |
| |
| boolean result = isContainedBy(subTypeArg, superTypeArg, covariant); |
| |
| if (!result) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public Boolean visitDeclared_Intersection( |
| AnnotatedDeclaredType subtype, AnnotatedIntersectionType supertype, Void p) { |
| return visitIntersectionSupertype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitDeclared_Null( |
| AnnotatedDeclaredType subtype, AnnotatedNullType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitDeclared_Primitive( |
| AnnotatedDeclaredType subtype, AnnotatedPrimitiveType supertype, Void p) { |
| // We do an asSuper first because in some cases unboxing implies a more specific annotation |
| // e.g. @UnknownInterned Integer => @Interned int because primitives are always interned |
| final AnnotatedPrimitiveType subAsSuper = |
| AnnotatedTypes.castedAsSuper(subtype.atypeFactory, subtype, supertype); |
| if (subAsSuper == null) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| return isPrimarySubtype(subAsSuper, supertype); |
| } |
| |
| @Override |
| public Boolean visitDeclared_Typevar( |
| AnnotatedDeclaredType subtype, AnnotatedTypeVariable supertype, Void p) { |
| return visitTypevarSupertype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitDeclared_Union( |
| AnnotatedDeclaredType subtype, AnnotatedUnionType supertype, Void p) { |
| Types types = checker.getTypeUtils(); |
| for (AnnotatedDeclaredType supertypeAltern : supertype.getAlternatives()) { |
| if (TypesUtils.isErasedSubtype( |
| subtype.getUnderlyingType(), supertypeAltern.getUnderlyingType(), types) |
| && isSubtype(subtype, supertypeAltern, currentTop)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public Boolean visitDeclared_Wildcard( |
| AnnotatedDeclaredType subtype, AnnotatedWildcardType supertype, Void p) { |
| return visitWildcardSupertype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Intersection as subtype |
| @Override |
| public Boolean visitIntersection_Declared( |
| AnnotatedIntersectionType subtype, AnnotatedDeclaredType supertype, Void p) { |
| return visitIntersectionSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitIntersection_Primitive( |
| AnnotatedIntersectionType subtype, AnnotatedPrimitiveType supertype, Void p) { |
| for (AnnotatedTypeMirror subtypeBound : subtype.getBounds()) { |
| if (TypesUtils.isBoxedPrimitive(subtypeBound.getUnderlyingType()) |
| && isSubtype(subtypeBound, supertype, currentTop)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public Boolean visitIntersection_Intersection( |
| AnnotatedIntersectionType subtype, AnnotatedIntersectionType supertype, Void p) { |
| Types types = checker.getTypeUtils(); |
| for (AnnotatedTypeMirror subBound : subtype.getBounds()) { |
| for (AnnotatedTypeMirror superBound : supertype.getBounds()) { |
| if (TypesUtils.isErasedSubtype( |
| subBound.getUnderlyingType(), superBound.getUnderlyingType(), types) |
| && !isSubtype(subBound, superBound, currentTop)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public Boolean visitIntersection_Null( |
| AnnotatedIntersectionType subtype, AnnotatedNullType supertype, Void p) { |
| // this can occur through capture conversion/comparing bounds |
| for (AnnotatedTypeMirror bound : subtype.getBounds()) { |
| if (isPrimarySubtype(bound, supertype)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public Boolean visitIntersection_Typevar( |
| AnnotatedIntersectionType subtype, AnnotatedTypeVariable supertype, Void p) { |
| return visitIntersectionSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitIntersection_Wildcard( |
| AnnotatedIntersectionType subtype, AnnotatedWildcardType supertype, Void p) { |
| return visitIntersectionSubtype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Null as subtype |
| @Override |
| public Boolean visitNull_Array(AnnotatedNullType subtype, AnnotatedArrayType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitNull_Declared( |
| AnnotatedNullType subtype, AnnotatedDeclaredType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitNull_Typevar( |
| AnnotatedNullType subtype, AnnotatedTypeVariable supertype, Void p) { |
| return visitTypevarSupertype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitNull_Wildcard( |
| AnnotatedNullType subtype, AnnotatedWildcardType supertype, Void p) { |
| return visitWildcardSupertype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitNull_Null(AnnotatedNullType subtype, AnnotatedNullType supertype, Void p) { |
| // this can occur when comparing typevar lower bounds since they are usually null types |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitNull_Union(AnnotatedNullType subtype, AnnotatedUnionType supertype, Void p) { |
| for (AnnotatedDeclaredType supertypeAltern : supertype.getAlternatives()) { |
| if (isSubtype(subtype, supertypeAltern, currentTop)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public Boolean visitNull_Intersection( |
| AnnotatedNullType subtype, AnnotatedIntersectionType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitNull_Primitive( |
| AnnotatedNullType subtype, AnnotatedPrimitiveType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Primitive as subtype |
| @Override |
| public Boolean visitPrimitive_Declared( |
| AnnotatedPrimitiveType subtype, AnnotatedDeclaredType supertype, Void p) { |
| // see comment in visitDeclared_Primitive |
| final AnnotatedDeclaredType subAsSuper = |
| AnnotatedTypes.castedAsSuper(subtype.atypeFactory, subtype, supertype); |
| if (subAsSuper == null) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| return isPrimarySubtype(subAsSuper, supertype); |
| } |
| |
| @Override |
| public Boolean visitPrimitive_Primitive( |
| AnnotatedPrimitiveType subtype, AnnotatedPrimitiveType supertype, Void p) { |
| return isPrimarySubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitPrimitive_Intersection( |
| AnnotatedPrimitiveType subtype, AnnotatedIntersectionType supertype, Void p) { |
| return visitIntersectionSupertype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitPrimitive_Typevar( |
| AnnotatedPrimitiveType subtype, AnnotatedTypeVariable supertype, Void p) { |
| return AtmCombo.accept(subtype, supertype.getUpperBound(), null, this); |
| } |
| |
| @Override |
| public Boolean visitPrimitive_Wildcard( |
| AnnotatedPrimitiveType subtype, AnnotatedWildcardType supertype, Void p) { |
| if (supertype.atypeFactory.ignoreUninferredTypeArguments |
| && supertype.isUninferredTypeArgument()) { |
| return true; |
| } |
| // this can occur when passing a primitive to a method on a raw type (see test |
| // checker/tests/nullness/RawAndPrimitive.java). This can also occur because we don't box |
| // primitives when we should and don't capture convert. |
| return isPrimarySubtype(subtype, supertype.getSuperBound()); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Union as subtype |
| @Override |
| public Boolean visitUnion_Declared( |
| AnnotatedUnionType subtype, AnnotatedDeclaredType supertype, Void p) { |
| return visitUnionSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitUnion_Intersection( |
| AnnotatedUnionType subtype, AnnotatedIntersectionType supertype, Void p) { |
| // For example: |
| // <T extends Throwable & Cloneable> void method(T param) {} |
| // ... |
| // catch (Exception1 | Exception2 union) { // Assuming Exception1 and Exception2 implement |
| // Cloneable |
| // method(union); |
| // This case happens when checking that the inferred type argument is a subtype of the |
| // declared type argument of method. |
| // See org.checkerframework.common.basetype.BaseTypeVisitor#checkTypeArguments |
| return visitUnionSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitUnion_Union( |
| AnnotatedUnionType subtype, AnnotatedUnionType supertype, Void p) { |
| // For example: |
| // <T> void method(T param) {} |
| // ... |
| // catch (Exception1 | Exception2 union) { |
| // method(union); |
| // This case happens when checking the arguments to method after type variable substitution |
| return visitUnionSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitUnion_Wildcard( |
| AnnotatedUnionType subtype, AnnotatedWildcardType supertype, Void p) { |
| // For example: |
| // } catch (RuntimeException | IOException e) { |
| // ArrayList<? super Exception> lWildcard = new ArrayList<>(); |
| // lWildcard.add(e); |
| |
| return visitWildcardSupertype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // typevar as subtype |
| @Override |
| public Boolean visitTypevar_Declared( |
| AnnotatedTypeVariable subtype, AnnotatedDeclaredType supertype, Void p) { |
| return visitTypevarSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitTypevar_Intersection( |
| AnnotatedTypeVariable subtype, AnnotatedIntersectionType supertype, Void p) { |
| // this can happen when checking type param bounds |
| return visitIntersectionSupertype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitTypevar_Primitive( |
| AnnotatedTypeVariable subtype, AnnotatedPrimitiveType supertype, Void p) { |
| return visitTypevarSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitTypevar_Typevar( |
| AnnotatedTypeVariable subtype, AnnotatedTypeVariable supertype, Void p) { |
| |
| if (AnnotatedTypes.haveSameDeclaration(checker.getTypeUtils(), subtype, supertype)) { |
| // subtype and supertype are uses of the same type parameter |
| boolean subtypeHasAnno = subtype.getAnnotationInHierarchy(currentTop) != null; |
| boolean supertypeHasAnno = supertype.getAnnotationInHierarchy(currentTop) != null; |
| |
| if (subtypeHasAnno && supertypeHasAnno) { |
| // if both have primary annotations then you can just check the primary annotations |
| // as the bounds are the same |
| return isPrimarySubtype(subtype, supertype); |
| |
| } else if (!subtypeHasAnno && !supertypeHasAnno && areEqualInHierarchy(subtype, supertype)) { |
| // two unannotated uses of the same type parameter are of the same type |
| return true; |
| |
| } else if (subtype.getUpperBound().getKind() == TypeKind.INTERSECTION) { |
| // This case happens when a type has an intersection bound. e.g., |
| // T extends A & B |
| // |
| // And one use of the type has an annotation and the other does not. e.g., |
| // @X T xt = ...; T t = ..; |
| // xt = t; |
| // |
| return visit(subtype.getUpperBound(), supertype.getLowerBound(), null); |
| } |
| } |
| |
| if (AnnotatedTypes.areCorrespondingTypeVariables( |
| checker.getProcessingEnvironment().getElementUtils(), subtype, supertype)) { |
| if (areEqualInHierarchy(subtype, supertype)) { |
| return true; |
| } |
| } |
| |
| // check that the upper bound of the subtype is below the lower bound of the supertype |
| return visitTypevarSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitTypevar_Null( |
| AnnotatedTypeVariable subtype, AnnotatedNullType supertype, Void p) { |
| return visitTypevarSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitTypevar_Wildcard( |
| AnnotatedTypeVariable subtype, AnnotatedWildcardType supertype, Void p) { |
| return visitWildcardSupertype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // wildcard as subtype |
| |
| @Override |
| public Boolean visitWildcard_Array( |
| AnnotatedWildcardType subtype, AnnotatedArrayType supertype, Void p) { |
| return visitWildcardSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitWildcard_Declared( |
| AnnotatedWildcardType subtype, AnnotatedDeclaredType supertype, Void p) { |
| if (subtype.isUninferredTypeArgument()) { |
| if (subtype.atypeFactory.ignoreUninferredTypeArguments) { |
| return true; |
| } else if (supertype.getTypeArguments().isEmpty()) { |
| // visitWildcardSubtype doesn't check uninferred type arguments, because the |
| // underlying Java types may not be in the correct relationship. But, if the |
| // declared type does not have type arguments, then checking primary annotations is |
| // sufficient. |
| // For example, if the wildcard is ? extends @Nullable Object and the supertype is |
| // @Nullable String, then it is safe to return true. However if the supertype is |
| // @NullableList<@NonNull String> then it's not possible to decide if it is a |
| // subtype of the wildcard. |
| AnnotationMirror subtypeAnno = subtype.getEffectiveAnnotationInHierarchy(currentTop); |
| AnnotationMirror supertypeAnno = supertype.getAnnotationInHierarchy(currentTop); |
| return qualifierHierarchy.isSubtype(subtypeAnno, supertypeAnno); |
| } |
| } |
| return visitWildcardSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitWildcard_Intersection( |
| AnnotatedWildcardType subtype, AnnotatedIntersectionType supertype, Void p) { |
| return visitWildcardSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitWildcard_Primitive( |
| AnnotatedWildcardType subtype, AnnotatedPrimitiveType supertype, Void p) { |
| if (subtype.isUninferredTypeArgument()) { |
| AnnotationMirror subtypeAnno = subtype.getEffectiveAnnotationInHierarchy(currentTop); |
| AnnotationMirror supertypeAnno = supertype.getAnnotationInHierarchy(currentTop); |
| return qualifierHierarchy.isSubtype(subtypeAnno, supertypeAnno); |
| } |
| return visitWildcardSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitWildcard_Typevar( |
| AnnotatedWildcardType subtype, AnnotatedTypeVariable supertype, Void p) { |
| return visitWildcardSubtype(subtype, supertype); |
| } |
| |
| @Override |
| public Boolean visitWildcard_Wildcard( |
| AnnotatedWildcardType subtype, AnnotatedWildcardType supertype, Void p) { |
| return visitWildcardSubtype(subtype, supertype); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // These "visit" methods are utility methods that aren't part of the visit interface |
| // but that handle cases that more than one visit method shares in common. |
| |
| /** |
| * An intersection is a supertype if all of its bounds are a supertype of subtype. |
| * |
| * @param subtype the possible subtype |
| * @param supertype the possible supertype |
| * @return true {@code subtype} is a subtype of {@code supertype} |
| */ |
| protected boolean visitIntersectionSupertype( |
| AnnotatedTypeMirror subtype, AnnotatedIntersectionType supertype) { |
| if (isSubtypeVisitHistory.contains(subtype, supertype, currentTop)) { |
| return true; |
| } |
| boolean result = true; |
| for (AnnotatedTypeMirror bound : supertype.getBounds()) { |
| // Only call isSubtype if the Java type is actually a subtype; otherwise, |
| // only check primary qualifiers. |
| if (TypesUtils.isErasedSubtype( |
| subtype.getUnderlyingType(), bound.getUnderlyingType(), subtype.atypeFactory.types) |
| && !isSubtype(subtype, bound, currentTop)) { |
| result = false; |
| break; |
| } |
| } |
| isSubtypeVisitHistory.put(subtype, supertype, currentTop, result); |
| return result; |
| } |
| |
| /** |
| * An intersection is a subtype if one of its bounds is a subtype of {@code supertype}. |
| * |
| * @param subtype an intersection type |
| * @param supertype an annotated type |
| * @return whether {@code subtype} is a subtype of {@code supertype} |
| */ |
| protected boolean visitIntersectionSubtype( |
| AnnotatedIntersectionType subtype, AnnotatedTypeMirror supertype) { |
| Types types = checker.getTypeUtils(); |
| // The primary annotations of the bounds should already be the same as the annotations on |
| // the intersection type. |
| for (AnnotatedTypeMirror subtypeBound : subtype.getBounds()) { |
| if (TypesUtils.isErasedSubtype( |
| subtypeBound.getUnderlyingType(), supertype.getUnderlyingType(), types) |
| && isSubtype(subtypeBound, supertype, currentTop)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * A type variable is a supertype if its lower bound is above subtype. |
| * |
| * @param subtype a type that might be a subtype |
| * @param supertype a type that might be a supertype |
| * @return true if {@code subtype} is a subtype of {@code supertype} |
| */ |
| protected boolean visitTypevarSupertype( |
| AnnotatedTypeMirror subtype, AnnotatedTypeVariable supertype) { |
| return isSubtypeCaching(subtype, supertype.getLowerBound()); |
| } |
| |
| /** |
| * A type variable is a subtype if its upper bounds is below the supertype. Note: When comparing |
| * two type variables this method and visitTypevarSupertype will combine to isValid the subtypes |
| * upper bound against the supertypes lower bound. |
| */ |
| protected boolean visitTypevarSubtype( |
| AnnotatedTypeVariable subtype, AnnotatedTypeMirror supertype) { |
| AnnotatedTypeMirror upperBound = subtype.getUpperBound(); |
| if (TypesUtils.isBoxedPrimitive(upperBound.getUnderlyingType()) |
| && supertype instanceof AnnotatedPrimitiveType) { |
| upperBound = supertype.atypeFactory.getUnboxedType((AnnotatedDeclaredType) upperBound); |
| } |
| if (supertype.getKind() == TypeKind.DECLARED |
| && TypesUtils.getTypeElement(supertype.getUnderlyingType()).getKind() |
| == ElementKind.INTERFACE) { |
| // Make sure the upper bound is no wildcard or type variable |
| while (upperBound.getKind() == TypeKind.TYPEVAR |
| || upperBound.getKind() == TypeKind.WILDCARD) { |
| if (upperBound.getKind() == TypeKind.TYPEVAR) { |
| upperBound = ((AnnotatedTypeVariable) upperBound).getUpperBound(); |
| } |
| if (upperBound.getKind() == TypeKind.WILDCARD) { |
| upperBound = ((AnnotatedWildcardType) upperBound).getExtendsBound(); |
| } |
| } |
| // If the supertype is an interface, only compare the primary annotations. |
| // The actual type argument could implement the interface and the bound of |
| // the type variable must not implement the interface. |
| if (upperBound.getKind() == TypeKind.INTERSECTION) { |
| Types types = checker.getTypeUtils(); |
| for (AnnotatedTypeMirror ub : ((AnnotatedIntersectionType) upperBound).getBounds()) { |
| if (TypesUtils.isErasedSubtype( |
| ub.getUnderlyingType(), supertype.getUnderlyingType(), types) |
| && isPrimarySubtype(ub, supertype)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| return isSubtypeCaching(upperBound, supertype); |
| } |
| |
| /** A union type is a subtype if ALL of its alternatives are subtypes of supertype. */ |
| protected boolean visitUnionSubtype(AnnotatedUnionType subtype, AnnotatedTypeMirror supertype) { |
| return areAllSubtypes(subtype.getAlternatives(), supertype); |
| } |
| |
| protected boolean visitWildcardSupertype( |
| AnnotatedTypeMirror subtype, AnnotatedWildcardType supertype) { |
| if (supertype.isUninferredTypeArgument()) { // TODO: REMOVE WHEN WE FIX TYPE ARG INFERENCE |
| // Can't call isSubtype because underlying Java types won't be subtypes. |
| return supertype.atypeFactory.ignoreUninferredTypeArguments; |
| } |
| return isSubtype(subtype, supertype.getSuperBound(), currentTop); |
| } |
| |
| protected boolean visitWildcardSubtype( |
| AnnotatedWildcardType subtype, AnnotatedTypeMirror supertype) { |
| if (subtype.isUninferredTypeArgument()) { |
| return subtype.atypeFactory.ignoreUninferredTypeArguments; |
| } |
| TypeMirror superTypeMirror = supertype.getUnderlyingType(); |
| if (supertype.getKind() == TypeKind.TYPEVAR) { |
| TypeVariable atv = (TypeVariable) supertype.getUnderlyingType(); |
| if (TypesUtils.isCaptured(atv)) { |
| superTypeMirror = TypesUtils.getCapturedWildcard(atv); |
| } |
| } |
| |
| if (superTypeMirror.getKind() == TypeKind.WILDCARD) { |
| // This can happen at a method invocation where a type variable in the method |
| // declaration is substituted with a wildcard. |
| // For example: |
| // <T> void method(Gen<T> t) {} |
| // Gen<?> x; |
| // method(x); // this method is called when checking this method call |
| // And also when checking lambdas |
| |
| boolean subtypeHasAnno = subtype.getAnnotationInHierarchy(currentTop) != null; |
| boolean supertypeHasAnno = supertype.getAnnotationInHierarchy(currentTop) != null; |
| |
| if (subtypeHasAnno && supertypeHasAnno) { |
| // if both have primary annotations then just check the primary annotations |
| // as the bounds are the same |
| return isPrimarySubtype(subtype, supertype); |
| |
| } else if (!subtypeHasAnno && !supertypeHasAnno && areEqualInHierarchy(subtype, supertype)) { |
| // TODO: wildcard capture conversion |
| // Two unannotated uses of wildcard types are the same type |
| return true; |
| } |
| } |
| |
| if (TypesUtils.isErasedSubtype( |
| subtype.getExtendsBound().getUnderlyingType(), |
| supertype.getUnderlyingType(), |
| subtype.atypeFactory.types)) { |
| return isSubtype(subtype.getExtendsBound(), supertype, currentTop); |
| } |
| // TODO: subtype is a wildcard that should have been captured, so just check the primary |
| // annotations. |
| AnnotationMirror subAnno = subtype.getEffectiveAnnotationInHierarchy(currentTop); |
| AnnotationMirror superAnno = supertype.getAnnotationInHierarchy(currentTop); |
| return qualifierHierarchy.isSubtype(subAnno, superAnno); |
| } |
| } |