@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; }