Skip to content

Supertype lookups (findSupertype()) on a self-reference (ResolvedRecursiveType) return null #137

Closed

Description

@cowtowncoder

A self-reference (ResolvedRecursiveType) itself still exposes no supertypes: getParentClass() returns null and getImplementedInterfaces() an empty list (to avoid infinite loops). So findSupertype() / typeParametersFor() called on a self-reference return null, even though the type it represents has those supertypes.

#132 fixed this for self-references held as supertypes of other types (ResolvedObjectType / ResolvedInterfaceType accessors now return getActualType()), and MemberResolver.resolve() special-cases a self-reference as main type; but other callers holding a self-reference directly (e.g. one obtained via getTypeParameters()) still lose its supertypes.

Reproduction

public interface Top { void top(); }
public interface BaseI<X> extends Top { X get(); }
public interface MidI<T> extends BaseI<OuterI> { }
public interface OuterI extends MidI<String> { }

TypeResolver r = new TypeResolver();
// `OuterI` within `BaseI<OuterI>`: a ResolvedRecursiveType
ResolvedType selfRef = r.resolve(OuterI.class).findSupertype(BaseI.class).getTypeParameters().get(0);
selfRef.findSupertype(Top.class);       // null
selfRef.typeParametersFor(BaseI.class); // null
new MemberResolver(r).resolve(selfRef, null, null).getMemberMethods().length; // 2 (special-cased)

Expected

Supertype lookups on a self-reference give the same results as for the type it represents (getActualType()).

Notes

  • Simplest approach: have ResolvedRecursiveType.findSupertype() (and possibly getParentClass() / getImplementedInterfaces()) delegate to getActualType(). Delegating the plain accessors is riskier: returning no supertypes is what currently terminates traversals of recursive types, and for expanding generic types (like C<T> extends Base<D<T>>, D<T> extends C<List<T>>) following actual types yields ever deeper types. Overriding only findSupertype() / typeParametersFor() would avoid that, since lookup by erased class terminates.
  • Related: Member type of bare type variable bound to self-reference isResolvedRecursiveType (unlike T[] or List<T>) #133 (member types that are bare self-references).

Activity

  1. self-assigned this
    on Oct 11, 2026
  2. added this to the 1.8.0 milestone on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions