TMod-c — Current Status

Rebrand (July 2026): Public project and language name is TMod-c (TYPE compiler). Mod-c is the legacy name during transition — repo directory, modc binary, and modc-mh/1 format id are unchanged until migration completes. Target compiler binary: tmodc; system ABI module: tmodc.mc / tmodc.h. Domains: tmodc.com, tmodc.org, tmodc.net.

Version: 0.26.8 (build 194)
Last updated: 2026-09-22
Active branch: mod-c_0.26
Tag: MOD-C_0.23.7 (items 5–6); 0.23.8 postfix FORWARD; 0.23.9 type-qualified methods; 0.24.0 .mh writer + reader; 0.24.2 test-tier Makefiles; 0.24.3 builtin integer/cardinal lowering; 0.24.4 codegen_header.mc self-host step; 0.24.5 qualident IMPORT … FROM; 0.24.6 ENUM Phase A/B; 0.24.7 ENUM Phase C + .mh negative import tests; 0.24.8 named CONST array bounds + const-eval; 0.24.9 symkind / mh_exportkind as .mc; 0.24.10 statement.mc; 0.24.11 expression.mc + OPAQUE (13a); 0.24.12 UNION (13c); 0.24.13 linked-chunk arena + STRUCT EXTENDS option A (13d); 0.24.14 Lexer.mc self-host + type_of_expr / init inference (21a); 0.24.15 discard enforcement (13b Policy A); 0.24.16 declaration.mc self-host + EXTENDS by-value auto-upcast; 0.24.17 instance methods (21) + static/instance by first formal; 0.24.17.135 type Name FORWARD; 0.24.18 #11 Phase A portable unit rebind; 0.25.0 item 20 VAR/REF/CONST formals (breaking); 0.25.1 callable field call-through; 0.25.2 cross-module method Phase A; 0.25.3 .mh formal signatures + 7a; 0.25.4 RECURSIVE enforce + signed SIZEOF + lean C prelude; 0.26.0 free-call soft-miss closed; 0.26.1 unknown types closed + no automatic stddef.h; 0.26.2 Parser → TParser; 0.26.3 parser.c → parser_common.mc + parser_main.mc; 0.26.3.170 .mh field lists; 0.26.4 method-type .mh RHS / cross-module field call-through; 0.26.5 width forms + .h prelude; 0.26.5.174 #11 import-bind + .mh widths; 0.26.5.175 EXTENDS polish (p^.x, as → .base, pointer/REF upcast); 0.26.5.176 Oberon p.x auto-deref; 0.26.5.177 Shapes example + light refactor; 0.26.5.178 p[i] peel + instance method on ^T; 0.26.5.179 Type::m(p) peel; 0.26.5.180 parent-chain methods; 0.26.5.181 LET write-forbid; 0.26.5.183 drop C const on LET + @ of LET/CONST; 0.26.5.186 extern type = struct, '\x1b', & vs @, trailing { , }; 0.26.5.187 DEFER only if executed; 0.26.5.188 DEFINE; 0.26.7.190 ... pass-through; 0.26.7.191 C-index p[i] on ^T; 0.26.7.192 several .mc inputs; 0.26.8.194 .mh alias RHS + packs + LEN.

Current Focus

Calm path (ordered — do in sequence):

Step Item Work
1 13a OPAQUE end-to-end Done (0.24.11).
2 13c Bare UNION Done (0.24.12) — parse / semantic / codegen_common_union_decl; tests green.
3 13d STRUCT EXTENDS option A Done (0.24.13) — chain clashes; synthetic base reserved only on EXTENDS; c.x → c.base.x; multi-level.
4 21a type_of_expr + simple init inference Done (0.24.14) — unlocks discard (13b), auto-upcast, VAR/REF, obj.method.
5 13b Discard enforcement (Policy A) Done (0.24.15) — known result requires (f()) or use; unknown/procedure bare OK.

Suggested next language work (on mod-c_0.26):

