← board

Indexing the result of a bare implicit-Self method call

function TPasParser.ParseExprOperand(...): TPasExpr;
begin
  ...
  if not (length(CurTokenText)=1) or not (CurTokenText[1] in ['A'..'_']) then

The boundary, measured

At the unfixed binary, four spellings of one call:

spelling before
Txt[1] bare, inside a method refused
Txt()[1] bare, explicit parens refused
Self.Txt[1] ok
p.Txt[1] from outside ok
GT[1], a bare global function ok

and, by return kind, bare: a dyn-array result refused, a class result ok.

Why it is the enumerated-predicate shape, not a missing feature

if (mpi >= 0) and (CurTok.Kind in [tkDot, tkLBrack]) and
   ((Procs[mpi].RetType = tyClass) or (Procs[mpi].RetType = tyRecord)) then

A hand-maintained list of return kinds that must grow a member per kind, with no diagnostic when it does not. And the failure is the family's signature: the diagnostic is about the WRONG SUBJECT — a bracket in a statement, a missing paren in an expression — so a reader chases the expression grammar. The operational tell held: the diagnostic was about the wrong subject, and one level up there was a list.

The list also cannot be right, because the machinery it was approximating already exists and is documented as singular: ApplyCallResultPtrSuffix is declared in pasparser_lval.inc with the comment "the ONE materialisation point for a suffix on a call RESULT". Three of the four spellings reach it. So this was never a widening question — one arm had opted out of the funnel and grown a two-member approximation of it.

Diagnosing it cost more than fixing it, and both instruments lied

pascal26:2468: error: expected ')' before ',' — the line was right and the near: window was wrong, pointing at ParseExc(...) on line 2301, 167 lines away. Two hypotheses died on the near-text before a cut sweep settled it: truncating pparser.pp at successive top-level boundaries and asking only whether the paren error appeared. TokenToExprOp alone parsed clean; including ParseExprOperand produced it.

Two of my own instruments also answered about something else. Stubbing the suspect function moved the error to its own HEADER, which read as "the imbalance predates this function" and was an artefact of the stub. And a predicate that grepped the WHOLE compiler output for expected ')' reported True on a run whose first error was undefined variable (charinset) — the string was present, from a later line. Grep the first line, not the output.

Residual found while cutting

Truncation surfaced a wall the parse error was hiding: pparser.pp:559 uses charinset, which lib/rtl/sysutils.pas does not declare (it names TSysCharSet in a comment as "the parameter type of the CharInSet / character" and then never declares the function). Filed as [[feature-b-sysutils-charinset]].

Log