plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


6e346ab — Peter John 2026-10-25T16:50:16+05:30
chore: drop stale Option/Result references from comments and tests
plum-checker/src/lib.rs CHANGED
@@ -333,7 +333,7 @@ pub fn lookup(env: &TypeEnv, name: &str) -> Result<PlumType, String> {
333
333
  pub type MethodEnv = BTreeMap<(String, String), PlumType>;
334
334
  /// Info about one `enum` variant: which enum it belongs to, its 0-based runtime tag
335
335
  /// (numbering is shared across all of that enum's variants), and its payload field
336
- /// types (empty for a payload-free variant like `Red` or `None`).
336
+ /// types (empty for a payload-free variant like `Red` or `End`).
337
337
  #[derive(Debug, Clone, PartialEq)]
338
338
  pub struct EnumVariantInfo {
339
339
  pub enum_name: String,
@@ -341,7 +341,7 @@ pub struct EnumVariantInfo {
341
341
  pub field_types: Vec<PlumType>,
342
342
  /// Field names for a NAMED-payload variant (`Circle(radius: Int)`), aligned
343
343
  /// index-for-index with `field_types` — empty for the positional/generic-payload
344
- /// form (`Some[T]`). See `ast::EnumVariant::field_names`.
344
+ /// form (`Next(T)`). See `ast::EnumVariant::field_names`.
345
345
  pub field_names: Vec<String>,
346
346
  pub values: Vec<ast::Expr>,
347
347
  }
@@ -543,9 +543,9 @@ pub fn buildGlobalTables(source: &ast::Source) -> (TypeEnv, MethodEnv, EnumVaria
543
543
 
544
544
  /// A `Type`'s name for comparison purposes only — deliberately ignores `.generics`
545
545
  /// entirely (on both sides), so a trait's bare return/param type (`cause() ->
546
- /// Option`, unable to name a concrete payload since a trait has no instance to
546
+ /// Link`, unable to name a concrete payload since a trait has no instance to
547
547
  /// infer one from) is treated as compatible with ANY instantiation an implementer
548
- /// declares (`cause(self) -> Option[JsonParseError]`) — a real but deliberately
548
+ /// declares (`cause(self) -> Link[JsonParseError]`) — a real but deliberately
549
549
  /// coarse check (arity + base type names), not full structural/generic equality.
550
550
  fn paramTypeCompareName(pt: &ast::ParamType) -> Option<&str> {
551
551
  match pt {
@@ -571,7 +571,7 @@ fn paramTypeCompareName(pt: &ast::ParamType) -> Option<&str> {
571
571
  /// a generic class's own methods (and a real trait's own declared signatures,
572
572
  /// which are never specialized at all) read naturally here, before mangling
573
573
  /// folds a method's concrete return/param types into new, unrelated-looking
574
- /// names (`Option[JsonParseError]` -> `Option$JsonParseError`).
574
+ /// names (`Link[JsonParseError]` -> `Link$JsonParseError`).
575
575
  fn checkTraitConformance(source: &ast::Source) -> Vec<CheckError> {
576
576
  let mut errors = Vec::new();
577
577
  let traits: BTreeMap<&str, &ast::Trait> = source
@@ -668,7 +668,7 @@ pub fn checkSource(source: &ast::Source) -> CheckResult<()> {
668
668
  // A discriminant enum (`enum Foo(n: Int) = ...`) requires every variant to supply
669
669
  // exactly one value per declared param, unified against that param's type. An
670
670
  // ordinary (param-less) enum must NOT have variants with values — most likely
671
- // caused by writing `Some(5)` where the generic-payload form `Some[Int]` was meant.
671
+ // caused by writing `Next(5)` in an enum that declares no discriminant params.
672
672
  for item in &source.items {
673
673
  if let ast::Item::Enum(e) = item {
674
674
  for v in &e.variants {
@@ -1425,7 +1425,7 @@ fn inferClassCallRaw(
1425
1425
  }
1426
1426
  match ctx.enum_variants.get(&call.type_name) {
1427
1427
  // A NAMED-payload enum variant (`Circle(radius: Int)`, as opposed to the
1428
- // positional/generic-payload form `Some[T]`, which never sets `field_names`
1428
+ // positional/generic-payload form `Next(T)`, which never sets `field_names`
1429
1429
  // and so falls through to the permissive "unmodeled" case below) — this is
1430
1430
  // ALSO how a record-shaped, single-variant enum (the `type X = ...`
1431
1431
  // replacement, e.g. `enum Cat = | Cat(name: Str)`) constructs, with no
@@ -1465,7 +1465,7 @@ fn inferClassCallRaw(
1465
1465
  }
1466
1466
  Ok(PlumType::TNamed(info.enum_name.clone()))
1467
1467
  }
1468
- // Positional/generic-payload variant (`Some[T]`, constructed via `Expr::FnCall`
1468
+ // Positional/generic-payload variant (`Next(T)`, constructed via `Expr::FnCall`
1469
1469
  // elsewhere instead), a payload-free variant, or an unmodeled (e.g.
1470
1470
  // builtin/std) type: allow, codegen will catch a genuine mismatch.
1471
1471
  _ => Ok(PlumType::TNamed(call.type_name.clone())),
@@ -1487,7 +1487,7 @@ fn bareVariantWrapType(type_name: &str, ctx: &CheckCtx) -> PlumType {
1487
1487
  }
1488
1488
 
1489
1489
  /// Checks a single case pattern against the type of the subject it matches, binding any
1490
- /// new names it introduces into `env`. Constructor-payload sub-patterns (`Some(x)`) bind
1490
+ /// new names it introduces into `env`. Constructor-payload sub-patterns (`Next(x)`) bind
1491
1491
  /// against that variant's declared field types (see `EnumVariantInfo::field_types`).
1492
1492
  fn checkPattern(
1493
1493
  pat: &ast::CasePattern,
@@ -1700,8 +1700,8 @@ pub(crate) fn lookupFieldType(
1700
1700
  // from here: `class_name` names a real, known NAMED-payload-capable
1701
1701
  // enum that simply has no field by this name (a hard error), or it's
1702
1702
  // an unresolved/unmodeled type name, OR a real enum whose variants
1703
- // are all positional/payload-free (`enum Option = | Some(Int) |
1703
+ // are all positional/payload-free (`enum Link = | Next(Int) |
1704
- // None`, no `field_names` at all to check against — permissive,
1704
+ // End`, no `field_names` at all to check against — permissive,
1705
1705
  // codegen will catch a genuine mismatch, exactly as it always has).
1706
1706
  // Distinguish by whether any variant belonging to this enum name
1707
1707
  // declares named fields at all.
@@ -1726,7 +1726,7 @@ pub(crate) fn lookupFieldType(
1726
1726
  }
1727
1727
 
1728
1728
  /// A mangled variant name's own unmangled base (`"Ok$Int$Str"` -> `"Ok"`,
1729
- /// `"None"` -> `"None"`) — `monomorphize::mangle` always joins the original
1729
+ /// `"End"` -> `"End"`) — `monomorphize::mangle` always joins the original
1730
1730
  /// variant name and its resolved type args with `$`, so the base is simply
1731
1731
  /// everything before the first `$`.
1732
1732
  fn variantBaseName(name: &str) -> &str {
@@ -1737,8 +1737,8 @@ fn variantBaseName(name: &str) -> &str {
1737
1737
  /// belonging to the same enum as `enum_name`, for the `?` operator (`Expr::Try`)
1738
1738
  /// — used identically by the checker (`inferExpr`) and codegen (`inferLocalType`
1739
1739
  /// callers), which is why this lives here rather than inline in either. Returns
1740
- /// `None` if `enum_name` isn't (a monomorphized specialization of) `Result` or
1740
+ /// `None` if `enum_name` isn't (a monomorphized specialization of) `Try`
1741
- /// `Option` — i.e. doesn't have both shapes of variant.
1741
+ /// — i.e. has no `Ok`/`Err` pair of variants.
1742
1742
  pub fn tryOperatorVariants<'a>(
1743
1743
  enum_name: &str,
1744
1744
  ctx: &'a CheckCtx,
@@ -1836,7 +1836,7 @@ pub fn inferExpr(expr: &ast::Expr, env: &TypeEnv, ctx: &CheckCtx) -> Result<Plum
1836
1836
  },
1837
1837
  ast::Expr::Paren(inner) => inferExpr(inner, env, ctx),
1838
1838
  // `expr?` — deliberately does NOT check that the enclosing function's
1839
- // return type actually accepts the `Err`/`None` value this can exit
1839
+ // return type actually accepts the `Err` value this can exit
1840
1840
  // with early; that's left permissive here (codegen will catch a
1841
1841
  // genuine mismatch as a wasm type error), consistent with this
1842
1842
  // checker's existing "unmodeled/unvalidated, codegen catches it"
@@ -2245,7 +2245,7 @@ pub fn inferExpr(expr: &ast::Expr, env: &TypeEnv, ctx: &CheckCtx) -> Result<Plum
2245
2245
  };
2246
2246
  lookupFieldType(class_name, field_name, ctx)
2247
2247
  }
2248
- // An unresolved generic method return (e.g. `Result[T, E].unwrap()`'s
2248
+ // An unresolved generic method return (e.g. `Try[T, E].unwrap()`'s
2249
2249
  // bare `T`, which this checker's `PlumType` erases entirely — see
2250
2250
  // `types.rs`'s `TNamed` having no type-argument slot) reaches here as
2251
2251
  // a plain `TVar`, not a real "wrong type". Field access on it is
plum-checker/src/monomorphize.rs CHANGED
@@ -49,7 +49,7 @@ pub fn fnGenericParams(f: &ast::Fn) -> Vec<String> {
49
49
  }
50
50
 
51
51
  /// The generic parameter names an `Enum` introduces — its OWN declared
52
- /// generics (`enum Option[T] = ...`) if it declares any, else (for an
52
+ /// generics (`enum Link[T] = ...`) if it declares any, else (for an
53
53
  /// enum that doesn't) every distinct
54
54
  /// single-uppercase-letter variant field type name, in first-appearance
55
55
  /// order — the older, purely-implicit inference this replaces.
@@ -90,7 +90,7 @@ enum ExtraGenericSource {
90
90
  /// plain bare-letter param like `reduce`'s `acc: U` is also covered, via the
91
91
  /// same detection `fnGenericParams` already does for non-closure params). A
92
92
  /// closure's return type can ALSO nest the extra letter inside a container
93
- /// (`Option.andThen`'s `cb: fn(T) -> Option[U]`), not just be the bare
93
+ /// (`Link.andThen`'s `cb: fn(T) -> Link[U]`), not just be the bare
94
94
  /// letter itself (`List.map`'s `cb: fn(T) -> U`) — `collectGenericParamNames`
95
95
  /// recurses to find either shape; `resolveMethodOwnGenerics`'s
96
96
  /// `ClosureReturn` handling knows how to bind a nested one back out.
@@ -382,11 +382,11 @@ fn substituteTypesInExpr(expr: &mut ast::Expr, subst: &Substitution) {
382
382
  /// generic parameters with its resolved concrete type's name.
383
383
  ///
384
384
  /// Variant names are ALSO mangled here, with the same suffix as the enum's own
385
- /// name (e.g. `Some` -> `Some$Int`) — even a payload-free variant like `None`.
385
+ /// name (e.g. `Next` -> `Next$Int`) — even a payload-free variant like `End`.
386
386
  /// This is necessary because the runtime `EnumVariants` table (built by
387
387
  /// `buildGlobalTables`) is keyed by bare variant name globally: without this,
388
388
  /// two specializations of the same generic enum would both register a variant
389
- /// literally named `Some`, colliding in that flat table.
389
+ /// literally named `Next`, colliding in that flat table.
390
390
  pub fn specializeEnum(e: &ast::Enum, subst: &Substitution, mangled_name: &str) -> ast::Enum {
391
391
  let params = enumGenericParams(e);
392
392
  let type_args: Vec<PlumType> = params
@@ -591,16 +591,16 @@ fn renameVarInArg(arg: &mut ast::Arg, old: &str, new: &str) {
591
591
 
592
592
  struct Monomorphizer<'a> {
593
593
  fns_generic: BTreeMap<String, &'a ast::Fn>,
594
- /// Every method declared on a generic enum (e.g. `Result`'s `isOk`/`isErr`,
594
+ /// Every method declared on a generic enum (e.g. `Try`'s `isOk`/`isErr`,
595
595
  /// or a record-shaped `List`/`Map`'s own methods), keyed by the enum's own
596
596
  /// bare name.
597
597
  methods_generic_on_enum: BTreeMap<String, Vec<&'a ast::Fn>>,
598
- /// Bare variant name (e.g. `"Some"`) -> the generic `Enum` it belongs to. Keyed
598
+ /// Bare variant name (e.g. `"Next"`) -> the generic `Enum` it belongs to. Keyed
599
- /// by variant name because a construction site (`Some(5)`) parses as a `FnCall`
599
+ /// by variant name because a construction site (`Next(5)`) parses as a `FnCall`
600
600
  /// whose `name` is the VARIANT, not the enum's own name.
601
601
  enums_generic_by_variant: BTreeMap<String, &'a ast::Enum>,
602
602
  /// Mangled enum name -> {original variant name -> mangled variant name}, e.g.
603
- /// `"Option$Int" -> {"Some": "Some$Int", "None": "None$Int"}`. Populated eagerly
603
+ /// `"Link$Int" -> {"Next": "Next$Int", "End": "End$Int"}`. Populated eagerly
604
604
  /// (in `resolveEnumInstantiation`, at the moment an instantiation's concrete
605
605
  /// type arguments become known) rather than waiting for the worklist to actually
606
606
  /// produce that specialization — so both a construction call site and a later
@@ -608,13 +608,13 @@ struct Monomorphizer<'a> {
608
608
  /// regardless of processing order.
609
609
  enum_variant_mangling: BTreeMap<String, BTreeMap<String, String>>,
610
610
  /// The enum's own bare name -> the generic `Enum` — used to detect a bare
611
- /// generic-enum-typed function param (e.g. `o: Option`), distinct from
611
+ /// generic-enum-typed function param (e.g. `o: Link`), distinct from
612
612
  /// `enums_generic_by_variant` (keyed by VARIANT name, used for construction
613
- /// sites like `Some(5)`).
613
+ /// sites like `Next(5)`).
614
614
  enums_generic_by_name: BTreeMap<String, &'a ast::Enum>,
615
615
  /// Free functions that are NOT generic by `fnGenericParams`'s lowercase-letter
616
616
  /// convention, but whose param type(s) bare-name a generic enum (e.g.
617
- /// `unwrapOr(o: Option, ...)`) — such a function still needs its own
617
+ /// `unwrapOr(o: Link, ...)`) — such a function still needs its own
618
618
  /// per-call-site specialization, since its receiver generic enum is
619
619
  /// dropped from the monomorphized output and the bare name would otherwise
620
620
  /// resolve to nothing.
@@ -655,7 +655,7 @@ struct Monomorphizer<'a> {
655
655
  /// (the generic enum its declared return type names, the concrete type args
656
656
  /// it names them with) — for every function/method whose OWN declared return
657
657
  /// type is a fully-general instantiation of a known generic enum (e.g.
658
- /// `-> Result[Int, Str]`). Populated once up front (in `monomorphizeSource`,
658
+ /// `-> Try[Int, Str]`). Populated once up front (in `monomorphizeSource`,
659
659
  /// alongside `enums_generic_by_name`) from the ORIGINAL, unmodified signatures
660
660
  /// — unlike everywhere else in this file, this doesn't need the specialization
661
661
  /// to have actually run yet, since the declared signature already says
@@ -808,10 +808,10 @@ impl<'a> Monomorphizer<'a> {
808
808
  env.insert(p.name.clone(), TypeScheme::mono(ty));
809
809
  }
810
810
  // The function's own return type, resolved for THIS specialization — used
811
- // below to resolve a bare payload-free variant (`None`) that's the
811
+ // below to resolve a bare payload-free variant (`End`) that's the
812
812
  // BODY'S TAIL EXPRESSION (implicit return), the same way `Stmt::Return`
813
813
  // already does. A tail `Stmt::Expr` never goes through `Stmt::Return`'s
814
- // own handling, so without this a trailing bare `None` is left
814
+ // own handling, so without this a trailing bare `End` is left
815
815
  // unmangled and later fails the real checker's return-type unification.
816
816
  let expected_ret = self
817
817
  .current_return_type
@@ -837,16 +837,16 @@ impl<'a> Monomorphizer<'a> {
837
837
  self.rewriteBlock(block, &mut env)?;
838
838
  // `inferConcrete` (not plain `infer`) — the tail's type feeds
839
839
  // `maybeRewriteReturn` below, which needs the call's REAL
840
- // specialized return type (e.g. `Option$Str`) when the tail is
840
+ // specialized return type (e.g. `Link$Str`) when the tail is
841
841
  // a call to another function whose OWN declared return type
842
842
  // instantiates a generic enum. Plain `infer` only consults
843
843
  // `self.global_env`, built once from the ORIGINAL source
844
844
  // before any mangling — so a tail like `return otherFn()`
845
- // where `otherFn() -> Option[Str]` would infer the bare,
845
+ // where `otherFn() -> Link[Str]` would infer the bare,
846
- // unmangled `Option` here, and `maybeRewriteReturn` would then
846
+ // unmangled `Link` here, and `maybeRewriteReturn` would then
847
847
  // permanently freeze this function's OWN return annotation at
848
848
  // that same bare, ungeneric name instead of the concrete
849
- // `Option$Str` the body actually produces.
849
+ // `Link$Str` the body actually produces.
850
850
  match block.stmts.last() {
851
851
  Some(ast::Stmt::Expr(e, ..)) => Some(self.inferConcrete(e, &env)),
852
852
  Some(ast::Stmt::Return(Some(e))) => Some(self.inferConcrete(e, &env)),
@@ -880,7 +880,7 @@ impl<'a> Monomorphizer<'a> {
880
880
  /// is `true` or `false`; the specialization path differs only in that its tail
881
881
  /// is inferred against a resolved substitution, so a generic-parameter-letter
882
882
  /// return resolves to the specialization's concrete bound type (which the
883
- /// ordinary path cannot do). The unparseable-`None` fill-in is gated on
883
+ /// ordinary path cannot do). The unparseable-`End` fill-in is gated on
884
884
  /// `resolve_return` so an ordinary void function (`returns: None` meaning "no
885
885
  /// declared return", not "a generic return the grammar dropped") is never given
886
886
  /// a fabricated return type. Never fabricates a return from an un-inferrable
@@ -893,7 +893,7 @@ impl<'a> Monomorphizer<'a> {
893
893
  None => resolve_return,
894
894
  Some(rt) => {
895
895
  // A generic enum named WITH concrete type args already
896
- // (`-> List[Str]`, `-> Result[Int, Str]`) is already fully
896
+ // (`-> List[Str]`, `-> Try[Int, Str]`) is already fully
897
897
  // resolved — only a genuinely BARE reference (`-> List`,
898
898
  // `-> Box`, no `[...]` at all) is the "unresolved" shape this
899
899
  // is meant to catch. Without the `rt.generics.is_empty()`
@@ -923,10 +923,10 @@ impl<'a> Monomorphizer<'a> {
923
923
  }
924
924
 
925
925
  /// Rewrites `pat` (bare-name/constructor pattern) in place: an uppercase variant
926
- /// name gets mangled to its specialized form (`Some` -> `Some$Int`) if `mangling`
926
+ /// name gets mangled to its specialized form (`Next` -> `Next$Int`) if `mangling`
927
927
  /// says this position's subject is a specialized generic enum; a plain binding
928
928
  /// name is inserted into `case_env` at `ty`. Recurses into a constructor
929
- /// pattern's own fields (`Some(Some(v))`), looking up *that* field's own
929
+ /// pattern's own fields (`Next(Next(v))`), looking up *that* field's own
930
930
  /// mangling table from `self.enum_variant_mangling` — a nested sub-pattern can
931
931
  /// be a specialization independent of its enclosing pattern's.
932
932
  fn manglePattern(
@@ -988,7 +988,7 @@ impl<'a> Monomorphizer<'a> {
988
988
  match stmt {
989
989
  ast::Stmt::Assign(a) => {
990
990
  for (target, value) in a.targets.iter_mut().zip(a.values.iter_mut()) {
991
- // For a field target (`self.head = None`), resolve the
991
+ // For a field target (`self.head = End`), resolve the
992
992
  // value against the field's OWN declared type BEFORE the
993
993
  // generic rewrite/inference below — same reasoning as
994
994
  // `ClassCall`'s field values: a bare payload-free variant
@@ -1026,7 +1026,7 @@ impl<'a> Monomorphizer<'a> {
1026
1026
  ast::Stmt::Return(Some(e)) => {
1027
1027
  // Same idea as the field-target case above, but against the
1028
1028
  // enclosing function's own declared return type (`return
1029
- // None` inside a method returning `Option[Int]`).
1029
+ // End` inside a method returning `Link[Int]`).
1030
1030
  if let Some(rt) = self.current_return_type.clone() {
1031
1031
  let resolved = self.resolveFieldType(&rt);
1032
1032
  let expected = crate::plumTypeFromAst(&resolved);
@@ -1094,7 +1094,7 @@ impl<'a> Monomorphizer<'a> {
1094
1094
  .collect();
1095
1095
  // If a subject's concrete type is a specialized generic enum, its
1096
1096
  // variant-name mangling table lets us rewrite that position's patterns
1097
- // (`Some`/`None` -> `Some$Int`/`None$Int`) to reference the correct
1097
+ // (`Next`/`End` -> `Next$Int`/`End$Int`) to reference the correct
1098
1098
  // specialization, so the checker/codegen's unmodified, bare-name-keyed
1099
1099
  // `EnumVariants` lookup still resolves each pattern correctly.
1100
1100
  let variant_manglings: Vec<Option<BTreeMap<String, String>>> = subject_types
@@ -1130,8 +1130,8 @@ impl<'a> Monomorphizer<'a> {
1130
1130
  // A case body's own LAST statement, when it's a bare
1131
1131
  // `Stmt::Expr`, is itself an implicit-return position
1132
1132
  // whenever this whole `match` is the function's tail
1133
- // expression (`first`/`last`'s `None =>` arm ending in a
1133
+ // expression (`first`/`last`'s `End =>` arm ending in a
1134
- // bare `None`, for instance) — same reasoning as
1134
+ // bare `End`, for instance) — same reasoning as
1135
1135
  // `rewriteFnBody`'s own tail-expression handling, just one
1136
1136
  // level down through the match. Resolving against the
1137
1137
  // function's OWN declared return type is a no-op unless
@@ -1180,7 +1180,7 @@ impl<'a> Monomorphizer<'a> {
1180
1180
  if let Some(idx) = variant.fields.iter().position(|t| t.name == *gp) {
1181
1181
  let field_name = &variant.field_names[idx];
1182
1182
  if let Some(fa) = call.fields.iter().find(|fa| &fa.name == field_name) {
1183
- // A bare payload-free variant reference (`None`) carries
1183
+ // A bare payload-free variant reference (`End`) carries
1184
1184
  // no type of its own to bind a generic param FROM — skip
1185
1185
  // it here; it gets resolved AGAINST the binding (once
1186
1186
  // known) below instead, same as every other field.
@@ -1192,9 +1192,9 @@ impl<'a> Monomorphizer<'a> {
1192
1192
  }
1193
1193
  }
1194
1194
  // No field is EVER directly typed as a bare generic param for a type
1195
- // like `List[T]` (its fields are `Option[Node[T]]`/`Int`, never a bare
1195
+ // like `List[T]` (its fields are `Link[Node[T]]`/`Int`, never a bare
1196
1196
  // `T`) — and even where one exists, constructing with a payload-free
1197
- // value (`List(head: None, ...)`) gives no VALUE to infer a type from
1197
+ // value (`List(head: End, ...)`) gives no VALUE to infer a type from
1198
1198
  // regardless. Fall back to an explicit `List[Int](...)` annotation at
1199
1199
  // the call site when field-value inference alone isn't enough.
1200
1200
  if bindings.len() != params.len() && call.generics.len() == params.len() {
@@ -1219,9 +1219,9 @@ impl<'a> Monomorphizer<'a> {
1219
1219
  // declared return type, exactly like `resolveEnumInstantiation` already
1220
1220
  // does for a generic-enum construction site that can't see every
1221
1221
  // parameter from its own arguments. This is what makes `fun
1222
- // makeList(values: ...T) -> List[T] = List(head: None, ...).add(values)`
1222
+ // makeList(values: ...T) -> List[T] = List(head: End, ...).add(values)`
1223
1223
  // work: the bare `List(...)` construction gives no type witness at all
1224
- // (every field value is a payload-free `None`/`0`), but by the time
1224
+ // (every field value is a payload-free `End`/`0`), but by the time
1225
1225
  // THIS function is specialized for a concrete `T`, `current_return_type`
1226
1226
  // has already been substituted to e.g. `List[Int]` by `specializeFn`.
1227
1227
  if bindings.len() != params.len() {
@@ -1249,10 +1249,10 @@ impl<'a> Monomorphizer<'a> {
1249
1249
  let mangled = self.ensureEnumSpecialized(e, &params, bindings);
1250
1250
 
1251
1251
  // Now that every generic param is bound, resolve any bare
1252
- // payload-free-variant field values (`None`) against THIS
1252
+ // payload-free-variant field values (`End`) against THIS
1253
1253
  // specialization's own field types — codegen only ever sees the
1254
1254
  // mangled specialization (the generic template is dropped), so a
1255
- // still-bare `None` would be an unresolvable reference by the time it
1255
+ // still-bare `End` would be an unresolvable reference by the time it
1256
1256
  // gets there.
1257
1257
  if let Some(info) = self.enum_variants.get(&mangled).cloned() {
1258
1258
  for fa in &mut call.fields {
@@ -1285,7 +1285,7 @@ impl<'a> Monomorphizer<'a> {
1285
1285
  /// `Attribute` arm — that generic walk seeds a closure's params as
1286
1286
  /// unconstrained `TVar`s (it has no way to know a method's declared
1287
1287
  /// closure-param types), and once a construction inside a closure body
1288
- /// (e.g. `andThen`'s `|n| Some(n / 2)`) gets mangled against a WRONG
1288
+ /// (e.g. `andThen`'s `|n| Next(n / 2)`) gets mangled against a WRONG
1289
1289
  /// (`TVar`-derived) binding, nothing downstream can fix it —
1290
1290
  /// `resolveEnumInstantiation` only fires for a bare, still-unmangled
1291
1291
  /// variant name, so a premature wrong rewrite sticks permanently. Called
@@ -1437,7 +1437,7 @@ impl<'a> Monomorphizer<'a> {
1437
1437
  }
1438
1438
  ExtraGenericSource::ClosureReturn(i) => {
1439
1439
  // The closure's body was already rewritten (any generic
1440
- // construction inside it, e.g. `andThen`'s `Some(n / 2)`,
1440
+ // construction inside it, e.g. `andThen`'s `Next(n / 2)`,
1441
1441
  // already mangled to its real specialization) by
1442
1442
  // `rewriteMethodClosureArgs`, called before this — just
1443
1443
  // re-seed the same closure-param env to infer its
@@ -1475,10 +1475,10 @@ impl<'a> Monomorphizer<'a> {
1475
1475
  };
1476
1476
  // A bare closure return (`cb: fn(T) -> U`, e.g. `List.map`)
1477
1477
  // binds `letter` directly to the closure's whole inferred
1478
- // type. A NESTED one (`cb: fn(T) -> Option[U]`, e.g.
1478
+ // type. A NESTED one (`cb: fn(T) -> Link[U]`, e.g.
1479
- // `Option.andThen`) instead needs `bound` (the closure's
1479
+ // `Link.andThen`) instead needs `bound` (the closure's
1480
- // ACTUAL concrete return, e.g. `Option$Int`) matched back
1480
+ // ACTUAL concrete return, e.g. `Link$Int`) matched back
1481
- // against the DECLARED shape `Option[U]` to recover what
1481
+ // against the DECLARED shape `Link[U]` to recover what
1482
1482
  // `U` itself was — `bindGenericArg` (shared with
1483
1483
  // `resolveFnInstantiation`'s free-function argument
1484
1484
  // binding) does exactly that structural extraction.
@@ -1511,7 +1511,7 @@ impl<'a> Monomorphizer<'a> {
1511
1511
  // generic-FREE-function machinery this reuses) are FLAT sets keyed by
1512
1512
  // this bare string alone, globally across the whole program — without
1513
1513
  // the receiver folded in, two unrelated receivers whose OWN method
1514
- // happens to mangle to the same suffix (e.g. `Option[Int].map` and
1514
+ // happens to mangle to the same suffix (e.g. `Link[Int].map` and
1515
1515
  // `List[Int].map` both producing `"map$Str"`) would collide: the
1516
1516
  // second one popped would find the first's bare name already in
1517
1517
  // `specialized` and silently skip producing its own body.
@@ -1575,7 +1575,7 @@ impl<'a> Monomorphizer<'a> {
1575
1575
  }
1576
1576
 
1577
1577
  /// Without this, a method call chained directly onto a FRESH
1578
- /// enum construction in the same statement (`Some(4).filter(...)`, or
1578
+ /// enum construction in the same statement (`Next(4).filter(...)`, or
1579
1579
  /// any `SomeVariant(...).method(...)` one-liner for a generic enum) could
1580
1580
  /// never resolve: the receiver's specialization is only ENQUEUED here,
1581
1581
  /// not fully produced until the worklist drains (well after the current
@@ -1675,9 +1675,9 @@ impl<'a> Monomorphizer<'a> {
1675
1675
  }
1676
1676
 
1677
1677
  /// If `expr` is a bare reference to a payload-free variant of a GENERIC
1678
- /// enum (`None`), and `expected` names a SPECIFIC specialization of that
1678
+ /// enum (`End`), and `expected` names a SPECIFIC specialization of that
1679
- /// same enum (`Option$Node$Int`), rewrites `expr`'s name to that
1679
+ /// same enum (`Link$Node$Int`), rewrites `expr`'s name to that
1680
- /// specialization's own mangled variant name (`None$Node$Int`) and
1680
+ /// specialization's own mangled variant name (`End$Node$Int`) and
1681
1681
  /// ensures that specialization is registered — codegen only ever knows
1682
1682
  /// about specializations (the generic template enum is dropped entirely),
1683
1683
  /// so an un-rewritten bare reference would be unresolvable by the time it
@@ -1869,14 +1869,14 @@ impl<'a> Monomorphizer<'a> {
1869
1869
  /// `List[T]`), given `actual` — the ACTUAL concrete `PlumType` an argument
1870
1870
  /// declared as `declared` was inferred to have. The bare case (`declared`
1871
1871
  /// IS the letter, e.g. `a: T`) binds it directly to `actual`. The
1872
- /// container case (`declared` is `List[T]`/`Option[T]`/`Result[T, E]`,
1872
+ /// container case (`declared` is `List[T]`/`Link[T]`/`Try[T, E]`,
1873
1873
  /// `actual` is that container's own mangled specialization like
1874
1874
  /// `List$Int`) looks up `actual`'s recorded `class_specialization_info`
1875
1875
  /// (the SAME table `resolveMethodOwnGenerics` already trusts for a
1876
1876
  /// method's own receiver) to recover the concrete type EACH of the
1877
1877
  /// container's own generic params was bound to, then recurses — so
1878
1878
  /// `List[T]` against `List$Int` binds `T -> Int`, and (one level deeper)
1879
- /// `List[Option[T]]` against `List$Option$Int` binds `T -> Int` too. A
1879
+ /// `List[Link[T]]` against `List$Link$Int` binds `T -> Int` too. A
1880
1880
  /// mismatch (wrong container, or `actual` isn't even a named/specialized
1881
1881
  /// type) silently binds nothing for that param, same as before this
1882
1882
  /// existed — `resolveFnInstantiation`'s caller already rejects an
@@ -2006,8 +2006,8 @@ impl<'a> Monomorphizer<'a> {
2006
2006
  }
2007
2007
 
2008
2008
  /// The bare names of any generic class or enum referenced directly (not via a
2009
- /// lowercase-letter generic parameter) in `f`'s param types — e.g. `"Option"` for
2009
+ /// lowercase-letter generic parameter) in `f`'s param types — e.g. `"Link"` for
2010
- /// `unwrapOr(o: Option, default: Int) -> Int`. See `fns_bare_generic`'s doc
2010
+ /// `unwrapOr(o: Link, default: Int) -> Int`. See `fns_bare_generic`'s doc
2011
2011
  /// comment for why such a function needs its own specialization.
2012
2012
  fn fnBareGenericRefs(&self, f: &ast::Fn) -> Vec<String> {
2013
2013
  let mut names: Vec<String> = Vec::new();
@@ -2098,17 +2098,17 @@ impl<'a> Monomorphizer<'a> {
2098
2098
  Ok(())
2099
2099
  }
2100
2100
 
2101
- /// Resolves a construction of a generic enum's variant (e.g. `Some(5)` for
2101
+ /// Resolves a construction of a generic enum's variant (e.g. `Next(5)` for
2102
- /// `enum Option = | Some(a) | None`), rewriting `call.name` from the bare
2102
+ /// `enum Link = | Next(a) | End`), rewriting `call.name` from the bare
2103
- /// variant name (`Some`) to its mangled form (`Some$Int`) once the enum's own
2103
+ /// variant name (`Next`) to its mangled form (`Next$Int`) once the enum's own
2104
2104
  /// concrete instantiation is known. Mangling is eager and deterministic — it
2105
2105
  /// doesn't wait for the worklist to actually produce the specialized `ast::Enum`
2106
2106
  /// (see `enum_variant_mangling`'s doc comment).
2107
2107
  ///
2108
- /// A variant that carries no generic fields (e.g. `None`) can't pin down the
2108
+ /// A variant that carries no generic fields (e.g. `End`) can't pin down the
2109
2109
  /// enum's type parameters on its own, so such a construction site is left alone
2110
- /// here — some other construction site (e.g. `Some(5)`) is what drives the
2110
+ /// here — some other construction site (e.g. `Next(5)`) is what drives the
2111
- /// specialization. (A bare `None` used as a *value*, not a call, is
2111
+ /// specialization. (A bare `End` used as a *value*, not a call, is
2112
2112
  /// `ast::Expr::TypeName` and doesn't go through this function at all — see the
2113
2113
  /// plan's Global Constraints for that narrower, documented residual limitation.)
2114
2114
  fn resolveEnumInstantiation(
@@ -2167,11 +2167,11 @@ impl<'a> Monomorphizer<'a> {
2167
2167
  }
2168
2168
  }
2169
2169
  // This single construction site couldn't pin down every generic parameter
2170
- // by itself — e.g. `Ok(5)` only ever supplies `Result`'s `T`, never its `E`
2170
+ // by itself — e.g. `Ok(5)` only ever supplies `Try`'s `T`, never its `E`
2171
2171
  // (no `Ok` call site can, since `Err`'s payload is a disjoint field). Fall
2172
2172
  // back to the enclosing function's declared return type, if it names this
2173
2173
  // same enum with an explicit, fully-general `[...]` instantiation (e.g.
2174
- // `-> Result[Int, Str]`) — that's the one other place a type this
2174
+ // `-> Try[Int, Str]`) — that's the one other place a type this
2175
2175
  // construction site can't see on its own is written down on purpose.
2176
2176
  if bindings.len() != params.len() {
2177
2177
  if let Some(rt) = self.current_return_type.clone() {
@@ -2206,7 +2206,7 @@ impl<'a> Monomorphizer<'a> {
2206
2206
  // `resolveClassInstantiation` — `gt` needs
2207
2207
  // `resolveFieldType`'s recursive resolution
2208
2208
  // before `plumTypeFromAst`, or a generic-with-args
2209
- // type argument (e.g. `Result[Option[Int], Str]`)
2209
+ // type argument (e.g. `Try[Link[Int], Str]`)
2210
2210
  // would have its own nested generics silently
2211
2211
  // dropped.
2212
2212
  let resolved = self.resolveFieldType(gt);
@@ -2322,7 +2322,7 @@ impl<'a> Monomorphizer<'a> {
2322
2322
  if !self.enum_variant_mangling.contains_key(&mangled) {
2323
2323
  // Insert a placeholder table BEFORE recursively resolving any variant's
2324
2324
  // own field types below — a self-referential generic enum (e.g. a
2325
- // linked-list `Node[T]`'s own `next: Option[Node[T]]` field) would
2325
+ // linked-list `Node[T]`'s own `next: Link[Node[T]]` field) would
2326
2326
  // otherwise recurse right back into specializing itself here before
2327
2327
  // this specialization is even marked as in-progress, infinitely. The
2328
2328
  // recursive re-entry only ever needs this specialization's MANGLED
@@ -2360,14 +2360,14 @@ impl<'a> Monomorphizer<'a> {
2360
2360
  // Eagerly registers this specialization's ORDINARY methods'
2361
2361
  // signatures right now (see `registerEnumMethodSignatures`'s doc
2362
2362
  // comment) — a method called on this value in the SAME statement
2363
- // that constructs it (`Some(4).filter(...)`) needs
2363
+ // that constructs it (`Next(4).filter(...)`) needs
2364
2364
  // `self.methods` to already know about it, well before the
2365
2365
  // worklist entry below is ever popped. MUST run only inside this
2366
2366
  // `if` (guarded by the `enum_variant_mangling` insert just
2367
2367
  // above, which marks this exact specialization as already being
2368
2368
  // set up) — a method whose own return type re-mentions this SAME
2369
- // specialization (`Option[T]`'s `filter` returning `Option[T]`)
2369
+ // specialization (`Link[T]`'s `filter` returning `Link[T]`)
2370
- // recurses right back into `ensureEnumSpecialized("Option$Int",
2370
+ // recurses right back into `ensureEnumSpecialized("Link$Int",
2371
2371
  // ...)`; running this unconditionally on every call (rather than
2372
2372
  // only the first) recurses infinitely and stack-overflows.
2373
2373
  self.registerEnumMethodSignatures(e, &mangled, &bindings);
@@ -2386,12 +2386,12 @@ impl<'a> Monomorphizer<'a> {
2386
2386
  /// Fully resolves a FIELD's declared type into something that will actually
2387
2387
  /// exist after monomorphization. `specializeEnum`'s own field substitution
2388
2388
  /// only replaces a bare generic-param NAME (`T` -> `Int`) — a field declared
2389
- /// `Option[Node[T]]` becomes `Option[Node[Int]]` this way, which is now
2389
+ /// `Link[Node[T]]` becomes `Link[Node[Int]]` this way, which is now
2390
- /// fully CONCRETE but still names the generic TEMPLATES `Option`/`Node`
2390
+ /// fully CONCRETE but still names the generic TEMPLATES `Link`/`Node`
2391
2391
  /// directly, both of which monomorphization deletes from the output (only
2392
2392
  /// their mangled specializations, e.g. `Node$Int`, survive). Recursively
2393
2393
  /// resolves any nested generic arguments first (so `Node[Int]` inside
2394
- /// `Option[Node[Int]]` becomes `Node$Int` before `Option[...]` itself is
2394
+ /// `Link[Node[Int]]` becomes `Node$Int` before `Link[...]` itself is
2395
2395
  /// resolved), then — if the type names a known generic enum applied to
2396
2396
  /// arguments — mangles it to that specialization's real name and enqueues
2397
2397
  /// the specialization if it hasn't been already, via the same
@@ -2471,8 +2471,8 @@ impl<'a> Monomorphizer<'a> {
2471
2471
  /// originally declared, generics and all) to drive
2472
2472
  /// `resolveEnumInstantiation`'s fallback for construction sites that
2473
2473
  /// can't infer every generic param from their own arguments alone (e.g.
2474
- /// `Err("...")` needs `Result`'s OTHER param, `T`, from the function's
2474
+ /// `Err("...")` needs `Try`'s OTHER param, `T`, from the function's
2475
- /// own `-> Result[Int, Str]` declaration) — flattening the return type
2475
+ /// own `-> Try[Int, Str]` declaration) — flattening the return type
2476
2476
  /// up front would replace `rt.name` with an already-mangled name that
2477
2477
  /// fallback's own bare-template-name comparison can never match again.
2478
2478
  fn resolveFnParamTypes(&mut self, f: &mut ast::Fn) {
@@ -2482,11 +2482,11 @@ impl<'a> Monomorphizer<'a> {
2482
2482
  ast::ParamType::Variadic(t) => *t = self.resolveFieldType(t),
2483
2483
  // A closure-typed param's own param/return types can equally
2484
2484
  // name a concrete generic instantiation after substitution
2485
- // (e.g. `andThen`'s `cb: fn(T) -> Option[U]` becomes `fn(Int)
2485
+ // (e.g. `andThen`'s `cb: fn(T) -> Link[U]` becomes `fn(Int)
2486
- // -> Option[Int]` once `T`/`U` are bound) — leaving them
2486
+ // -> Link[Int]` once `T`/`U` are bound) — leaving them
2487
2487
  // unresolved meant this param's type, once turned into a
2488
2488
  // `PlumType` via `plumTypeFromAst` for the body's own type
2489
- // env, silently flattened to a bare, unmangled `Option`
2489
+ // env, silently flattened to a bare, unmangled `Link`
2490
2490
  // (`plumTypeFromAst` drops every type's `.generics`
2491
2491
  // regardless of nesting depth), so a call through the
2492
2492
  // closure param (`cb(v)`) inferred the wrong, un-mangled
@@ -2515,12 +2515,12 @@ impl<'a> Monomorphizer<'a> {
2515
2515
  /// PRODUCED — it says nothing about code further downstream that CONSUMES an
2516
2516
  /// already-specialized generic-enum value returned from calling some other
2517
2517
  /// (already fully concrete, non-generic) function or method, e.g.
2518
- /// `match parseIt() { Ok(v) => ... }` where `fun parseIt() -> Result[Int, Str]`.
2518
+ /// `match parseIt() { Ok(v) => ... }` where `fun parseIt() -> Try[Int, Str]`.
2519
2519
  /// `self.infer` can't help there either: it's built from `buildGlobalTables`,
2520
2520
  /// which (like every other `PlumType` site) drops a declared type's generic
2521
- /// args entirely (`plumTypeFromAst` maps `Result[Int, Str]` to the bare
2521
+ /// args entirely (`plumTypeFromAst` maps `Try[Int, Str]` to the bare
2522
- /// `TNamed("Result")`) — so it has no way to know this call's result is the
2522
+ /// `TNamed("Try")`) — so it has no way to know this call's result is the
2523
- /// SPECIALIZED `Result$Int$Str`, not the generic template.
2523
+ /// SPECIALIZED `Try$Int$Str`, not the generic template.
2524
2524
  ///
2525
2525
  /// This resolves that one specific, common shape directly from the callee's own
2526
2526
  /// declaration (recorded in `fn_return_generic_enum` during classification) —
@@ -2580,7 +2580,7 @@ impl<'a> Monomorphizer<'a> {
2580
2580
  }
2581
2581
  // Runs next (before the generic per-field rewrite below): a
2582
2582
  // bare payload-free variant field value (`Node(..., next:
2583
- // None)`) carries no type of its own to infer a generic
2583
+ // End)`) carries no type of its own to infer a generic
2584
2584
  // param from, and needs the enum's OWN (about-to-be-
2585
2585
  // specialized) field type to resolve which specialization it
2586
2586
  // actually means — `resolveClassInstantiation` handles that
@@ -2760,8 +2760,8 @@ impl<'a> Monomorphizer<'a> {
2760
2760
  ast::Expr::Compare(c) => {
2761
2761
  self.rewriteExpr(&mut c.left, env)?;
2762
2762
  self.rewriteExpr(&mut c.right, env)?;
2763
- // `current != None`: `current`'s side may already be a SPECIFIC
2763
+ // `current != End`: `current`'s side may already be a SPECIFIC
2764
- // enum specialization (e.g. `Option$Node$Int`) while `None`
2764
+ // enum specialization (e.g. `Link$Node$Int`) while `End`
2765
2765
  // itself is still bare (it carries no type of its own) —
2766
2766
  // resolve each side against the OTHER's type; a no-op unless
2767
2767
  // that side is actually a bare generic-enum variant.
@@ -2799,9 +2799,9 @@ impl<'a> Monomorphizer<'a> {
2799
2799
  | ast::Expr::TypeName(_) => {}
2800
2800
  ast::Expr::Closure(cl) => {
2801
2801
  // A closure literal's body can itself contain a generic
2802
- // construction site (`|v| Some(v)`) that needs rewriting to
2802
+ // construction site (`|v| Next(v)`) that needs rewriting to
2803
2803
  // its mangled variant/class name, same as anywhere else —
2804
- // without this, codegen later traps with "variant 'Some'
2804
+ // without this, codegen later traps with "variant 'Next'
2805
2805
  // missing from the GC type registry" the first time such a
2806
2806
  // closure is actually compiled. The closure's OWN params
2807
2807
  // aren't bound to any concrete type YET at this point in the
@@ -2876,7 +2876,7 @@ pub fn monomorphizeSource(source: &ast::Source) -> Result<ast::Source, String> {
2876
2876
  // `fn_return_generic_enum` loop below, which calls `resolveFieldType` and
2877
2877
  // can therefore trigger `registerEnumMethodSignatures` for a generic enum
2878
2878
  // as a SIDE EFFECT (e.g. resolving a nested `List[Str]` inside some other
2879
- // function's `-> Result[List[Str], Str]` return type).
2879
+ // function's `-> Try[List[Str], Str]` return type).
2880
2880
  // `registerEnumMethodSignatures` looks up `methods_generic_on_enum` and,
2881
2881
  // finding it not yet populated, silently registers ZERO methods for that
2882
2882
  // specialization (`Some(methods) = ... else { return }`) — and because the
@@ -2924,7 +2924,7 @@ pub fn monomorphizeSource(source: &ast::Source) -> Result<ast::Source, String> {
2924
2924
  // before `plumTypeFromAst`, same as every other site
2925
2925
  // that turns a declared `ast::Type` generic argument
2926
2926
  // into a `PlumType` — otherwise a nested generic type
2927
- // argument (e.g. this method's own `Option[List[Json]]`
2927
+ // argument (e.g. this method's own `Link[List[Json]]`
2928
2928
  // return type) has its `List[Json]` silently flattened
2929
2929
  // to bare `List`, and every match on this method's
2930
2930
  // return value ends up destructuring against the
plum-checker/tests/checker_tests.rs CHANGED
@@ -281,15 +281,15 @@ fn matchIntPatternAgainstStrSubjectIsError() {
281
281
 
282
282
  #[test]
283
283
  fn bareEnumTagUnifiesWithOwningEnumType() {
284
- // Regression: a bare non-Bool tag like `None` used to type as `TNamed("None")`
284
+ // Regression: a bare non-Bool tag like `Nothing` used to type as `TNamed("Nothing")`
285
- // (itself, not its enum), so comparing it against an `Option` value would wrongly
285
+ // (itself, not its enum), so comparing it against a `Maybe` value would wrongly
286
286
  // fail with a type mismatch.
287
287
  let src = "\
288
- enum Option =
288
+ enum Maybe =
289
- | Some(Int)
289
+ | Just(Int)
290
- | None
290
+ | Nothing
291
291
 
292
- fun isNone(o: Option) -> Bool = o == None
292
+ fun isNone(o: Maybe) -> Bool = o == Nothing
293
293
  ";
294
294
  let source = parse(src);
295
295
  let result = checkSource(&source);
@@ -299,12 +299,12 @@ fun isNone(o: Option) -> Bool = o == None
299
299
  #[test]
300
300
  fn variantConstructionChecksArgCountAndTypes() {
301
301
  let src = "\
302
- enum Option =
302
+ enum Maybe =
303
- | Some(Int)
303
+ | Just(Int)
304
- | None
304
+ | Nothing
305
305
 
306
- fun makeSome(v: Int) -> Option =
306
+ fun makeJust(v: Int) -> Maybe =
307
- Some(v)
307
+ Just(v)
308
308
  ";
309
309
  let source = parse(src);
310
310
  let result = checkSource(&source);
@@ -314,12 +314,12 @@ fun makeSome(v: Int) -> Option =
314
314
  #[test]
315
315
  fn variantConstructionWrongArgTypeIsError() {
316
316
  let src = "\
317
- enum Option =
317
+ enum Maybe =
318
- | Some(Int)
318
+ | Just(Int)
319
- | None
319
+ | Nothing
320
320
 
321
- fun bad() -> Option =
321
+ fun bad() -> Maybe =
322
- Some(\"x\")
322
+ Just(\"x\")
323
323
  ";
324
324
  let source = parse(src);
325
325
  let result = checkSource(&source);
@@ -380,9 +380,9 @@ enum Step(n: Int) =
380
380
  #[test]
381
381
  fn enumDiscriminantValueOnParamLessEnumIsError() {
382
382
  let src = "\
383
- enum Option =
383
+ enum Maybe =
384
- | Some(5)
384
+ | Just(5)
385
- | None
385
+ | Nothing
386
386
  ";
387
387
  let source = parse(src);
388
388
  let result = checkSource(&source);
@@ -412,9 +412,9 @@ fn fieldAccessOnOrdinaryEnumReceiverIsUnaffectedByEnumParams() {
412
412
  // not the checker, is what would catch a genuinely bad field access here) —
413
413
  // exactly as it did before discriminant enums existed.
414
414
  let src = "\
415
- enum Option =
415
+ enum Maybe =
416
- | Some(Int)
416
+ | Just(Int)
417
- | None
417
+ | Nothing
418
418
 
419
419
  fun bad(self) -> Int =
420
420
  self.n
@@ -570,15 +570,15 @@ fun useIt() -> Str =
570
570
 
571
571
  #[test]
572
572
  fn genericEnumSingleInstantiationTypeChecks() {
573
- // A generic Option-shaped enum, constructed at one concrete type (`Some(5)`),
573
+ // A generic Maybe-shaped enum, constructed at one concrete type (`Just(5)`),
574
574
  // matched, must resolve end-to-end via checkSource.
575
575
  let src = "\
576
- enum Option[T] =
576
+ enum Maybe[T] =
577
- | Some(T)
577
+ | Just(T)
578
- | None
578
+ | Nothing
579
579
 
580
580
  fun get() -> Int =
581
- o = Some(5)
581
+ o = Just(5)
582
582
  match o
583
583
  Some(v) =>
584
584
  v
@@ -590,36 +590,36 @@ fun get() -> Int =
590
590
  assert!(result.is_ok(), "expected Ok, got {:?}", result.err());
591
591
 
592
592
  // Directly prove resolution happened: the monomorphized output must contain a
593
- // concrete `Option$Int` enum whose `Some$Int` variant carries an `Int` field
593
+ // concrete `Maybe$Int` enum whose `Just$Int` variant carries an `Int` field
594
- // (not the generic `a`), and must NOT retain the generic `Option` template.
594
+ // (not the generic `a`), and must NOT retain the generic `Maybe` template.
595
- // The variant name is ALSO mangled (`Some` -> `Some$Int`), the same suffix as
595
+ // The variant name is ALSO mangled (`Just` -> `Just$Int`), the same suffix as
596
596
  // the enum's own name.
597
597
  let mono = plum_checker::monomorphize::monomorphizeSource(&source)
598
598
  .expect("monomorphize should succeed");
599
599
  let opt = mono.items.iter().find_map(|it| match it {
600
- Item::Enum(e) if e.name == "Option$Int" => Some(e),
600
+ Item::Enum(e) if e.name == "Maybe$Int" => Some(e),
601
601
  _ => None,
602
602
  });
603
- let opt = opt.expect("expected a specialized `Option$Int` enum in the output");
603
+ let opt = opt.expect("expected a specialized `Maybe$Int` enum in the output");
604
604
  let some = opt
605
605
  .variants
606
606
  .iter()
607
- .find(|v| v.name == "Some$Int")
607
+ .find(|v| v.name == "Just$Int")
608
- .expect("expected `Some$Int` (mangled) variant on `Option$Int`");
608
+ .expect("expected `Just$Int` (mangled) variant on `Maybe$Int`");
609
609
  assert_eq!(
610
610
  some.fields,
611
611
  vec![Type {
612
612
  name: "Int".to_string(),
613
613
  generics: vec![]
614
614
  }],
615
- "Some's field should be concrete Int"
615
+ "Just's field should be concrete Int"
616
616
  );
617
617
  assert!(
618
618
  !mono
619
619
  .items
620
620
  .iter()
621
- .any(|it| matches!(it, Item::Enum(e) if e.name == "Option")),
621
+ .any(|it| matches!(it, Item::Enum(e) if e.name == "Maybe")),
622
- "the generic `Option` template must be dropped from the output"
622
+ "the generic `Maybe` template must be dropped from the output"
623
623
  );
624
624
  }
625
625
 
@@ -629,12 +629,12 @@ fn genericEnumMultipleInstantiationsCoexistAndTypeCheck() {
629
629
  // program must now type-check correctly for BOTH instantiations — this is the
630
630
  // behavior this task adds (previously this was a documented, rejected limitation).
631
631
  let src = "\
632
- enum Option[T] =
632
+ enum Maybe[T] =
633
- | Some(T)
633
+ | Just(T)
634
- | None
634
+ | Nothing
635
635
 
636
636
  fun useInt() -> Int =
637
- o = Some(5)
637
+ o = Just(5)
638
638
  match o
639
639
  Some(v) =>
640
640
  v
@@ -642,7 +642,7 @@ fun useInt() -> Int =
642
642
  0
643
643
 
644
644
  fun useStr() -> Str =
645
- o = Some(\"x\")
645
+ o = Just(\"x\")
646
646
  match o
647
647
  Some(v) =>
648
648
  v
@@ -664,12 +664,12 @@ fun useStr() -> Str =
664
664
  })
665
665
  };
666
666
  assert!(
667
- has_enum_with_variant("Option$Int", "Some$Int"),
667
+ has_enum_with_variant("Maybe$Int", "Just$Int"),
668
- "expected Option$Int with Some$Int"
668
+ "expected Maybe$Int with Just$Int"
669
669
  );
670
670
  assert!(
671
- has_enum_with_variant("Option$Str", "Some$Str"),
671
+ has_enum_with_variant("Maybe$Str", "Just$Str"),
672
- "expected Option$Str with Some$Str"
672
+ "expected Maybe$Str with Just$Str"
673
673
  );
674
674
  }
675
675
 
@@ -724,11 +724,11 @@ fn ordinaryFunctionWithBareGenericEnumParamTypeChecks() {
724
724
  // The shape that broke the pre-existing codegen test: an otherwise-ordinary
725
725
  // function taking a bare generic-enum-typed parameter.
726
726
  let src = "\
727
- enum Option[T] =
727
+ enum Maybe[T] =
728
- | Some(T)
728
+ | Just(T)
729
- | None
729
+ | Nothing
730
730
 
731
- fun unwrapOr(o: Option, default: Int) -> Int =
731
+ fun unwrapOr(o: Maybe, default: Int) -> Int =
732
732
  match o
733
733
  Some(v) =>
734
734
  v
@@ -736,7 +736,7 @@ fun unwrapOr(o: Option, default: Int) -> Int =
736
736
  default
737
737
 
738
738
  fun use() -> Int =
739
- unwrapOr(Some(5), 0)
739
+ unwrapOr(Just(5), 0)
740
740
  ";
741
741
  let source = parse(src);
742
742
  let result = checkSource(&source);
@@ -1134,18 +1134,18 @@ fn classImplementingTraitWithGenericReturnTypeMismatchIsPermitted() {
1134
1134
  // same generic type with a concrete arg is still a match (base type name
1135
1135
  // only; see `checkTraitConformance`'s doc comment).
1136
1136
  let src = "\
1137
- enum Option[T] =
1137
+ enum Maybe[T] =
1138
- | Some(T)
1138
+ | Just(T)
1139
- | None
1139
+ | Nothing
1140
1140
 
1141
1141
  trait Err =
1142
- cause() -> Option
1142
+ cause() -> Maybe
1143
1143
 
1144
1144
  enum MyErr(Err) =
1145
1145
  | MyErr(pos: Int)
1146
1146
 
1147
- fun cause(self) -> Option[MyErr] =
1147
+ fun cause(self) -> Maybe[MyErr] =
1148
- None
1148
+ Nothing
1149
1149
  ";
1150
1150
  let source = parse(src);
1151
1151
  assert!(
@@ -1403,9 +1403,9 @@ fn nested_union_in_payload() {
1403
1403
  #[test]
1404
1404
  fn tryOperatorVariantsOnlyOkErr() {
1405
1405
  let src = "\
1406
- enum Opt =
1406
+ enum Maybe =
1407
- | Some(Int)
1407
+ | Just(Int)
1408
- | None
1408
+ | Nothing
1409
1409
  ";
1410
1410
  let source = parseClean(src);
1411
1411
  let (_, methods, variants, params) = plum_checker::buildGlobalTables(&source);
plum-checker/tests/monomorphize_tests.rs CHANGED
@@ -53,13 +53,13 @@ fn enumGenericParamsDetectsImplicitUppercaseLetterVariantFields() {
53
53
  let e = ast::Enum {
54
54
  line: 0,
55
55
  column: 0,
56
- name: "Option".to_string(),
56
+ name: "Maybe".to_string(),
57
57
  generics: vec![],
58
58
  implements: vec![],
59
59
  params: vec![],
60
60
  variants: vec![
61
61
  ast::EnumVariant {
62
- name: "Some".to_string(),
62
+ name: "Just".to_string(),
63
63
  fields: vec![ast::Type {
64
64
  name: "T".to_string(),
65
65
  generics: vec![],
@@ -68,7 +68,7 @@ fn enumGenericParamsDetectsImplicitUppercaseLetterVariantFields() {
68
68
  values: vec![],
69
69
  },
70
70
  ast::EnumVariant {
71
- name: "None".to_string(),
71
+ name: "Nothing".to_string(),
72
72
  fields: vec![],
73
73
  field_names: vec![],
74
74
  values: vec![],
@@ -154,13 +154,13 @@ fn specializeEnumSubstitutesGenericVariantFieldNames() {
154
154
  let e = ast::Enum {
155
155
  line: 0,
156
156
  column: 0,
157
- name: "Option".to_string(),
157
+ name: "Maybe".to_string(),
158
158
  generics: vec![],
159
159
  implements: vec![],
160
160
  params: vec![],
161
161
  variants: vec![
162
162
  ast::EnumVariant {
163
- name: "Some".to_string(),
163
+ name: "Just".to_string(),
164
164
  fields: vec![ast::Type {
165
165
  name: "T".to_string(),
166
166
  generics: vec![],
@@ -169,7 +169,7 @@ fn specializeEnumSubstitutesGenericVariantFieldNames() {
169
169
  values: vec![],
170
170
  },
171
171
  ast::EnumVariant {
172
- name: "None".to_string(),
172
+ name: "Nothing".to_string(),
173
173
  fields: vec![],
174
174
  field_names: vec![],
175
175
  values: vec![],
@@ -178,8 +178,8 @@ fn specializeEnumSubstitutesGenericVariantFieldNames() {
178
178
  };
179
179
  let mut bindings = std::collections::BTreeMap::new();
180
180
  bindings.insert("T".to_string(), PlumType::TInt);
181
- let specialized = specializeEnum(&e, &Substitution(bindings), "Option$Int");
181
+ let specialized = specializeEnum(&e, &Substitution(bindings), "Maybe$Int");
182
- assert_eq!(specialized.name, "Option$Int");
182
+ assert_eq!(specialized.name, "Maybe$Int");
183
183
  assert_eq!(
184
184
  specialized.variants[0].fields,
185
185
  vec![ast::Type {
plum-core/src/ast.rs CHANGED
@@ -102,7 +102,7 @@ pub struct Enum {
102
102
  /// rather than parsed).
103
103
  pub line: usize,
104
104
  pub column: usize,
105
- /// The enum's OWN declared generic type parameters (`enum Option[T] =
105
+ /// The enum's OWN declared generic type parameters (`enum Link[T] =
106
106
  /// ...`). Empty for a non-generic enum.
107
107
  pub generics: Vec<GenericParam>,
108
108
  /// Trait names this enum claims to implement (`enum Cat(ToStr) = ...`).
@@ -126,7 +126,7 @@ pub struct EnumVariant {
126
126
  pub fields: Vec<Type>,
127
127
  /// Field names for a NAMED-payload variant (`Circle(radius: Int)`), aligned
128
128
  /// index-for-index with `fields` — empty for the older, still-supported
129
- /// positional/generic-payload form (`Some[T]`), NOT one empty string per
129
+ /// positional/generic-payload form (`Next(T)`), NOT one empty string per
130
130
  /// field. Lets a construction site use named args (`Circle(radius: 5)`,
131
131
  /// parsed as an ordinary `ClassCall` — see `plum-checker`/`plum-wasm-codegen`'s
132
132
  /// `ClassCall` handling) instead of only positional ones (`Circle(5)`, still
@@ -361,11 +361,11 @@ pub enum Expr {
361
361
  ClassCall(ClassCall),
362
362
  /// `expr.field` or `expr.method(…)`
363
363
  Attribute(Box<AttributeExpr>),
364
- /// `expr?` — unwraps a `Result`'s `Ok`/an `Option`'s `Some`, or returns the
364
+ /// `expr?` — unwraps a `Try`'s `Ok`, or returns the
365
- /// `Err`/`None` value from the enclosing function as-is otherwise.
365
+ /// `Err` value from the enclosing function as-is otherwise.
366
366
  Try(Box<Expr>),
367
- /// `left ?: right` — if `left` is `Ok(v)`/`Some(v)`, the value is `v`; if
367
+ /// `left ?? right` — if `left` is `Ok(v)`, the value is `v`; if
368
- /// it's `Err(_)`/`None`, the value is `right`. Unlike `Try`, a plain
368
+ /// it's `Err(_)`, the value is `right`. Unlike `Try`, a plain
369
369
  /// expression with no early return.
370
370
  Elvis(Box<ElvisExpr>),
371
371
  /// `{expr}` — grouped/parenthesized expression
plum-core/src/parser.rs CHANGED
@@ -1089,7 +1089,7 @@ impl<'a> AstParser<'a> {
1089
1089
  }
1090
1090
 
1091
1091
  fn parseElvis(&self, node: Node) -> Expr {
1092
- // elvis_expression: expression "?:" expression
1092
+ // elvis_expression: expression "??" expression
1093
1093
  let named: Vec<Node> = self.namedChildren(node);
1094
1094
  let left = named
1095
1095
  .first()
@@ -1161,7 +1161,7 @@ impl<'a> AstParser<'a> {
1161
1161
  /// safe_attribute: primary_expression "?." fn_identifier fn_argument_list?
1162
1162
  ///
1163
1163
  /// Desugars entirely here into `object.map(|v| v.member(...))` — reusing
1164
- /// Option/Result's existing generic `.map` method for the actual
1164
+ /// Try's existing generic `.map` method for the actual
1165
1165
  /// unwrap/transform/rewrap, so nothing past the parser (checker,
1166
1166
  /// monomorphizer, codegen, formatters) needs to know `?.` exists at all.
1167
1167
  /// The synthesized param name can't collide with a real variable in scope:
plum-examples/error_propagation.plum CHANGED
@@ -7,7 +7,7 @@ import std/List
7
7
  # `Try[T, E]` is either `Ok(T)` or `Err(E)`. `E` is normally a tag union such
8
8
  # as `[NegativeNum, BadNumStr]`: a set of bare tags (optionally carrying a
9
9
  # payload, e.g. `InvalidJson(Str)`) that needs no declaration. There is no
10
- # `Option`: a missing value is just an error tag like `[OutOfBounds]`.
10
+ # A missing value is just an error tag like `[OutOfBounds]`.
11
11
  #
12
12
  # `expr?` unwraps an `Ok`, or exits the enclosing function early with the
13
13
  # `Err` value. The error unions of different calls merge automatically into
plum-std/Array.plum CHANGED
@@ -29,7 +29,7 @@ import std/Number
29
29
  # fixed-length allocation once made), so it allocates a new, one-longer array
30
30
  # and copies, same amortized-growth idea as `Buffer`'s own `write`.
31
31
  #
32
- # New elements start out `None`-shaped — really a null `anyref` under the
32
+ # New elements start out null-shaped — really a null `anyref` under the
33
33
  # hood — so `get` on an index that was never `set` traps rather than
34
34
  # returning some default `T` value (there is no way to conjure a default `T`
35
35
  # generically).
plum-std/Try.plum CHANGED
@@ -10,7 +10,7 @@ import std/Map
10
10
  # carrying an `E`). `E` is normally a structural tag union such as
11
11
  # `[NotFound, BadNum(Str)]`; `?` merges the error unions of different calls
12
12
  # into the enclosing function's declared union. A missing value is just an
13
- # error tag (`[OutOfBounds]`, `[KeyNotFound]`, ...), so there is no Option.
13
+ # error tag (`[OutOfBounds]`, `[KeyNotFound]`, ...), with no separate optional type.
14
14
  enum Try[T, E] =
15
15
  | Ok(T)
16
16
  | Err(E)
plum-tooling/tree-sitter-plum/grammar.js CHANGED
@@ -202,7 +202,7 @@ module.exports = grammar({
202
202
  field(
203
203
  "parameters",
204
204
  optional(choice(
205
- // Positional payload, e.g. `Some(Int)`/`Some(T)` — brackets are
205
+ // Positional payload, e.g. `Next(Int)`/`Next(T)` — brackets are
206
206
  // reserved for generics (a type/trait/enum's OWN declared
207
207
  // params, and instantiating one, e.g. `List[Int]`); a variant's
208
208
  // payload always uses parens, same as a class field's type.
@@ -533,9 +533,8 @@ module.exports = grammar({
533
533
  ),
534
534
  ),
535
535
 
536
- // Rust-style error-propagation postfix: `expr?` — unwraps a `Result`'s `Ok`
536
+ // Rust-style error-propagation postfix: `expr?` — unwraps a `Try`'s `Ok`, or exits the enclosing function early with the
537
- // or an `Option`'s `Some`, or exits the enclosing function early with the
538
- // `Err`/`None` value as-is otherwise. Same postfix shape/precedence as
537
+ // `Err` value as-is otherwise. Same postfix shape/precedence as
539
538
  // `attribute` (`.`) just above.
540
539
  try_expression: ($) =>
541
540
  choice(
@@ -553,12 +552,12 @@ module.exports = grammar({
553
552
  ),
554
553
 
555
554
  // Groovy/Kotlin-style safe navigation: `obj?.member` / `obj?.method(args)`
556
- // on a `Result`/`Option` value — same shape as `attribute` just above, but
555
+ // on a `Try` value — same shape as `attribute` just above, but
557
556
  // with a `?.` token in place of `.`. `?.` is its own literal token, so the
558
557
  // lexer's usual longest-match rule already picks it over a bare `?` (the
559
558
  // `try_expression` token) with no extra grammar conflict needed, the same
560
- // way `elvis_expression`'s `?:` needed none. Desugars entirely in
559
+ // way `elvis_expression`'s `??` needed none. Desugars entirely in
561
- // `parser.rs` (into `.map(|v| v.member(...))`, reusing Option/Result's
560
+ // `parser.rs` (into `.map(|v| v.member(...))`, reusing Try's
562
561
  // existing generic `.map`) — nothing past the parser ever sees this node.
563
562
  safe_attribute: ($) =>
564
563
  prec(
@@ -641,9 +640,9 @@ module.exports = grammar({
641
640
  seq($.expression, "?", $.expression, ":", $.expression),
642
641
  ),
643
642
 
644
- // Elvis / null-coalescing: `opt ?: default` — if `left` is `Ok(v)`/`Some(v)`,
643
+ // Elvis / null-coalescing: `opt ?? default` — if `left` is `Ok(v)`,
645
- // the value is `v`; if it's `Err(_)`/`None`, the value is `right`. A single
644
+ // the value is `v`; if it's `Err(_)`, the value is `right`. A single
646
- // atomic `"?:"` token (maximal munch out-munches the bare `"?"` ternary/
645
+ // atomic `"??"` token (maximal munch out-munches the bare `"?"` ternary/
647
646
  // try-operator tokens at the same position, so — unlike `try_expression`,
648
647
  // which genuinely shares ternary's leading `?` and needs the `conflicts`
649
648
  // entry above — this needs no special conflict handling).
plum-wasm-codegen/src/lib.rs CHANGED
@@ -383,7 +383,7 @@ pub struct CompileCtx<'a> {
383
383
  /// wasm-gc type-section indices for this program's enums/Str/closures —
384
384
  /// every value's real representation (see `docs/superpowers/plans/2026-07-25-wasm-gc-migration.md`).
385
385
  pub gc_types: GcTypeRegistry,
386
- /// Payload-free variant name (True/False/None/...) -> the global index holding
386
+ /// Payload-free variant name (True/False/End/...) -> the global index holding
387
387
  /// its one pre-allocated instance (see this migration plan's Decision 2).
388
388
  pub singleton_globals: HashMap<String, u32>,
389
389
  }
@@ -404,7 +404,7 @@ struct LocalCtx<'a> {
404
404
  /// `Match` stmt identity (pointer address) -> scratch slot offset.
405
405
  match_scratch_index: HashMap<usize, u32>,
406
406
  /// First local index reserved for nested-constructor-pattern scratch temporaries
407
- /// (`Some(Some(v))`'s inner `Some(v)`); the outermost pattern uses a
407
+ /// (`Next(Next(v))`'s inner `Next(v)`); the outermost pattern uses a
408
408
  /// `match_scratch` slot instead, so this only covers depth >= 1.
409
409
  nested_class_scratch_base: u32,
410
410
  /// `CasePattern::Class` identity (pointer address) -> scratch slot offset.
@@ -702,7 +702,7 @@ fn classOrVariantTypeIdx(r: &GcTypeRegistry, name: &str) -> u32 {
702
702
  .or_else(|| r.class_type_idx.get(name))
703
703
  // A scratch slot narrowed to a MULTI-variant enum's own wide supertype
704
704
  // (not any one variant) — the `?` operator's scratch local holds the
705
- // subject at its natural (possibly multi-variant, e.g. `Result`/`Option`)
705
+ // subject at its natural (possibly multi-variant, e.g. `Try`)
706
706
  // type, never narrower, since it ref.casts to whichever of the two
707
707
  // variants applies only at the point of use, not up front.
708
708
  .or_else(|| r.enum_super_type_idx.get(name))
@@ -917,11 +917,11 @@ fn plumTypeToGcValtype(t: &PlumType, registry: &GcTypeRegistry) -> ValType {
917
917
  nullable: true,
918
918
  heap_type: HeapType::Concrete(*idx),
919
919
  }),
920
- // A field typed as a generic enum (e.g. `Node.next: Option[Node]`)
920
+ // A field typed as a generic enum (e.g. `Node.next: Link[Node]`)
921
921
  // resolves here to the BARE generic name — `plumTypeFromAst`
922
922
  // has no representation for type arguments, so it can't know this means
923
- // `Option$Int` once monomorphization specializes (and removes the
923
+ // `Link$Int` once monomorphization specializes (and removes the
924
- // unspecialized) `Option` — same permissive `anyref` fallback as
924
+ // unspecialized) `Link` — same permissive `anyref` fallback as
925
925
  // `plumTypeToValtype` above, for the same "genuinely unmodeled" reason:
926
926
  // `struct.get`/`ref.test`/`ref.cast` all work against `anyref` operands
927
927
  // fine, so a field merely being STORED as `anyref` instead of the exact
@@ -943,7 +943,7 @@ fn plumTypeToGcValtype(t: &PlumType, registry: &GcTypeRegistry) -> ValType {
943
943
  /// ONE `rec` group via `module.addGcTypes` — a single group sidesteps every ordering
944
944
  /// question about mutual/self-references (a record-shaped enum's field naming
945
945
  /// another such enum's type, an enum variant field referencing its own enum,
946
- /// `Node.next: Option[Node]`, etc.), since within one `rec` group members may
946
+ /// `Node.next: Link[Node]`, etc.), since within one `rec` group members may
947
947
  /// reference each other regardless of declaration order.
948
948
  fn buildGcTypeRegistry(
949
949
  module: &mut WasmModule,
@@ -1611,7 +1611,7 @@ fn compileSourceInner(
1611
1611
  }
1612
1612
  }
1613
1613
 
1614
- // Pre-allocate one instance of every payload-free variant (True/False/None/...)
1614
+ // Pre-allocate one instance of every payload-free variant (True/False/End/...)
1615
1615
  // as a global, populated once by a `start` function rather than reconstructed on
1616
1616
  // every reference — see this migration plan's Decision 2. `struct.new` isn't
1617
1617
  // allowed inside a global's own const-expr initializer (confirmed empirically in
@@ -2087,7 +2087,7 @@ fn registerStringConcatHelper(module: &mut WasmModule, byte_array_type_idx: u32)
2087
2087
  /// Registers `__string_eq(a: ref Str, b: ref Str) -> i32` (1/0), a hand-written
2088
2088
  /// runtime helper backing `Str`'s `==`/`!=` operator. `Str` is a wasm-gc `ref` —
2089
2089
  /// the general `Compare` codegen's default for reference-typed operands is
2090
- /// `ref.eq` (identity), correct for a payload-free singleton (`None`/`True`) or
2090
+ /// `ref.eq` (identity), correct for a payload-free singleton (`End`/`True`) or
2091
2091
  /// class-instance identity, but WRONG for `Str`: two independently-heap-allocated
2092
2092
  /// `array<i8>` values holding identical bytes (e.g. two `"a"` literals, or one
2093
2093
  /// literal and a runtime-built string) are different objects and would compare
@@ -3352,7 +3352,7 @@ struct Collector<'a> {
3352
3352
  /// One scratch-local type per subject (usually one, more for `match a, b, ...`).
3353
3353
  match_scratch: HashMap<usize, Vec<PlumType>>,
3354
3354
  /// `CasePattern::Class` identity -> its scratch local. Covers EVERY constructor
3355
- /// pattern, including top-level ones — under wasm-gc, matching `Some(v)` needs a
3355
+ /// pattern, including top-level ones — under wasm-gc, matching `Next(v)` needs a
3356
3356
  /// `ref.cast` from the subject's static supertype down to the concrete variant
3357
3357
  /// type before any `struct.get` on it validates, and that narrowed value needs
3358
3358
  /// its OWN local (declared with the concrete variant's ref type) distinct from
@@ -3550,12 +3550,12 @@ impl<'a> Collector<'a> {
3550
3550
  }
3551
3551
  ast::Expr::Not(inner) => self.walkExpr(inner),
3552
3552
  // `expr?` needs a scratch local to hold `inner`'s value across the
3553
- // `ref.test` that decides Ok/Some vs Err/None (which consumes it)
3553
+ // `ref.test` that decides Ok vs Err (which consumes it)
3554
3554
  // and the `ref.cast` that reads it again inside whichever branch —
3555
3555
  // reuses the same pointer-identity-keyed scratch-slot mechanism
3556
3556
  // constructor patterns already use (`nested_class_scratch`), keyed
3557
3557
  // by this `Expr::Try` node itself. Declared with `inner`'s own
3558
- // (possibly multi-variant, e.g. `Result`/`Option`) enum type —
3558
+ // (possibly multi-variant, e.g. `Try`) enum type —
3559
3559
  // `classOrVariantTypeIdx` falls back to `enum_super_type_idx` for
3560
3560
  // exactly this case — since narrowing happens later via `ref.cast`
3561
3561
  // in each branch, not by declaring this local pre-narrowed.
@@ -4383,7 +4383,7 @@ fn exprHasResult(expr: &ast::Expr, ctx: &LocalCtx) -> bool {
4383
4383
  /// patterns" fallback in `compile_match_arms` is provably unreachable code, not a real
4384
4384
  /// gap. `compileMatch` uses this to append a synthetic trap-and-never-fall-through
4385
4385
  /// wildcard arm (rather than let the arms recursion hit its non-exhaustive-match error)
4386
- /// so previously-working exhaustive enum matches (e.g. `Some`/`None`, `True`/`False`)
4386
+ /// so previously-working exhaustive enum matches (e.g. `Next`/`End`, `True`/`False`)
4387
4387
  /// keep compiling even without a trailing wildcard, while a genuinely non-exhaustive
4388
4388
  /// match (an `Int` match, or an enum match missing a variant) still gets a clear error.
4389
4389
  /// True if `cases` already covers every combination of enum variants across all
@@ -5017,7 +5017,7 @@ fn compileVariantConstructorArm(
5017
5017
  }
5018
5018
 
5019
5019
  /// Checks `fields[fpos..]` (a constructor pattern's own sub-patterns, e.g. the `v` in
5020
- /// `Some(v)`, or — recursively — the `Some(v)` in `Wrap(Some(v))`) against the
5020
+ /// `Next(v)`, or — recursively — the `Next(v)` in `Wrap(Next(v))`) against the
5021
5021
  /// already-loaded value in `container_local`, one field at a time. Once every field
5022
5022
  /// has matched, calls `on_match` (typically: proceed to the next top-level subject
5023
5023
  /// position). A mismatch at any field — at any nesting depth — falls through to
@@ -5148,7 +5148,7 @@ fn compileFieldPatterns(
5148
5148
  }
5149
5149
  ast::CasePattern::Name(n) => {
5150
5150
  // An uppercase, payload-free variant name used as a field pattern (e.g.
5151
- // matching a nested `None` rather than binding a name to it).
5151
+ // matching a nested `End` rather than binding a name to it).
5152
5152
  ctx.enum_variants
5153
5153
  .get(n)
5154
5154
  .ok_or_else(|| format!("codegen: unknown enum variant '{}'", n))?;
@@ -5383,8 +5383,8 @@ fn compileExpr(
5383
5383
  ast::Expr::Paren(inner) => {
5384
5384
  compileExpr(inner, body, ctx, state)?;
5385
5385
  }
5386
- // `expr?` — `ref.test` decides Ok/Some (fall through, extract the
5386
+ // `expr?` — `ref.test` decides Ok (fall through, extract the
5387
- // field) vs Err/None (return the already-constructed value AS-IS,
5387
+ // field) vs Err (return the already-constructed value AS-IS,
5388
5388
  // no reconstruction needed). The `then`/`else` branches don't need to
5389
5389
  // agree in wasm's usual sense: after `return`, the rest of that
5390
5390
  // branch is unreachable, so the validator accepts the `if`'s single
@@ -5499,7 +5499,7 @@ fn compileExpr(
5499
5499
  }
5500
5500
  Instruction::End.encode(body);
5501
5501
  }
5502
- // `left ?: right` — same `ref.test`/scratch-local narrowing as `Try`
5502
+ // `left ?? right` — same `ref.test`/scratch-local narrowing as `Try`
5503
5503
  // above, but both branches produce an ordinary value (no `return`):
5504
5504
  // the failure branch compiles `right` (already unified against the
5505
5505
  // success field's type by the checker), the success branch extracts
@@ -5723,7 +5723,7 @@ fn compileExpr(
5723
5723
  },
5724
5724
  // Bool/class/enum values are wasm-gc refs — `==`/`!=` compares
5725
5725
  // reference identity via `ref.eq`. That's exactly right for a
5726
- // payload-free singleton (`None`/`True`/`False`, this migration
5726
+ // payload-free singleton (`End`/`True`/`False`, this migration
5727
5727
  // plan's Decision 2) and for class-instance identity.
5728
5728
  // Ordering a ref type has no meaning and was never valid.
5729
5729
  _ => match &c.op {
@@ -6002,7 +6002,7 @@ fn compileExpr(
6002
6002
  compileExpr(&synthetic, body, ctx, state)?;
6003
6003
  }
6004
6004
  // A NAMED-payload enum variant construction (`Circle(radius: 5)`, as
6005
- // opposed to the positional/generic-payload form `Some[T]` — constructed
6005
+ // opposed to the positional/generic-payload form `Next(T)` — constructed
6006
6006
  // via `Expr::FnCall` instead, elsewhere) — mirrors `plum-checker`'s own
6007
6007
  // `inferClassCallRaw` handling of the same call shape. Against the
6008
6008
  // variant's own GC struct type (`gc_types.variant_type_idx`), matched by
@@ -6342,8 +6342,8 @@ fn compileExpr(
6342
6342
  Ok(())
6343
6343
  }
6344
6344
 
6345
- /// Compiles a variant-construction call. A payload-free variant (`None`, called as
6345
+ /// Compiles a variant-construction call. A payload-free variant (`End`, called as
6346
- /// `None()` rather than used bare) is its pre-allocated singleton global. A payload
6346
+ /// `End()` rather than used bare) is its pre-allocated singleton global. A payload
6347
6347
  /// variant pushes its field values in order and does `struct.new` into its own
6348
6348
  /// concrete variant type.
6349
6349
  /// Compiles the missing TRAILING arguments of a call to `func_key` (a plain
plum-wasm-codegen/tests/codegen_tests.rs CHANGED
@@ -545,7 +545,7 @@ fn wasmtimeGcConfigSupportsSubtypingRefTestAndNullableSelfReferentialFields() {
545
545
  // Deeper empirical check: enum-variant subtyping (abstract supertype + concrete
546
546
  // subtypes in one `rec` group), ref.test-based dispatch, ref.cast to narrow to a
547
547
  // subtype, and a nullable field that references the struct's OWN type (the
548
- // Node.next: Option[Node] shape List needs) — all in one hand-encoded module,
548
+ // Node.next: Link[Node] shape List needs) — all in one hand-encoded module,
549
549
  // bypassing plum entirely.
550
550
  use wasm_encoder::*;
551
551
 
@@ -553,8 +553,8 @@ fn wasmtimeGcConfigSupportsSubtypingRefTestAndNullableSelfReferentialFields() {
553
553
  let mut types = TypeSection::new();
554
554
 
555
555
  // rec group: type 0 = abstract enum supertype (empty struct, non-final so it can
556
- // be subtyped); type 1 = concrete "Some"-like subtype with one i32 payload field;
556
+ // be subtyped); type 1 = concrete "Next"-like subtype with one i32 payload field;
557
- // type 2 = concrete "None"-like subtype (empty, no payload).
557
+ // type 2 = concrete "End"-like subtype (empty, no payload).
558
558
  types.ty().rec(vec![
559
559
  SubType {
560
560
  is_final: false,
@@ -619,7 +619,7 @@ fn wasmtimeGcConfigSupportsSubtypingRefTestAndNullableSelfReferentialFields() {
619
619
  },
620
620
  }]);
621
621
 
622
- // type 4: () -> i32 — constructs a "Some"-like subtype (type 1) holding 42,
622
+ // type 4: () -> i32 — constructs a "Next"-like subtype (type 1) holding 42,
623
623
  // stores it as the supertype (type 0), ref.tests it against type 1, then
624
624
  // ref.casts and struct.gets the payload back out. Also builds a 2-node linked
625
625
  // list (type 3) and confirms unlinking (overwriting `next` with ref.null) and
@@ -653,7 +653,7 @@ fn wasmtimeGcConfigSupportsSubtypingRefTestAndNullableSelfReferentialFields() {
653
653
  let locals_super = 0u32;
654
654
  let locals_node = 1u32;
655
655
 
656
- // local_super = Some(42) (as the supertype)
656
+ // local_super = Next(42) (as the supertype)
657
657
  f.instruction(&Instruction::I32Const(42));
658
658
  f.instruction(&Instruction::StructNew(1));
659
659
  f.instruction(&Instruction::LocalSet(locals_super));
@@ -722,7 +722,7 @@ fn wasmtimeGcConfigSupportsSubtypingRefTestAndNullableSelfReferentialFields() {
722
722
  #[test]
723
723
  fn wasmtimeGcConfigAllowsStructNewInGlobalConstExpr() {
724
724
  // Decision 2 of the wasm-gc migration plan pre-allocates payload-free enum
725
- // variants (True/False/None/...) once as globals. Confirm a global's
725
+ // variants (True/False/End/...) once as globals. Confirm a global's
726
726
  // initializer expression can directly use struct.new (not just i32.const/
727
727
  // ref.null), or the plan needs a `start` function fallback instead.
728
728
  use wasm_encoder::*;