TODO next: #11 build-time defaults deferred (no second architecture to prove a compiler default). Soft: 21a-ternary. See After that: #11 architecture packs.

  1. Close name soft-miss (breaking) — Done. Calls 0.26.0; unknown types + drop automatic <stddef.h> 0.26.1. Prelude builtins and multi-word C spellings (long long, unsigned int) stay. size_t / FILE / void must be imported.
  2. .mh type completeness — Done (0.26.3.170 field lists; 0.26.4 method-type RHS). Cross-module obj.field and obj.field(...) call-through. Import the method type and the struct (TFn + TBox). Identifiers ≤ 255; whole-file .mh load; writer DynBuf.
  3. #11 width forms — Done (0.26.5). Exact integer N / cardinal N / real N (8/16/32/64; real 32/64). Generated .h owns the C prelude; -C writes a companion .h. Import as the one unit bind Done (0.26.5.174). .mh width spelling Done (0.26.5.174). .mh alias RHS — Done (0.26.8.194). In-tree packs — Done (0.26.8.194) (tests/fixtures/lp64, ilp32; stand-in for tmodc.lp64 until item 15). Deferred (same item): build-time defaults (TMODC_ARCH). See Recent Accomplishments (0.26.8.194).
  4. Optional EXTENDS polish — aggregate as → .base Done (0.26.5.175); pointer/REF upcast Done (0.26.5.175); ^T parent fields (p^.x) Done (0.26.5.175); Oberon p.x auto-deref Done (0.26.5.176); p[i] peel Done (0.26.5.178); instance method on ^T Done (0.26.5.178); Type::m(p) peel Done (0.26.5.179); parent-chain methods Done (0.26.5.180); C-index p[i] on ^T Done (0.26.7.191). See Recent Accomplishments (0.26.7.191).
  5. LET write-forbid — Done (0.26.5.181). Drop C const on LET (27b) + @ of LET/CONST — Done (0.26.5.183). C struct-tag extern type + char \x + parse &/{ , } — Done (0.26.5.186). DEFER only if executed — Done (0.26.5.187). DEFINE — Done (0.26.5.188). ... pass-through — Done (0.26.7.190). C-index p[i] on ^T — Done (0.26.7.191). Kilo snaptoken tutorial — Done (19 September 2026). Several .mc inputs — Done (0.26.7.192). .mh alias RHS + packs + LEN — Done (0.26.8.194). See Recent Accomplishments (0.26.8.194). Next: #11 build-time defaults deferred; soft 21a-ternary.
  6. Soft item 20 — named open-array alias formals. Bare formals stay by-value — see docs/parameter-passing.md.
  7. Soft: 21a-ternary; discard _ (24). CONST stays static const — C #define constants are DEFINE, not item 19 lowering. Self-host remains background. 7b startup auto-init — Cancelled (31 August 2026). Call module entries explicitly (7a). No generated main walk of imports.
  8. Later: loop-local FOR (breaking, JPL Power of Ten rule 6) — counted FOR always declares a read-only index; not a use of an enclosing VAR. See Future plan: loop-local FOR below. Not started; do not slip into 0.26.x calm-path work.
  9. Later: optional BOUND on WHILE / REPEAT / LOOP (JPL rule 2) — const trip count; loop bound 0 = idle loop. See Future plan: BOUND. Not 0.26.x.
  10. Later: drop unused IS — lexer keyword / EBNF relational op; never parsed. No type guards / RTTI. See Drop IS. IN stays reserved for SET OF membership (item 12).
  11. Later: cyclic TMod-c IMPORT — after Makefile subdirectory compilation (item 15). parser_main ↔︎ declaration / expression / statement works today via generated C headers and previously emitted .mh. Do not design a language rule until directories land. Interim: extract a shared module (parser_common).
  12. When a stdlib is designed: borrowed / pre-allocated buffers (e.g. TStringBuffer::init_at), not only malloc/realloc. See Stdlib: borrowed buffers.
  13. Later: SET OF enum (Wirth bitset, not a hash set) — IN, { … } literals, named CONST sets, + * - == !=. See Future plan: SET OF enum. Not 0.26.x.
  14. Later: shebang scripts — #!/usr/bin/env -S tmodc -run then PROGRAM/MODULE; wrapper already has -run; cache the binary so a later run can skip modc/cc. Parser: leading # before the unit keyword (#! drop, other directives emit early). See Future plan: shebang / -run scripts. Not 0.26.x calm-path (#11 first).

0.26.8.194 closes .mh alias RHS (export type integer complete integer 64), in-tree lp64/ilp32 packs, and interim LEN. #11 build-time defaults deferred. 0.26.7.192 closes several .mc inputs (tmodc -C -d dir *.mc; one unit per arena; not a dep walker). 0.26.7.191 closes C-index p[i] on ^T (E.row[at].updateRow(); not a pointer-to-array peel). 0.26.7.190 closes ... last-formal pass-through (emit , ...; consume extras via stdarg.h FFI). 0.26.5.188 closes [EXPORT] DEFINE identifier RestOfLine (bind like untyped IMPORT; #define pass-through unbound). 0.26.5.187 closes DEFER only if control passed that statement (stmt_index cutoff). 0.26.5.186 closes C struct-tag extern type, '\x1b', & vs @ parse error, and array trailing comma. 0.26.5.183 closes drop C const on LET and @ of LET/CONST. 0.26.5.181 closes LET write-forbid (bare name only). 0.26.5.180 closes parent-chain methods (Child → Parent::m). 0.26.5.179 closed Type::m(p) peel (@x not peeled). 0.26.5.178 closed p[i] peel and instance method on ^T. 0.26.5.177 is a light refactor plus examples/Shapes.mc. 0.26.5.176 closed Oberon p.x auto-deref. 0.26.5.175 closed EXTENDS polish (p^.x / as / pointer·REF). 0.26.5.174 closes import-as-bind and .mh width tails. 0.26.5.173 closed width forms and moved the C prelude into generated .h. 0.26.4.172 closed .mh type completeness. 0.26.3.168 closed parser.c self-host. 0.26.1.158 closed unknown type names and dropped automatic <stddef.h>. 0.26.0.156 closed free-call soft-miss. No hidden RTTI / type tags / vtables on structs (Modula-2 + C stance).

EOS layout rules (0.24.3) remain in force. Arena growth is pointer-stable (linked chunks; no whole-slab realloc).

DEFINE statement (0.26.5.188)

Status: Done. Design locked 12 September 2026; implemented and promoted 14 September 2026. Tests: tests/test_define.mc, tests/test3_define.mc.

Syntax (Wirth; rest of line is C, not TMod-c):

define-decl = [ "EXPORT" ] "DEFINE" identifier rest-of-line .

Example:

define CTRL_KEY(k) ((k) & 0x1f)
export define PUBLIC_FLAG 1

Not done (later): macro expansion; #undef; #if as TMod-c; leading feature-test-before-includes.

After DEFINE: ... pass-through (TOK_DOT_DOT_DOT)

Status: Done (0.26.7.190). At least one named formal, then , ... last. Emit , ... in C. No TMod-c va_list / va_arg. Consume extras via FFI (stdarg.h + vprintf / vsnprintf). PROGRAM / MODULE lists reject .... .mh signatures may end (string, ...). Tests: tests/test_ellipsis.mc, tests/test3_ellipsis.mc, tests/test3_ellipsis_tail.mc. Kilo: define CTRL_KEY; editorSetStatusMessage(fmt: string, ...). Language Report §8.2; EBNF formal-parameters.

