← board

Symptom

type
  TBase = class public class procedure Bump; virtual; end;
  TDer  = class(TBase) public class procedure Bump; override; end;
var c: TBaseClass;
begin
  c := TDer;
  c.Bump;      { <-- statement is neither a call nor an assignment }
end.

Assigning the result of a virtual function class method was fine; only the statement position failed, and only for the CLASS-virtual dispatch kind.

Root cause — the double case, five times over

compiler/pasparser_stmt.inc carried five hand-written copies of

if (ASTKind[n] = AN_CALL) or (ASTKind[n] = AN_VIRTUAL_CALL)
   or (ASTKind[n] = AN_INTF_CALL) or (ASTKind[n] = AN_CALL_IND) then

and not one of them mentioned AN_CLASS_VIRTUAL_CALL, which was added later. Textbook devdocs/dev/normalise-dont-special-case.md: the fifth arm is the one that stays broken because nobody greps for the sibling.

Fix (landed 2026-08-25)

New ASTNodeIsCall(node): Boolean in compiler/symtab.inc naming all five kinds once; the five copies in pasparser_stmt.inc now call it. Any future call kind is added in one place.

Regression test test/test_virtual_class_method_called_as_a_statement.pas, .expected from fpc 3.2.2.

Where it was found

[[feature-pascal-corpus-generics]], wall at generics.defaults.pas:1865.

Log