plato-232.md
July 31, 2026 · View on GitHub
Summary
Add discriminated sum types (tagged unions) with exhaustive match to Plato, targeting
C#. Executes the RFC idea plato-077: this issue is the design + spec +
test-corpus stage; the parser/AST front end (wave 2) and the stdlib migration (wave 3)
are the implementation increments that follow. Removes the language's biggest partiality
gap — the XxxKind + conditionally-valid-fields hand-encoding — and unblocks the stdlib
Option/Result cleanup (plato-079).
Design (fixed)
- Declaration:
typegains an=case-list body as an alternative to the braced field body:type PathSegment2D = Move(EndPoint: Point2D) | ... | Close;. Payload-free cases (enums) omit parens:type FillRule = NonZero | EvenOdd;. - Match: an expression, exhaustive, positional binders in case-field declaration order, no default arm in v1. Errors name the sum type and the missing / unknown / duplicated cases.
- Backend (C#): one
readonly partial struct—int Kindtag (0-based, declaration order) + flattenedCase_Fieldfields; private all-fields ctor; one static factory per case that zeroes the inactive fields (so struct equality over tag+fields is well-defined).matchlowers during elaboration to a tag-conditional chain of existing TIR nodes — no new TIR node, downstream passes untouched. Generated code compiles on net8.0 default LangVersion (C# 12; noswitch-expression exhaustiveness dependency). - Diagnostics: proposed CHK300–CHK307 (+ CHK320 for the non-C# writers rejecting sums). Full catalog with message texts in the design doc.
- Generics: supported iff the monomorphizer specializes sums per type argument with
no unifier change (expected yes); else v1 restricts with CHK306.
option.platodecides.
Full spec: design doc.
Survey (115 XxxKind types, ~11 true sums vs ~100 enums, flagship-5, 40-paths "after"):
survey doc.
Scope
- In (v1): declaration + match syntax; exhaustiveness/name checking; C# tagged-struct emission; monomorphic (and, if cheap, generic) sums.
- Out (v1): GLSL/TS/Rust emission (CHK320 rejection); nested patterns; guards; default
arm; recursive sum types (flat struct cannot contain itself);
unique/affine interaction; per-case shared fields.
Related
- plato-077 — the RFC idea this executes.
- plato-079 — stdlib partiality cleanup, hard-blocked on this.
- plato-076 — Gratify kernel port; names this as its top language gap.
- plato-230 — plato-src-v3, the survey's source corpus.
Done means
- Design doc landed: declaration + match syntax, static/dynamic semantics, C# emission contract (hand-written PathSegment2D struct), diagnostics catalog.
- Test corpus authored —
plato-test-sum/— 5 positive + 6 negative fixtures with the// EXPECT: <code>convention + README. - v3 survey: 115
XxxKindtypes classified (enum vs sum), flagship-5 recommended,40-paths.plato"after" drafted. - Front end (wave 2):
=case-list +matchparse and type-check; exhaustiveness diagnostics CHK300–CHK306 (+CHK320) fire on the negative corpus; positive corpus compiles to C# on net8.0 default LangVersion. (Plato1d3ed84AST,507de64checking/lowering/emission; PlatoTests 126 → 142.) - Migration (wave 3): the flagship five migrated (
PathSegment2D,Paint,MaskSource2D,ScalarFieldNode2D/3D,WindowFunction);lint plato-src-v30 parse/0 symbol errors (4584 → 4549),plato-src193, PlatoTests 142/142. (Plato37ed5e9.)
Closing note (2026-07-27)
Shipped: type X = Case(...fields) | Case | ...; declarations + exhaustive match expressions
(positional binders, no default arm) — grammar (parakeet), AST, symbol resolution, checker
(CHK300–CHK306, CHK320), match lowering during elaboration to a tag-conditional chain over
existing TIR nodes (no new node), and C#-only tagged readonly partial struct emission (int
Kind + flattened Case_Field fields + per-case factories + structural equality). Generics
restricted in v1 (CHK306). Test corpus plato-test-sum/ + SumTypeCheckingTests (12) +
SumTypeCodegenTests (4, in-proc Roslyn). Wave-3 migrated the five flagship plato-src-v3
kind-pattern types to real sums; 26-fields confirmed non-recursive (operands are Integer
node indices) so no substitution was needed.
Commits: parakeet 12aad78 (grammar); Plato 1d3ed84 (wave-1 AST), 507de64 (wave-2
checking/lowering/emission), 37ed5e9 (wave-3 flagship migrations + v3 README + survey).
Remaining (follow-up, not this issue): the ~100 pure-enum XxxKind types — the kind-pattern
sweep — plus the deferred features (bare constructors, generic sums, default arm, guards, nested
patterns, recursive sums, GLSL/TS/Rust support). Tracked as plato-233.