← board

record helper for T / type helper for T — type helpers

Surface needed by generics.helpers (v1 slice)

TValueAnsiStringHelper = record helper for AnsiString
  function ToLower: AnsiString; inline;         { Self = the string value }
end;
TValueUInt32Helper = record helper for UInt32
  class function GetSignMask: UInt32; static; inline;
  const SIZED_SIGN_MASK: array[1..32] of UInt32 = (...);
end;

Consumers: ALeft.ToLower (const string params — lvalue receivers), and type-name statics UInt32.GetSignMask / UInt32.SIZED_SIGN_MASK[i] inside the helpers unit itself.

Design sketch

  1. Type-section parse: = record|type helper for <type> → register a class-like entry with HelperTargetTk/Rec; members parse via the existing class member machinery. Self (param 0) is the TARGET type BY REFERENCE, not tyClass.
  2. Impl headers function THelper.M... — the Self injection must fork on the helper marker (both decl and impl sides must agree, see b321's lesson).
  3. Dispatch: member access on a NON-class receiver consults a helper registry (target tk+rec → newest helper ci; FPC: last visible helper wins), binds the method, passes @receiver as Self. Lvalue receivers first; rvalue receivers need a materialized temp (later).
  4. Type-name receivers for statics/consts.

Gate

make test + self-host byte-identical (shared parser); fpjson suite stays green.

v1 LANDED 2026-07-14 (b331)

Decl (record helper for <type> via the advanced-record machinery, helper marker in UClsHelperTk/Rec), impl-side Self fork, and call dispatch (early ParseLValueAST intercept → ParseClassRecordSelectors with the helper as rec id; Self = receiver by reference). Instance methods on variables/params work, incl. Self mutation and const-string params; last-visible-helper-wins; frozen+managed strings are one family. Pinned: test/test_record_helper_for_string_b331.pas.

Remaining (v2+)

v2 LANDED (same commit series): helper STATICS + consts

class function ...; static; in a helper: Self = target BY VALUE (a dummy — static bodies may not read Self, per FPC), marked UMthIsStatic, callable both through a VALUE (c.GetSignMask) and through the HELPER's name (TU32Helper.GetSignMask — both the record-factory factor path and the class-name selector path fork on the helper marker and pass a literal-0 Self). Consts in helper bodies were already global-scoped and work. Remaining: TARGET-type-name receivers (UInt32.GetSignMask), rvalue receivers, class helpers — and generics.defaults' methods NAMED after type keywords (class function Integer(constref ...)), which is the next real wall.

v3 slice landed 2026-07-18 (a379d016): type helper for spelling

type helper for T now parses as an alias of record helper for T (parser type -section dispatch, mirrors the record-helper branch; self-host byte-identical). Instance methods + statics dispatch through the existing helper machinery. Test: test_type_helper_for_spelling.pas. Remaining v3: target-type-name receivers (UInt32.GetSignMask, UInt32.SIZED_SIGN_MASK[i]), rvalue receivers ('abc'.ToLower, F().ToLower — need a materialized temp), class helpers.