← board

IEnumerable<T> / IEnumerator<T> are missing from the RTL

Measured

Corpus packages/rtl-generics/src, probe program gcprobe; uses Generics.Collections; begin end., no -dVER3_0_0, binary aa572136dc9c (self-host fixedpoint at HEAD). The whole output is two errors:

pascal26:259: error: unknown type: IEnumerable
pascal26:259: error: expected ')' before '>'

generics.collections.pas:259 is

    procedure AddRange(const AEnumerable: IEnumerable<T>); overload;

The near: text on that error points somewhere else entirely — it is stale after a token splice, which is [[bug-a-the-near-context-window-is-stale-after-a-token-splice]]. Reduce from the FILE and LINE, never from near: on this corpus.

Why it is a library gap and not a compiler gap

IEnumerable is declared in neither the corpus nor our RTL:

$ grep -rn IEnumerable <corpus>/generics.defaults.pas <corpus>/inc/*.inc   # only USES
$ grep -rn IEnumerable lib/rtl/*.pas                                       # nothing

FPC supplies all four from the implicitly-used ObjPas/System units:

declaration FPC source
generic IEnumerator<T> rtl/objpas/objpas.pp:79
generic IEnumerable<T> rtl/objpas/objpas.pp:86
IEnumerator (TObject-based) rtl/inc/objpash.inc:273
IEnumerable rtl/inc/objpash.inc:280

Control — the compiler handles the shape. A generic interface declared locally and used as a parameter type of a generic class, alongside an overload on the bare parameter, compiles and runs (tiface.pas, mode delphi, prints val 5). So nothing here is waiting on Track P or Track A; the four declarations are simply absent.

The shape to add

  IEnumerator<T> = interface
    function GetCurrent: T;
    function MoveNext: Boolean;
    procedure Reset;
    property Current: T read GetCurrent;
  end;

  IEnumerable<T> = interface
    function GetEnumerator: IEnumerator<T>;
  end;

plus the two non-generic TObject-based ones. Where they go is the open question and is worth deciding before writing them: FPC puts them in the implicit ObjPas unit so that no uses is needed, and rtl-generics relies on exactly that. Putting them anywhere a program must uses would not fix the corpus.

property ... read on an interface is the one shape in the block above that should be checked against pxx before assuming it compiles; if it does not, that is a Track P ticket and this one becomes blocked-by it.

Provenance

Found by [[bug-p-the-rtl-generics-corpus-stops-on-tkey-in-a-tlist-body]] moving: that ticket's unknown type: TKey wall is fixed (template capture overrun on a bodiless nested class) and this is what the corpus reaches next. Corpus read from /home/neo/pxx/library_candidates/rtl-generics/packages/rtl-generics/src. Run time ~1m30s.


RESOLVED 2026-08-30 (frankB) — and the ticket's premise about placement was wrong

IEnumerator<T> / IEnumerable<T> are declared in lib/rtl/classes.pas. Corpus re-run: the two unknown type: IEnumerable errors are gone.

The premise that was wrong, because it is the reusable part

This ticket said:

Putting them anywhere a program must uses would not fix the corpus.

That is false for this corpus, and checking it took one grep. generics.collections.pas:43 reads

uses
    RtlConsts, Classes, SysUtils, Generics.MemoryExpanders, Generics.Defaults,
    Generics.Helpers, Generics.Strings;

— and RtlConsts, Classes and SysUtils are all ours (lib/rtl/). So Classes is a place the caller already reaches, and no implicit-unit machinery was needed at all. The reasoning that produced the wrong premise was sound about FPC (it really does use the implicit ObjPas unit so that no uses is needed) and simply never asked what this particular caller already imports. "FPC needs mechanism X for reason R" does not imply we need X, when our caller's own uses clause already solves R.

The genuine limitation, stated rather than hidden: a program using these without uses Classes still fails where FPC would succeed. Recorded in the source comment, not just here.

What shipped, and what deliberately did not

Where rung 6b now stands

One error, not two, and it is a compiler limit rather than a library gap:

pascal26:127: error: too many generic parameters (MAX_TEMPLATE_PARAMS)
  in: .../inc/generics.dictionariesh.inc

TDictionaryEnumerable declares six type parameters (three of them via the CUSTOM_DICTIONARY_CONSTRAINTS := TKey, TValue, THashFactory macro) against MAX_TEMPLATE_PARAMS = 4. Filed as [[bug-a-max-template-params-is-4-but-rtl-generics-declares-6]] — Track A, since it is compiler/defs.inc.

Gate

make lib-test green against stable v398, classes job included; plus a TStringList smoke (Create/Add/Sort/index/Count) to confirm the additive change did not disturb the existing surface. Compile time for the corpus probe rose 75s -> 118s, which is consistent with getting further rather than with a hang.

Log