public boolean isSubtypeOneLevel(AnnotatedTypeMirror sup, AnnotatedTypeMirror sub) {
    // Current implementation only need to deal with one level
    // 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;

    // TODO: Need to handle Generics and Arrays
    if (sup.hasAnnotation(READONLY)
        || sup.hasAnnotation(PLACE_HOLDER)
        || sub.hasAnnotation(PLACE_HOLDER)) return true;
    else if (sup.hasAnnotation(I)) {
      // t1 is a subtype of t2, if and only they have the same
      // immutability argument
      if (!sub.hasAnnotation(I)) return false;

      AnnotationUtils annoUtils = new AnnotationUtils(this.env);
      String t1Arg =
          annoUtils.parseStringValue(
              sub.getAnnotation(I.class.getCanonicalName()), IMMUTABILITY_KEY);
      String t2Arg =
          annoUtils.parseStringValue(
              sup.getAnnotation(I.class.getCanonicalName()), IMMUTABILITY_KEY);

      return ((t1Arg != null) && (t2Arg != null) && t1Arg.equals(t2Arg));
    }

    return ((sup.hasAnnotation(IMMUTABLE) && sub.hasAnnotation(IMMUTABLE))
        || (sup.hasAnnotation(MUTABLE) && sub.hasAnnotation(MUTABLE))
        || (sup.hasAnnotation(ASSIGNS_FIELDS) && sub.hasAnnotation(ASSIGNS_FIELDS))
        || (sup.hasAnnotation(ASSIGNS_FIELDS) && sub.hasAnnotation(MUTABLE)));
  }
  public boolean isSameImmutability(AnnotatedTypeMirror sup, AnnotatedTypeMirror sub) {
    // 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;

    if (sup.hasAnnotation(PLACE_HOLDER) || sub.hasAnnotation(PLACE_HOLDER)) return true;
    else if (sup.hasAnnotation(READONLY)) return sub.hasAnnotation(READONLY);
    else if (sup.hasAnnotation(MUTABLE)) return sub.hasAnnotation(MUTABLE);
    else if (sup.hasAnnotation(IMMUTABLE)) return sub.hasAnnotation(IMMUTABLE);

    return false;
  }
 @Override
 public final boolean isSubtype(AnnotatedTypeMirror rhs, AnnotatedTypeMirror lhs) {
   if (lhs.getKind() == TypeKind.TYPEVAR && rhs.getKind() == TypeKind.TYPEVAR) {
     // TODO: Investigate whether there is a nicer and more proper way to
     // get assignments between two type variables working.
     if (lhs.getAnnotations().isEmpty()) {
       return true;
     }
   }
   // Otherwise Covariant would cause trouble.
   if (rhs.hasAnnotation(KeyForBottom.class)) {
     return true;
   }
   return super.isSubtype(rhs, lhs);
 }
 @Override
 public AnnotatedTypeMirror getAnnotatedType(Tree tree) {
   AnnotatedTypeMirror type = super.getAnnotatedType(tree);
   if (tree instanceof ExpressionTree) tree = TreeUtils.skipParens((ExpressionTree) tree);
   Element elt = InternalUtils.symbol(tree);
   if (!checker.isChecking() /*&& !checker.isDefaultAnyType(type)*/
       && type.isAnnotated()
       && !type.getKind().isPrimitive()
       && type.getKind() != TypeKind.NULL
       && (elt == null || !ElementUtils.isStatic(elt))) {
     // We don't want annotations on the following trees
     Kind kind = tree.getKind();
     if (kind == Kind.METHOD_INVOCATION
         || kind == Kind.ARRAY_ACCESS
         || kind == Kind.MEMBER_SELECT
         || kind == Kind.CONDITIONAL_EXPRESSION) {
       type = type.getCopy(false);
     }
   }
   return type;
 }
  /** 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;
  }