← board

for-in over an implicit-Self array field fails in methods

Symptom

examples/adventure/adventure.pas now compiles past the old line-325 (Length(Riddles)) blocker but fails at engine.pas:349:

pascal26:349: error: for-in: not a generator, enum type, or iterable variable ()

The failing code iterates an unqualified class field:

function TGame.FindRoom(const id: AnsiString): TRoom;
var r: TRoom;
begin
  Result := nil;
  for r in Rooms do          { Rooms is a dyn-array field of TGame }
    if r.Id = id then begin Result := r; Exit; end;
end;

for r in Self.Rooms is expected to work; for r in Rooms does not.

Minimal repro:

program forin_implicit_field;
type
  TObj = class
    Items: array of Integer;
    function Sum: Integer;
  end;

function TObj.Sum: Integer;
var v: Integer;
begin
  Result := 0;
  for v in Items do Result := Result + v;
end;

var o: TObj;
begin
  o := TObj.Create;
  SetLength(o.Items, 3);
  o.Items[0] := 10; o.Items[1] := 20; o.Items[2] := 12;
  Writeln(o.Sum);   { expected 42 }
end.

Root cause

ParseForStatementAST (compiler/parser.inc, the iterable-variable branch ~4786) resolves the source with fsym := FindSym(CurTok.SVal) — locals/globals only — then calls ParseForInVarAST(varIdx, fsym), which is keyed on a symbol index, not an AST node. When the name is an implicit-Self field, FindSym returns <0, so it falls through to the 'for-in: not a generator, enum type, or iterable variable' error.

This is the same gap the Length/High intrinsics had, but the fix is more involved: ParseForInVarAST takes a symbol, not a node, so supporting an implicit-Self (or any obj.field) source needs a node-based source variant (build the AN_FIELD over Self via the same path ParseLValueAST uses, then drive the enumerator desugar off that node).

Acceptance

Log