Not this slice: stdarg builtins; printf format checking; dedicated method/... tests.

C-style p[i] on ^T (0.26.7.191)

Status: Done (0.26.7.191). If p is ^T and T is not an array, p[i] has type T (C *(p + i)). Not array_index.auto_deref — that flag is pointer-to-array ((*(p))[i], 0.26.5.178). Copy-shell and clear is_pointer in semantic_type_of_designator (same strip as p^). Instance p[i].method() then follows ordinary T receiver rules; REF still auto-& → Type__m(&p[i]). Named alias (pERow = ^ERow) and bare ^T both work. Unknown fields on p[i] are now checked (no longer foreign C). Tests: tests/test_ptr_index.mc, tests/test3_ptr_index.mc. Kilo: E.row[at].updateRow(); ERow:: / EditorConfig:: methods; tutorial Done (19 September 2026). Language Report §5.3 / §6.2 / §8.3.

Not this slice: first formal ^Owner as instance; changing realloc buffers into real array[N] types.

Several .mc inputs (0.26.7.192)

Status: Done (0.26.7.192). tmodc accepts more than one leftover non-option. Each unit is a fresh source buffer + arena + parser (process_one_unit in src/modc.mc). Nothing is shared. error_at still exit(1); a non-zero generate or a missing file stops the batch.

Several inputs: bare -C / -H / -M (auto names from PROGRAM/MODULE) and optional -d. Explicit -C file.c / -H / -M / -b is an error. -C / -H / -M never consume a following .mc. Cap MAX_MC_INPUTS 512. Use while i < mc_input_count (not FOR … TO count - 1) so gcc -Wstrict-overflow stays quiet.

Not a dependency walker and not item 15. --print-imports / --deps still later. Language Report / EBNF unchanged.

After that: #11 architecture packs

Status: Partial (0.26.8.194). Widths, import-bind, .mh alias RHS, and in-tree packs are done. Build-time defaults deferred (think first; no second architecture to prove a compiler default). Unbound units stay int / unsigned int / float / const char * (cint).

