@Override
    protected boolean isSubtypeTypeArguments(AnnotatedDeclaredType rhs, AnnotatedDeclaredType lhs) {
      List<AnnotatedTypeMirror> rhsTypeArgs = rhs.getTypeArguments();
      List<AnnotatedTypeMirror> lhsTypeArgs = lhs.getTypeArguments();

      if (rhsTypeArgs.isEmpty() || lhsTypeArgs.isEmpty()) return true;

      TypeElement lhsElem = (TypeElement) lhs.getUnderlyingType().asElement();
      // TypeElement rhsElem = (TypeElement) lhs.getUnderlyingType().asElement();
      // the following would be needed if Covariant were per type parameter
      // AnnotatedDeclaredType lhsDecl = currentATF.fromElement(lhsElem);
      // AnnotatedDeclaredType rhsDecl = currentATF.fromElement(rhsElem);
      // List<AnnotatedTypeMirror> lhsTVs = lhsDecl.getTypeArguments();
      // List<AnnotatedTypeMirror> rhsTVs = rhsDecl.getTypeArguments();

      int[] covarVals = null;
      if (lhsElem.getAnnotation(Covariant.class) != null) {
        covarVals = lhsElem.getAnnotation(Covariant.class).value();
      }

      if (lhsTypeArgs.size() != rhsTypeArgs.size()) {
        // This test fails e.g. for casts from a type with one type
        // argument to a type with two type arguments.
        // See test case nullness/generics/GenericsCasts
        // TODO: shouldn't the type be brought to a common type before
        // this?
        return true;
      }

      for (int i = 0; i < lhsTypeArgs.size(); ++i) {
        boolean covar = false;
        if (covarVals != null) {
          for (int cvv = 0; cvv < covarVals.length; ++cvv) {
            if (covarVals[cvv] == i) {
              covar = true;
            }
          }
        }

        if (covar) {
          if (!isSubtype(rhsTypeArgs.get(i), lhsTypeArgs.get(i))) return false;
        } else {
          if (!isSubtypeAsTypeArgument(rhsTypeArgs.get(i), lhsTypeArgs.get(i))) return false;
        }
      }

      return true;
    }
    @Override
    public Void visitDeclared(AnnotatedDeclaredType type, ElementKind p) {
      Element elt = type.getElement();
      if (!type.isAnnotated()
          && (
          /*checker.isDefaultAnyType(type) ||*/
          elt != null
              && ElementUtils.isStatic(elt)
              && (elt.getKind() == ElementKind.FIELD
                  || elt.getKind() == ElementKind.LOCAL_VARIABLE
                  || elt.getKind() == ElementKind.EXCEPTION_PARAMETER
                  || elt.getKind() == ElementKind.ENUM_CONSTANT
                  || elt.getKind() == ElementKind.PARAMETER))) {
        type.clearAnnotations();
        Set<AnnotationMirror> set = AnnotationUtils.createAnnotationSet();
        set.add(checker.ANY);
        if (elt != null && ElementUtils.isStatic(elt)) set.add(checker.PEER);

        annotateConstants(type, set);
      }
      return super.visitDeclared(type, p);
    }
  /** Tests whether t1 is a subtype of t2, with respect to IGJ Subtyping rules */
  @Override
  public boolean isSubtype(AnnotatedTypeMirror sup, AnnotatedTypeMirror sub) {
    // TODO: Check that they are up to the same base
    // Err... cannot handle type variables quite yet
    if (sup.getKind() == TypeKind.TYPEVAR
        || sup.getKind() == TypeKind.WILDCARD
        || sub.getKind() == TypeKind.TYPEVAR
        || sub.getKind() == TypeKind.WILDCARD) return true;

    AnnotatedTypes annoUtils = new AnnotatedTypes(env, factory);

    AnnotatedTypeMirror valueBaseType = annoUtils.asSuper(sub, sup);
    if (valueBaseType == null)
      // For now
      valueBaseType = sub;

    boolean isSubtype = isSubtypeOneLevel(sup, valueBaseType);
    boolean typeArgShouldBeSame = sup.hasAnnotation(MUTABLE);

    if ((sup.getKind() == TypeKind.DECLARED) && (valueBaseType.getKind() == TypeKind.DECLARED)) {
      AnnotatedDeclaredType supDecl = (AnnotatedDeclaredType) sup;
      AnnotatedDeclaredType subDecl = (AnnotatedDeclaredType) valueBaseType;

      //            if (supDecl.getTypeArguments().size() != subDecl.getTypeArguments().size())
      //                System.out.println("Investigate this " + supDecl + " " + subDecl);

      for (int i = 0;
          i < supDecl.getTypeArguments().size() && i < subDecl.getTypeArguments().size();
          ++i) {
        AnnotatedTypeMirror supArg = supDecl.getTypeArguments().get(i);
        AnnotatedTypeMirror subArg = subDecl.getTypeArguments().get(i);
        if (typeArgShouldBeSame) isSubtype &= isSameImmutability(supArg, subArg);
        else isSubtype &= isSubtype(supArg, subArg);
      }
    } else if ((sup.getKind() == TypeKind.ARRAY) && (valueBaseType.getKind() == TypeKind.ARRAY)) {
      AnnotatedArrayType supArr = (AnnotatedArrayType) sup;
      AnnotatedArrayType subArr = (AnnotatedArrayType) valueBaseType;
      if (typeArgShouldBeSame)
        isSubtype &= isSameImmutability(supArr.getComponentType(), subArr.getComponentType());
      else isSubtype &= isSubtype(supArr.getComponentType(), subArr.getComponentType());
    }

    return isSubtype;
  }
 @Override
 public final R visitDeclared(AnnotatedDeclaredType type, AnnotatedTypeMirror p) {
   assert p instanceof AnnotatedDeclaredType : p;
   R r = scan(type.getTypeArguments(), ((AnnotatedDeclaredType) p).getTypeArguments());
   return r;
 }