Done (item #11): width forms; import-as-bind; .mh width tails; .mh alias RHS (export type integer complete integer 64); packs tests/fixtures/lp64 and ilp32 (lean .h; -M only — do not tmodc -H a pack header). Stand-in for tmodc.lp64 / tmodc.ilp32 until item 15 (include/tmodc/ + search). Quoted import integer from "fixtures/lp64.mh".

Deferred (same item): TMODC_ARCH / compiler-build default selection. A unit that wants 64-bit integer still binds (TYPE or pack import).

Not that slice: first formal ^Owner as instance; item 19 CONST codegen; @n as const ^T; import integer from tmodc.lp64.

LEN (C element count) (0.26.8.194)

Status: Done (0.26.8.194). Interim, until language-level array lengths and string byte length.

LEN(designator) only (not a type). Type integer. C:

((integer)(sizeof(n) / sizeof((n)[0])))

Same signed type as SIZEOF. Designator is written twice in C; sizeof does not evaluate it (except VLAs). Pointers/string are sizeof(pointer)/1 until real LEN. Tests: tests/test_len.mc; tests/test3_len.mc.

Future plan: loop-local FOR (JPL Rule 6)

Status: Design locked (20 August 2026). Not implemented. Breaking when done — sole FOR form; no dual FOR i := on an existing VAR. Spec in docs/syntax-ebnf.md + Language Report (docs/language-report.md) when the bump starts.

Why: C11 for (int i = …) / Modula-3 / Ada: the index exists only for the loop. Today TMod-c is Oberon/Pascal (FOR i := assigns a predeclared var i; codegen for (i = start; …)). JPL Power of Ten rule 6 wants smallest scope.

Syntax (declaration =, same optional type as var-item / let-item):

for-statement =
    "FOR" identifier [ ":" type-specifier ] "=" expression
        ( "TO" | "DOWNTO" ) expression
        [ "BY" const-expression ]
    "DO" { invariant-clause } statement-sequence "END" ;
for i = 0 to n do … end
for i: integer = 0 to n do … end
for i = n downto 0 by 1 do … end

Semantics

Codegen — same wrapper as now; i is a new local in that block:

{
    const long long __for_end_lN = (long long)(end);
    for (integer i = start; (long long)(i) <= __for_end_lN; i++) { … }
}

Start evaluated once as the initializer. End stays precalculated. i is not C const (the header does i++); read-only is a TMod-c body rule. Nested for i = … each have their own { }.

When implementing: parser (:= → = + optional type); semantic (define loop-scoped binding, keep write-forbid, no enclosing-VAR lookup); codegen_c_for_stmt; tests; mechanical sweep of src/*.mc and tests (for i := → for i =, drop loop-only var i). Version bump (not a quiet 0.26.x patch). Empty TO range still does not enter; BY 0 stays illegal.

Future plan: BOUND on WHILE / REPEAT / LOOP (JPL Rule 2)

Status: Design locked (31 August 2026). Not implemented. Optional in the language; a later --pot (or equivalent) may require it. Do not slip into 0.26.x. FOR does not take BOUND (it already has TO/DOWNTO).

Why: Holzmann rule 2 — every loop has a tool-visible trip ceiling, except one identified non-terminating task loop. INVARIANT is a boolean, not a max count.

Syntax (const-expression, same family as FOR … BY / array bounds):

while-statement =
    "WHILE" expression "DO"
        [ "BOUND" const-expression ]
        { invariant-clause }
        statement-sequence
    "END" ;

repeat-statement =
    "REPEAT"
        [ "BOUND" const-expression ]
        { invariant-clause }
        statement-sequence
    "UNTIL" expression ;

loop-statement =
    "LOOP"
        [ "BOUND" const-expression ]
        { invariant-clause }
        statement-sequence
    "END" ;
while p <> nil do
    bound 4096
    p := p^.next
end

repeat
    bound 8
    try_once
until done

loop
    bound 0
    wait_for_cmd
end

Semantics

When implementing: statement.mc + EBNF + Language Report §7.1; semantic: const-eval, BOUND 0 only on LOOP; codegen trip counter + assert; tests (including test3 for while … bound 0). Keyword BOUND. May share a version bump with loop-local FOR or land separately.

Drop IS (no type guards)

Status: Locked (31 August 2026). Not implemented. Lexer still reserves IS (TOK_KEYWORD_IS); parser never uses it. EBNF no longer lists IS (relational-op is IN only). Language Report §6.3: IN is planned set membership; IS is dropped.

Oberon IS is a runtime type test (p is T) — needs RTTI or a tag. TMod-c: no vtables / no IS on structs. Remove the keyword (lexer table, TokenKind::string, EBNF relational-op / keyword, report). is becomes a legal identifier.

IN: still reserved; unused as an operator. Keep — v1 SET OF enum membership (not a type guard). See Future plan: SET OF enum.

When implementing: small mechanical drop; any test3 that used IS as a keyword should expect an identifier. Spec in docs/syntax-ebnf.md + Language Report. Not 0.26.x calm-path.

Future plan: SET OF enum (Wirth bitset)

Status: Design locked (2 September 2026). Not implemented. After the current higher-priority list. Do not slip into 0.26.x. Spec in docs/syntax-ebnf.md + Language Report §5 / §6.3 when the bump starts (productions already drafted as Planned).

Why: IN is in the grammar for set membership, not type guards (IS is dropped). Compiler source wants named groups (LOOP_KINDS) and kind in LOOP_KINDS instead of or chains. This is Pascal/Modula-2 SET OF: a bit vector on a small ordinal, not Julia/Python Set{T} (hash table, heap, arbitrary T). Hash sets, if ever, are stdlib — not IN.

v1 surface

type NodeKindSet = set of NodeKind

const LOOP_KINDS: NodeKindSet =
    { NODE_FOR, NODE_WHILE, NODE_REPEAT_UNTIL, NODE_LOOP }

if new_parent^.kind in LOOP_KINDS then
    child.enclosing_loop := new_parent
end

Codegen — bit test / mask; no heap:

typedef uint64_t NodeKindSet;
static const NodeKindSet LOOP_KINDS =
    (1ULL << NODE_FOR) | (1ULL << NODE_WHILE) |
    (1ULL << NODE_REPEAT_UNTIL) | (1ULL << NODE_LOOP);

if (LOOP_KINDS & (1ULL << new_parent->kind))
    child.enclosing_loop = new_parent;

TMod-c still rejects mixing ColorSet with NodeKindSet even if both are 64-bit in C.

v1 rejects: SET OF integer / real / struct / string; enumerator = 1000 or more than 64 members; SET OF char (256 bits — later); FOR x IN s; CARD; hash Set. Optional later: <= subset, XOR symmetric difference, NOT complement in E, Ada-style x in (A, B) sugar.

When implementing: keyword SET; set-type in type-expression / type-specifier; IN in the relational parser (token exists); { … } set vs array in semantic; operator types; tests (test_* membership/algebra, test3_* bad base type / ordinal out of 0..63). Version bump (not a quiet 0.26.x patch). May land after IS drop so IN is the only leftover relational keyword.

Future plan: shebang / -run scripts

Status: Noted 14 September 2026. Not implemented. Sometime later; do not slip into 0.26.x calm-path (#11 first). bin/tmodc already supports tmodc -run file.mc [args…] (temp .c / .h / binary, then exec).

Intent: a unit can be chmod +x and run as:

#!/usr/bin/env -S tmodc -run
program hello()
import printf from "stdio.h"
begin
    printf("hello\n")
end

Use /usr/bin/env, not /bin/env. GNU env -S is required so the kernel does not treat tmodc -run as one program name.

Wrapper (tmodc.sh.in): after -run, the next existing file is the unit (not only *.mc — a shebang of ./hello has no extension). Program argv follows that path. Optional: if argv[1] is a file whose first line is #!, implied -run.

Parser: parse_program already skips blanks, ;, and comments before PROGRAM/MODULE. A shebang is # at column 1 → TOK_PREPROCESSOR, which is an error today. Allow preprocessor tokens before the unit keyword:

Lexer already slurps # at column 1; no new token.

Cache (-run binaries): hosted convenience in the wrapper, not in modc. Keep the executable under ${XDG_CACHE_HOME:-$HOME/.cache}/tmodc/ (not /tmp). Reuse when the cached binary still exists, the unit .mc is not newer (mtime or content hash), and compiler identity matches (modc version / build.number, CC, TMODC_CFLAGS). Key: hash of absolute path + compiler-id + cflags. First cut may hash only the main unit; later include imported .mh / .h (or rebuild if any sibling is newer) — a stale import with an unchanged script mtime is the main foot-gun. Write to a temp name then mv. TMODC_NOCACHE=1 or -run -B forces a rebuild. TMODC_KEEP may print the cache path. Flight builds must not depend on a user cache.

Not that slice: ... formals; changing DEFINE; making unary env without -S work on non-GNU.

7b cancelled — no startup auto-init

Status: Won’t do (31 August 2026). Item 7a (module entry as an importable symbol) stays.

Do not generate calls of imported MODULE bodies from PROGRAM / main (Modula-2 / Delphi style). Hidden init is extra control flow (JPL rule 1) and a place to hide malloc (rule 3). Authors call the bound module entry explicitly when they want it. Optional module BEGIN…END remains a procedure the programmer invokes — not a constructor the compiler schedules.

Stdlib: borrowed buffers (when designed)

Not scheduled. tests/StringBuffer.mc today mallocs / reallocs / owns _data — fine for hosted tests, not a flight/Wasm default.

When a real stdlib is designed, prefer caller RAM:

procedure TStringBuffer::init_at(ref b: TStringBuffer, buf: ^char, cap: integer)

Hosted growing StringBuffer may remain for the compiler and Linux tests. Flight / --pot / wasm32 guests should not need realloc.

DbC resolve (REQUIRE / ENSURE)

Done in semantic_analyze_block_body — requires[] / ensures[] go through semantic_resolve_expr (same as loop INVARIANT and ASSERT). REF/VAR formals load as in the rest of the language.

Still not a nil-check of a REF T: n is the object. Nil-check ^T formals and pointer fields; check p <> nil before foo(p^). Remaining polish (not planned): side-effect-free contract lint; optional --pot “two assertions per routine” warn.

Recent Accomplishments (22 September 2026 — 0.26.8.194 / .mh alias RHS, packs, LEN)

.mh alias RHS Done

In-tree packs Done (fixtures)

LEN Done (interim)

Recent Accomplishments (21 September 2026 — 0.26.7.192 / several .mc inputs)

Several .mc on the command line Done

Recent Accomplishments (19 September 2026 — Kilo tutorial finished)

Snaptoken Kilo port Done (examples/kilo/kilo.mc)

Recent Accomplishments (18 September 2026 — 0.26.7.191 / C-index p[i] on ^T)

C-style p[i] on ^T Done

Recent Accomplishments (17 September 2026 — 0.26.7.190 / ... pass-through)

... last formal Done

Recent Accomplishments (14 September 2026 — 0.26.5.188 / DEFINE)

[EXPORT] DEFINE identifier RestOfLine Done

Recent Accomplishments (8 September 2026 — 0.26.5.186 / extern struct, escapes, parse)

C struct-tag extern type, char \x, parse_primary Done

Recent Accomplishments (7 September 2026 — 0.26.5.183 / LET C const and @)

Drop C const on LET Done (27b) and @ of LET/CONST Done

Recent Accomplishments (7 September 2026 — 0.26.5.181 / LET write-forbid)

LET write-forbid Done (Slice A)

Recent Accomplishments (4 September 2026 — 0.26.5.180 / parent-chain methods)

Parent-chain instance methods Done

Recent Accomplishments (4 September 2026 — 0.26.5.179 / Type::m(p) peel)

Qualified instance Type::m(p) on ^Owner Done

Recent Accomplishments (4 September 2026 — 0.26.5.178 / p[i] peel + instance on ^T)

Pointer-to-array p[i] and p.method on ^T Done

Recent Accomplishments (4 September 2026 — 0.26.5.177 / Shapes + refactor)

Light refactoring already checked in. examples/Shapes.mc — tagged EXTENDS scene (no IS); field auto-deref (176); downcast p as ^Circle after a programmer tag.

Recent Accomplishments (3 September 2026 — 0.26.5.176 / Oberon p.x auto-deref)

Field auto-deref Done; p[i] and p.method on ^T not in this build (closed 0.26.5.178)

Recent Accomplishments (2 September 2026 — 0.26.5.175 / EXTENDS polish)

Optional #4 — three slices Done; Oberon p.x not in this build

Verification: user typed in; tests green. Build 175.

Recent Accomplishments (31 August 2026 — one Makefile / gcc · clang · tcc)

make                              # gcc (default)
make clean && make CC=clang test-all
make clean && make CC=tcc test-all

Recent Accomplishments (28 August 2026 — 0.26.5.174 / #11 import-bind + .mh widths)

Import as the one unit bind Done

.mh width spelling Done

Verification: user typed in; tests green. Build 174.

Recent Accomplishments (27 August 2026 — 0.26.5.173 / width forms + .h prelude)

Width forms Done (integer N / cardinal N / real N)

Generated .h owns the C prelude Done

Verification: make test-all on gcc, clang, and tcc. In-tree modules regenerated. Promoted 0.26.5.173.

Recent Accomplishments (26 August 2026 — 0.26.4.172 / method-type .mh RHS)

Method-type export / import Done

Recent Accomplishments (25 August 2026 — 0.26.3.170 / .mh field lists)

Identifiers ≤ 255 Done

.mh reader whole-file Done

Writer DynBuf prefixes Done

Field lists — writer / reader / import Done

Recent Accomplishments (24 August 2026 — 0.26.3.168 / parser self-host)

parser.c → parser_common.mc + parser_main.mc Done

Cyclic .mh imports — noted, not designed

Recent Accomplishments (18 August 2026 — 0.26.1.158 / unknown types + no automatic stddef.h)

Unknown type names are errors Done (breaking)

Automatic <stddef.h> dropped Done

Verification: make test-all, selfhost, bootstrap, promote green on gcc, clang, and tcc. Promoted 0.26.1.158.

Recent Accomplishments (18 August 2026 — 0.26.0.156 / free-call soft-miss closed)

Unresolved free calls are errors Done (breaking)

Recent Accomplishments (14 August 2026 — 0.25.4.154 / RECURSIVE + signed SIZEOF + lean prelude)

RECURSIVE required on direct self-calls Done

SIZEOF is integer in C as well as in the type system Done

Lean generated C prelude Done (partial; <stddef.h> stays until 0.26)

Automatic includes: <stddef.h> (NULL, size_t), <stdint.h> (uint8_t), <stdbool.h>, <assert.h>.
typedef uint8_t byte; — #define nil NULL / #define NIL NULL.
Portable four typedefs unchanged. No automatic <stdlib.h> / <stdio.h> / <string.h> / <math.h> / <limits.h> / <float.h>. exit and friends must be imported. Tests, examples, and src/*.mc updated.

Verification: make test-all, selfhost, bootstrap green on gcc, clang, and tcc. Promoted 0.25.4.154.

0.26: drop <stddef.h> / require every non-builtin name — Done (0.26.0–0.26.1). Next: .mh field lists.

Recent Accomplishments (12 August 2026 — 0.25.3.150 / 7a module-entry bind)

Item 7a — Module entry as a real symbol Done

Recent Accomplishments (11 August 2026 — 0.25.3 / formal signatures + cross-module auto-&)

Item 6a — .mh formal signatures Done

Recent Accomplishments (11 August 2026 — 0.25.2 / cross-module methods Phase A)

Cross-module Type::method / obj.method Done (Phase A)

Recent Accomplishments (7 August 2026 — 0.25.1 / callable fields)

Callable field call-through Done

Recent Accomplishments (6 August 2026 — 0.25.0 / item 20)

Item 20 — VAR / REF / CONST formals Done (breaking)

Recent Accomplishments (6 August 2026 — 0.24.18 / #11 Phase A)

Item #11 Phase A — portable builtin unit rebind Done

type Name FORWARD (0.24.17.135) Done

Recent Accomplishments (3 August 2026 — 0.24.17 / item 21)

Item 21 — obj.method + instance vs static Done

Recent Accomplishments (1 August 2026 — 0.24.16 / auto-upcast)

EXTENDS by-value auto-upcast Done

Recent Accomplishments (31 July 2026 — 0.24.16 / declaration self-host)

Self-host: declaration.c → declaration.mc Done

Recent Accomplishments (29 July 2026 — 0.24.15 / 13b)

Item 13b — discard enforcement (Policy A) Done

Recent Accomplishments (29 July 2026 — 0.24.14 / 21a)

Item 21a — type_of_expr + simple init inference Done

Design (same period)

Recent Accomplishments (28 July 2026 — 0.24.14)

Self-host: lexer.c → Lexer.mc Done

Self-host: lexer.c → Lexer.mc Done

Recent Accomplishments (25 July 2026 — 0.24.13)

Item 13d — STRUCT EXTENDS option A Done

Infrastructure — linked-chunk arena Done

Recent Accomplishments (23 July 2026 — 0.24.12)

Item 13c — bare UNION types Done

Item 13a — OPAQUE types Done (0.24.11.x)

Design discussion (future — not implemented)

Recent Accomplishments (0.24.11 — expression.mc)

Self-host: expression.c → expression.mc

Recent Accomplishments (18 July 2026 — design + self-host)

Self-host: statement.c → statement.mc (0.24.10.110)

Design — no user-facing void; OPAQUE types

Design — explicit discard of function results Implemented (0.24.15 / 13b Policy A)

Grammar + lexer groundwork

Prior same line (0.24.9)

Recent Accomplishments (16 July 2026 — 0.24.8)

Named CONST in array bounds

Integer const-eval (shared)

Release process

Recent Accomplishments (15 July 2026 — 0.24.7)

ENUM Phase C

.mh negative import tests

Test include paths (Makefile)

Design — WHILE … BY not planned

Recent Accomplishments (14 July 2026 — 0.24.6)

ENUM Phase A/B

tmodc ABI prelude (planned)

TMod-c rebrand (documentation)

Recent Accomplishments (13 July 2026 — 0.24.5)

Qualident IMPORT … FROM

Recent Accomplishments (11 July 2026 — 0.24.4)

codegen_header.mc (self-host)

Recent Accomplishments (9–10 July 2026 — 0.24.3)

Import sources — documented (phase 1 vs phase 2)

EOS / layout (expressions, statements, RETURN)

Prior — 0.24.3 (9 July 2026)

Builtin integer / cardinal lowering

Parser / layout (9 July — import/decl)

Prior — 0.24.2 infrastructure (9 July 2026)

Three-tier test targets (gcc / clang / tcc)

Negative test fixtures (test3_*)

Version / build numbering

Project housekeeping

Prior Release — 0.24.0 (7 July 2026)

.mh module linking — items 7–8

Cross-module Mod-c imports work end-to-end: EXPORT emits modc-mh/1 tables; IMPORT … FROM "*.mh" loads them at semantic analysis; generated C #includes the associated .h.

Self-host in progress: src/*.mc modules import from corresponding .mh files.

Design Decisions (still in force)

See docs/language-report.md (prose) and docs/syntax-ebnf.md (full EBNF). Filenames use hyphens (no _; syntax-ebnf.md not syntax.ebnf.md). Former monolithic docs/grammar.md is a redirect; archive in docs/grammar_working_notes.md. Highlights:

TODO / Next Actions

IMPORT / EXPORT / .mh polish

  1. Items 1–4 semantics phase 2 core. Done (0.23.3–0.23.5)
  2. Item 5 — sizeof(UserType) at parse time. Done (0.23.7)
  3. Item 6 — EXTERN TYPE | VAR | PROCEDURE | FUNCTION. Done (0.23.7)
  4. Item 7 — .mh writer on EXPORT. Done (0.24.0)
  5. Item 8 — .mh reader on IMPORT … FROM "*.mh". Done (0.24.0)
  6. .mh completeness / signature richness; method export coverage. Negative import tests. Done (0.24.7) — test3_* tier. Method instance/static + import Type::name + cross-module obj.method (Phase A) — Done (0.25.2). Formal signatures + cross-module auto-& (6a) — Done (0.25.3). 6a. .mh formal signatures — Done (0.25.3). Type field lists / cross-module obj.field — Done (0.26.3.170). Method-type RHS / field call-through — Done (0.26.4.172) — complete function|procedure ( … ) [ : T ]; synthetic method_type; import the method type name as well as the struct.
  7. Qualident FROM. Done (0.24.5) — project-root path resolution (after Makefile subdirs); IMPORT * bulk import; IMPORT … AS … remain. 7a. Module entry as real import symbol — Done (12 August 2026 / 0.25.3.150) — same-unit MODULE name + importer auto-bind of @module export; synthetic proc + formals; arena-copied names; module VAR/REF ABI. Tests test_module_ref / test_module_entry. 7b. Module startup initialization — Cancelled (31 August 2026). No generated calls of imported module bodies. Call the 7a-bound entry explicitly. See 7b cancelled.

Language (after .mh polish or parallel)

  1. FORWARD … END / EXTERN … END declaration blocks.
  2. type ident FORWARD — Done (0.24.17.135) — incomplete types completed later in-unit.
  3. LEN — Done (0.26.8.194) — interim C ((integer)(sizeof(n) / sizeof((n)[0]))). Language-level array / string length later. SIZEOF C type — Done (0.25.4) — ((integer)sizeof(...)). See LEN. 10b. RECURSIVE on direct self-call — Done (0.25.4) — test3_recursive; mutual recursion later.
  4. Unit binding of bare integer / cardinal / real / string (Phase A TYPE) — Done (0.24.18). Width forms integer N / cardinal N / real N — Done (0.26.5). Import as the one bind Done (0.26.5.174). .mh width spelling Done (0.26.5.174). .mh alias RHS Done (0.26.8.194). In-tree packs Done (0.26.8.194). Deferred: build-time default selection.
  5. ENUM — Phase A/B/C done (0.24.6–0.24.7). Tests: test2_enum_parse.mc, test_enum.mc, test3_enum_dup.mc, test_mh_enum_export.mc, test2_mh_enum_import.mc, test_mh_enum_import.mc.
  6. Named constants in array bounds — Done (0.24.8) — array[LIMIT] / array[LIMIT+1]; integer CONST fold; string CONST excluded from fold. 13a. OPAQUE types — Done (0.24.11) — TYPE RHS opaque / ^opaque / POINTER TO opaque; codegen void *; tests incl. negative bare opaque. 13b. Enforce discard form (Policy A) — Done (0.24.15) — known result → (f()) or use; unknown/procedure bare OK; tests test_discard / test3_discard; gcc/clang/tcc green. 13c. UNION types — Done (0.24.12) — bare C union; is_union + codegen_common_union_decl; tests test_union / test3_union; gcc/clang/tcc green. 13d. STRUCT EXTENDS (Oberon layout / option A) — Done (0.24.13) — clashes; base_depth rewrite; synthetic base (reserved only on EXTENDS); tests test_extends / test3_extends_*. By-value auto-upcast — Done (0.24.16) — upcast_depth; init / assign / return / same-unit call args; C .base × N. Polish: p^.x, as → .base, pointer/REF upcast — Done (0.26.5.175). Oberon p.x auto-deref — Done (0.26.5.176). p[i] peel + instance on ^T — Done (0.26.5.178). Type::m(p) peel — Done (0.26.5.179). Parent-chain methods — Done (0.26.5.180).

Infrastructure

  1. Makefile test3 target — negative semantic tests. Done (0.24.2)
  2. Makefile subdirectory compilation — src/ tree → build/; prerequisite for project-root module search paths. 15a. Cyclic IMPORT policy — after 15. Candidates later: extract-common (current), interface/implementation split, cycle ban, or interface-only first pass. Not a 0.26.x language change.
  3. Codegen: @ vs as precedence.
  4. Codegen: struct forward-declare / deferred p* typedefs.
  5. FOR end temp type from expression types.
  6. Compile-time CONST codegen — demoted (12 September 2026). Stays C static const. Integer fold for bounds already (0.24.8). Need a preprocessor constant → DEFINE. Not a CONST → #define rewrite.
  7. VAR / REF / CONST formals — Done (0.25.0) — codegen + semantic modes; call-site auto-&; breaking non-VAR bare-name assign. Soft: named array-alias formals. 20b. Callable field call-through — Done (0.25.1) — method-typed fields / values; Oberon no auto-self; ambiguity with instance methods; test_callable_field / test_Math.
  8. Instance methods phase 2 — Done (0.24.17) — obj.method / chains / statements; instance vs static by first formal type; test_smoke / test3_smoke; gcc/clang/tcc green. 21a. type_of_expr + simple init inference — Done (0.24.14) — semantic_type_of_expr; init when : omitted; tests test_infer.mc; suite green. 21a-ternary. Ternary polish (later) — type cond ? a : b when both arms share one static type (and optionally array-literal inits); not required for 21a core.

Deferred

  1. Type guard selectors (IS) — dropped (31 August 2026). Range expressions still deferred. IN unused as an operator; reserved.
  2. Stdlib BitSet; SET OF unlikely. Borrowed-buffer / init_at API when stdlib is designed (not malloc-only).
  3. Discard binding _ (optional sugar; distinct from (f()) statement discard).
  4. Optional BOUND on WHILE / REPEAT / LOOP — plan.
  5. Drop keyword IS — plan.
  6. LET write-forbid — Done (0.26.5.181) — notes. Bare LET name not LHS of := / INC / DEC. 27b. Drop C const on LET locals — Done (0.26.5.183) — ordinary C locals; @ of LET/CONST illegal. Not item 19 (CONST declarations).

Strategic Direction

  1. TMod-c rebrand (in progress) — documentation and user-facing naming move to TMod-c / tmodc first; code and build artifacts keep modc aliases until deliberate cutover. tmodc system module (planned): width types and shared C ABI in tmodc.mc → tmodc.h; multi-TU generated C #includes tmodc.h. Language builtins stay in semantic prelude for now. Experimental src/tmodc.h exists for hand-written C that includes generated headers.
  2. Self-host with .mh imports — convert remaining src/*.c → .mc (recent: parser_common / parser_main, declaration, Lexer, expression, statement, codegen_header, symkind, mh_exportkind, …); keep full chain green under make test-all.
  3. Polish .mh linking — method Phase A + formals (6a) Done (0.25.2–0.25.3); module-entry bind (7a) Done (12 August); type field lists Done (0.26.3.170); method-type RHS / field call-through Done (0.26.4.172). 7b Cancelled (explicit module-entry calls only).
  4. Declaration blocks — forward/extern groups once .mh export shape is stable.
  5. Expression types / init inference (21a) Done. Discard (13b Policy A) Done. Instance methods (21) Done. VAR formals (20) Done (0.25.0). Callable fields Done (0.25.1). Cross-module method Phase A Done (0.25.2). Formal signatures / cross-module auto-& Done (0.25.3). Module-entry bind (7a) Done (12 August). RECURSIVE + signed SIZEOF + lean prelude Done (0.25.4). Free-call soft-miss Done (0.26.0). Unknown types + drop stddef.h Done (0.26.1). Parser self-host (parser_common / parser_main) Done (0.26.3). .mh field lists Done (0.26.3.170). Method-type RHS / field call-through Done (0.26.4.172). Width forms + .h prelude Done (0.26.5.173). #11 import-bind + .mh widths Done (0.26.5.174). EXTENDS polish (p^.x / as / pointer·REF) Done (0.26.5.175). Oberon p.x auto-deref Done (0.26.5.176). p[i] peel + instance on ^T Done (0.26.5.178). Type::m(p) peel Done (0.26.5.179). Parent-chain methods Done (0.26.5.180). .mh alias RHS + packs + LEN Done (0.26.8.194). Remaining on this stem: #11 build-time defaults deferred. CONST codegen demoted (use DEFINE for #define constants).
  6. OPAQUE (13a) … 0.26.5.186 extern struct / '\x' / & vs @ Done. DEFINE Done (0.26.5.188). ... Done (0.26.7.190). Kilo snaptoken tutorial Done (19 September 2026). Several .mc inputs Done (0.26.7.192). 0.26.8.194 Done. Next: #11 defaults deferred; 21a-ternary later. Cyclic IMPORT after item 15. Later (not 0.26.x): loop-local FOR; optional BOUND; drop IS; SET OF enum.

Useful Commands

make                              # gcc (default)
make CC=clang                     # clang flags
make CC=tcc                       # tcc flags
make clean && make CC=clang test-all
make clean && make CC=tcc test-all

make test                         # test_* compile + run
make test2                        # test2_* semantic (-a)
make test3                        # test3_* negative (failure = pass)
make test-all                     # all three tiers

make version                      # manual: bump BUILD_NUMBER + src/version.h
make selfhost
make bootstrap
make promote

./bin/modc.0.25.4 tests/test_smoke.mc -C build/test/test_smoke.c
./bin/modc.0.25.4 tests/test_var_ref.mc -C build/test/test_var_ref.c
./bin/modc.0.25.4 tests/test_callable_field.mc -C build/test/test_callable_field.c
./bin/modc.0.25.4 tests/test_Math.mc -C build/test/test_Math.c
./bin/modc.0.25.4 tests/test3_var_ref1.mc -a              # expect non-zero (non-VAR assign)
./bin/modc.0.25.4 tests/test3_smoke.mc -a                 # expect non-zero (static via .)
./bin/modc.0.25.4 tests/test3_callable_field_ambig.mc -a  # expect non-zero (field vs instance)
./bin/modc.0.25.4 tests/test3_recursive.mc -a             # expect non-zero (self-call without RECURSIVE)
./bin/modc.0.25.4 tests/test_method.mc -C build/test/test_method.c
./bin/modc.0.25.4 tests/test_discard.mc -C build/test/test_discard.c
./bin/modc.0.25.4 tests/test3_discard.mc -a              # expect non-zero (tier-3 pass)
./bin/modc.0.25.4 tests/test_opaque.mc -C build/test/test_opaque.c
./bin/modc.0.25.4 -a tests/test5_mh_method_import_string.mc   # cross-module methods (needs include/*.mh)
./bin/modc.0.25.4 tests/test_module_ref.mc -C build/test/test_module_ref.c
./bin/modc.0.25.4 tests/test_module_entry.mc -C build/test/test_module_entry.c
./bin/modc.0.25.4 -a tests/test_mh_enum_import.mc
# make string-lib && link test4_string against -ltmod_string (manual; auto-& on free)
./bin/modc.0.24.17 tests/test_union.mc -C build/test/test_union.c
./bin/modc.0.24.17 tests/test_extends.mc -C build/test/test_extends.c
./bin/modc.0.24.17 tests/test3_extends_clash.mc -a           # expect non-zero (tier-3 pass)
./bin/modc.0.24.17 tests/test3_extends_base_reserved.mc -a # expect non-zero (tier-3 pass)
./bin/modc.0.24.17 tests/test3_opaque_parse.mc -a    # expect non-zero (tier-3 pass)
./bin/modc.0.24.17 tests/test3_union.mc -a           # expect non-zero if negative
./bin/modc.0.24.17 tests/test_mh_export.mc -C build/test/test_mh_export.c -M
./bin/modc.0.24.17 tests/test2_mh_import.mc -C tests/test2_mh_import.c -a
./bin/modc.0.24.17 tests/test_enum.mc -C build/test/test_enum.c
./bin/modc.0.24.17 tests/test_const.mc -C build/test/test_const.c
./bin/modc.0.24.17 tests/test3_enum_dup.mc -a    # expect non-zero (tier-3 pass)

Context & History

Prior Releases (summary)