plum

#treesitter#compiler#wasm

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

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


401e713 — Peter John 2026-11-01T20:52:26+05:30
T8: migrate inline payload variants to record members; legacy syntax errors on
plum-core/src/loader.rs CHANGED
@@ -253,6 +253,37 @@ fn mergeItems(
253
253
  names: &mut HashMap<String, PathBuf>,
254
254
  prims: &mut Vec<PrimitiveDecl>,
255
255
  ) -> Result<(), String> {
256
+ // Removed forms (inline variant payloads, lone self-named variant).
257
+ // TODO(T9): remove Json exemption
258
+ if !file.to_string_lossy().ends_with("plum-std/Json.plum") {
259
+ // A payload-less self-named single variant (`enum Array[T] = | Array`)
260
+ // is an opaque builtin handle and stays legal.
261
+ let opaque: Vec<String> = source
262
+ .items
263
+ .iter()
264
+ .filter_map(|i| match i {
265
+ Item::Enum(e)
266
+ if e.variants.len() == 1
267
+ && e.variants[0].name == e.name
268
+ && e.variants[0].fields.is_empty()
269
+ && e.variants[0].values.is_empty() =>
270
+ {
271
+ Some(format!(
272
+ "enum {0} has a single variant named {0} — write record {0}(...)",
273
+ e.name
274
+ ))
275
+ }
276
+ _ => None,
277
+ })
278
+ .collect();
279
+ let legacy: Vec<String> = crate::unions::legacyErrors(source)
280
+ .into_iter()
281
+ .filter(|m| !opaque.contains(m))
282
+ .collect();
283
+ if !legacy.is_empty() {
284
+ return Err(legacy.join("\n"));
285
+ }
286
+ }
256
287
  // Parse-time literal errors (out-of-range integers) of every merged file.
257
288
  MERGED_LIT_ERRORS.with(|e| e.borrow_mut().extend(source.formErrors.iter().cloned()));
258
289
  // Namespaces (a `primitive` whose name is no built-in) travel with the
plum-core/tests/loader_test.rs CHANGED
@@ -378,3 +378,32 @@ fn outOfRangeLiteralInEntryOrImportSurvivesTheMerge() {
378
378
  let merged = loadAndMerge(&entry, &lib_path).expect("loadAndMerge failed");
379
379
  assert!(merged.formErrors.iter().any(|e| e.2.contains("out of range")), "{:?}", merged.formErrors);
380
380
  }
381
+
382
+ #[test]
383
+ fn legacyInlinePayloadIsAnError() {
384
+ let dir = TempDir::new().unwrap();
385
+ let lib_path = dir.path().join("libs");
386
+ let entry = writeLibFile(
387
+ &lib_path,
388
+ "main",
389
+ "module fixtures\n\nenum Shape =\n | Circle(r: Unit)\n | Dot\n",
390
+ );
391
+ let err = loadAndMerge(&entry, &lib_path).unwrap_err();
392
+ assert_eq!(
393
+ err,
394
+ "inline variant payloads are removed: declare record Circle(...) and list Circle in enum Shape"
395
+ );
396
+ }
397
+
398
+ #[test]
399
+ fn legacySingleSelfVariantIsAnError() {
400
+ let dir = TempDir::new().unwrap();
401
+ let lib_path = dir.path().join("libs");
402
+ let entry = writeLibFile(
403
+ &lib_path,
404
+ "main",
405
+ "module fixtures\n\nenum User =\n | User(name: Unit)\n",
406
+ );
407
+ let err = loadAndMerge(&entry, &lib_path).unwrap_err();
408
+ assert_eq!(err, "enum User has a single variant named User — write record User(...)");
409
+ }
plum-examples/constants.plum CHANGED
@@ -20,8 +20,7 @@ primitive Grid =
20
20
  fun cells() -> Int =
21
21
  W * H
22
22
 
23
- enum Tile =
24
- | Tile(x: Int)
23
+ record Tile(x: Int) =
25
24
 
26
25
  W = 100
27
26
 
@@ -30,8 +29,7 @@ enum Tile =
30
29
  fun origin() -> Tile =
31
30
  Tile(x: 0)
32
31
 
33
- enum Wrap[T] =
32
+ record Wrap[T](v: T) =
34
- | Wrap(v: T)
35
33
 
36
34
  fun area(self) -> Int = Grid.W * Grid.H
37
35
 
plum-examples/control_flow.plum CHANGED
@@ -61,9 +61,9 @@ fun describeMagnitude(n: Int) -> Int =
61
61
  return 100
62
62
  x => x * 2
63
63
 
64
- enum TailOption =
64
+ enum TailOption = TailSome | TailNone
65
+
65
- | TailSome(Int)
66
+ record TailSome(v: Int)
66
- | TailNone
67
67
 
68
68
  fun unwrapTailOr(o: TailOption, default: Int) -> Int =
69
69
  match o
plum-examples/digit_names.plum CHANGED
@@ -7,10 +7,12 @@ import std/Int
7
7
  # `MAX`-style consts keep working.
8
8
 
9
9
  enum V2 =
10
- | A1(n: Int)
10
+ | A1
11
11
  | B2
12
12
  K1 = 40
13
13
 
14
+ record A1(n: Int)
15
+
14
16
  error S1 = E1 | E2
15
17
 
16
18
  fun fail(n: Int) -> S1!Int =
plum-examples/dop_visitor.plum CHANGED
@@ -41,15 +41,17 @@ import std/Int
41
41
  import std/Float
42
42
  import std/Str
43
43
 
44
- enum Rating =
44
+ enum Rating = Good | Bad
45
+
45
- | Good(name: Str)
46
+ record Good(name: Str)
46
- | Bad(name: Str)
47
+ record Bad(name: Str)
47
-
48
+
48
- enum Book =
49
+ enum Book = FantasyBook | ScifiBook | ChildrensTaleBook | NonFictionBook
50
+
49
- | FantasyBook(title: Str, pages: Int, hasMythicalCreatures: Bool)
51
+ record FantasyBook(title: Str, pages: Int, hasMythicalCreatures: Bool)
50
- | ScifiBook(title: Str, pages: Int, theme: Str)
52
+ record ScifiBook(title: Str, pages: Int, theme: Str)
51
- | ChildrensTaleBook(title: Str, pages: Int, moralLesson: Str)
53
+ record ChildrensTaleBook(title: Str, pages: Int, moralLesson: Str)
52
- | NonFictionBook(title: Str, pages: Int, rating1: Rating, rating2: Rating)
54
+ record NonFictionBook(title: Str, pages: Int, rating1: Rating, rating2: Rating)
53
55
 
54
56
  fun collectInterestingInfo(book: Book) -> Str =
55
57
  match book
plum-examples/error_propagation.plum CHANGED
@@ -97,10 +97,9 @@ fun describeAge(s: Str) -> Str =
97
97
  "age {n}"
98
98
 
99
99
  # A plain list of cases is an ordinary enum.
100
- enum Failure =
100
+ enum Failure = Timeout | Refused | BadRequest
101
- | Timeout
101
+
102
- | Refused
103
- | BadRequest(Str)
102
+ record BadRequest(v: Str)
104
103
 
105
104
  fun isRetryable(f: Failure) -> Bool =
106
105
  match f
plum-examples/functions.plum CHANGED
@@ -65,9 +65,9 @@ record GenericBox2[T](value: T) =
65
65
  fun doubled(value: Int) -> Int =
66
66
  identity(value) + identity(value)
67
67
 
68
- enum Parcel[T] =
68
+ enum Parcel[T] = Delivered[T] | Lost
69
+
69
- | Delivered(T)
70
+ record Delivered[T](v: T)
70
- | Lost
71
71
 
72
72
  fun contentsOr(p: Parcel[Int], default: Int) -> Int =
73
73
  match p
plum-examples/match.plum CHANGED
@@ -32,9 +32,9 @@ fun describeColor(c: Color) -> Str =
32
32
 
33
33
  # A dedicated, non-generic "maybe an Int", as opposed to the generic
34
34
  # generic `Reading[T]` (used elsewhere in this file).
35
- enum IntOpt =
35
+ enum IntOpt = Present | Absent
36
+
36
- | Present(Int)
37
+ record Present(v: Int)
37
- | Absent
38
38
 
39
39
  fun describeOption(opt: IntOpt) -> Int =
40
40
  match opt
@@ -46,33 +46,38 @@ fun main() -> Int =
46
46
 
47
47
  # ---- pattern matching regression tests ----
48
48
 
49
- enum Nested =
49
+ enum Nested = Wrap | Empty
50
+
50
- | Wrap(IntOpt)
51
+ record Wrap(v: IntOpt)
51
- | Empty
52
52
 
53
53
  fun unwrapNested(n: Nested) -> Int =
54
54
  match n
55
55
  Wrap(Present(v)) => v
56
56
  Wrap(Absent) => -1
57
+ Wrap(_) => -2
57
58
  Empty => 0
58
59
 
59
- enum Reading[T] =
60
+ enum Reading[T] = Sample[T] | NoSignal
61
+
60
- | Sample(T)
62
+ record Sample[T](v: T)
63
+
64
+ enum GenericBox[T] = GFull[T] | GEmpty
65
+
61
- | NoSignal
66
+ record GFull[T](v: T)
62
67
 
63
- enum GenericBox[T] =
68
+ fun sampleOf(n: Int) -> Reading[Int] =
64
- | GFull(T)
69
+ Sample(n)
65
- | GEmpty
66
70
 
67
71
  fun unwrapGenericBox(b: GenericBox[Reading[Int]]) -> Int =
68
72
  match b
69
73
  GFull(Sample(v)) => v
70
74
  GFull(NoSignal) => -1
75
+ GFull(_) => -2
71
76
  GEmpty => 0
72
77
 
73
- enum RecOption =
78
+ enum RecOption = RSome | RNone
79
+
74
- | RSome(RecOption)
80
+ record RSome(v: RecOption)
75
- | RNone
76
81
 
77
82
  fun unwrapTwice(o: RecOption) -> Int =
78
83
  match o
@@ -144,13 +149,17 @@ record Point(x: Int, y: Int) =
144
149
  Point(_, _) => "elsewhere"
145
150
 
146
151
  enum Shape =
147
- | Circle(Point)
152
+ | Circle
148
- | Square(Point)
153
+ | Square
149
154
 
150
155
  fun measure(self) -> Int =
151
156
  match self
152
157
  Circle(Point(x, y)) => x + y
153
158
  Square(Point(x, y)) => x * y
159
+ _ => 0
160
+
161
+ record Circle(v: Point)
162
+ record Square(v: Point)
154
163
 
155
164
  # Named payload fields (`Ring(radius: Int)`) alongside the
156
165
  # unnamed-positional-payload form (`Circle(Point)` above) — lets a
@@ -158,14 +167,23 @@ enum Shape =
158
167
  # anonymous positional types. Pattern matching is unaffected either way
159
168
  # (still positional, by declaration order — `Rect(w, h)`, not `Rect(w:, h:)`).
160
169
  enum Figure =
161
- | Ring(radius: Int)
170
+ | Ring
162
- | Rect(w: Int, h: Int)
171
+ | Rect
163
172
 
164
173
  fun area(self) -> Int =
165
174
  match self
166
175
  Ring(radius) => radius * radius
167
176
  Rect(w, h) => w * h
168
177
 
178
+ record Ring(radius: Int)
179
+ record Rect(w: Int, h: Int)
180
+
181
+ fun measureOf(s: Shape) -> Int =
182
+ s.measure()
183
+
184
+ fun areaOf(f: Figure) -> Int =
185
+ f.area()
186
+
169
187
  test "match example computes correctly"
170
188
  assert main() == 5
171
189
 
@@ -176,7 +194,7 @@ test "nested constructor pattern mismatch falls through to next case runs correc
176
194
  assert unwrapNested(Wrap(Absent)) == -1
177
195
 
178
196
  test "nested constructor pattern against a specialized generic enum runs correctly"
179
- assert unwrapGenericBox(GFull(Sample(7))) == 7
197
+ assert unwrapGenericBox(GFull(sampleOf(7))) == 7
180
198
 
181
199
  test "doubly nested constructor pattern runs correctly"
182
200
  assert unwrapTwice(RSome(RSome(RNone))) == 1
@@ -220,16 +238,16 @@ test "match on a plain class supports literal/wildcard sub-patterns and case fal
220
238
  assert Point(x: 5, y: 5).classify() == "elsewhere"
221
239
 
222
240
  test "a plain class nested inside an enum variant pattern destructures correctly"
223
- assert Circle(Point(x: 3, y: 4)).measure() == 7
241
+ assert measureOf(Circle(Point(x: 3, y: 4))) == 7
224
- assert Square(Point(x: 3, y: 4)).measure() == 12
242
+ assert measureOf(Square(Point(x: 3, y: 4))) == 12
225
243
 
226
244
  test "named-payload enum variant constructs via named args and destructures by position"
227
- assert Ring(radius: 5).area() == 25
245
+ assert areaOf(Ring(radius: 5)) == 25
228
- assert Rect(w: 3, h: 4).area() == 12
246
+ assert areaOf(Rect(w: 3, h: 4)) == 12
229
247
 
230
248
  test "named-payload enum variant still supports positional construction too"
231
- assert Ring(5).area() == 25
249
+ assert areaOf(Ring(5)) == 25
232
- assert Rect(3, 4).area() == 12
250
+ assert areaOf(Rect(3, 4)) == 12
233
251
 
234
252
  test "a pattern name reused with different types in one fn"
235
253
  r := Sample(1)
plum-examples/oop_visitor.plum CHANGED
@@ -33,9 +33,10 @@ import std/Int
33
33
  import std/Float
34
34
  import std/Str
35
35
 
36
- enum Rating =
36
+ enum Rating = Good | Bad
37
+
37
- | Good(name: Str)
38
+ record Good(name: Str)
38
- | Bad(name: Str)
39
+ record Bad(name: Str)
39
40
 
40
41
  record FantasyBook(title: Str, pages: Int, hasMythicalCreatures: Bool) =
41
42
 
plum-examples/pattern_if.plum CHANGED
@@ -13,19 +13,18 @@ import std/Str
13
13
  #
14
14
  # A plain name over an optional (`x := opt`) keeps its meaning: bind when present.
15
15
 
16
- enum Shape =
16
+ enum Shape = Circle | Rect | Dot
17
+
17
- | Circle(radius: Float)
18
+ record Circle(radius: Float)
18
- | Rect(w: Float, h: Float)
19
+ record Rect(w: Float, h: Float)
19
- | Dot
20
20
 
21
21
  struct Vec2(x: Int, y: Int)
22
22
 
23
- enum Job =
23
+ enum Job = Idle | Busy
24
- | Idle
24
+
25
- | Busy(left: Int)
25
+ record Busy(left: Int)
26
26
 
27
- enum Soldier =
28
- | Soldier(hp: Int, shape: Shape)
27
+ record Soldier(hp: Int, shape: Shape)
29
28
 
30
29
  fun area(s: Shape) -> Float =
31
30
  if Circle(r) := s
plum-examples/shapes.plum CHANGED
@@ -5,8 +5,8 @@ import std/Float
5
5
  # A closed set of shapes as one enum: mixed kinds live in a single type and
6
6
  # `match` picks the behavior per variant.
7
7
  enum Shape =
8
- | Circle(r: Float)
8
+ | Circle
9
- | Rect(w: Float, h: Float)
9
+ | Rect
10
10
 
11
11
  fun area(self) -> Float =
12
12
  match self
@@ -18,10 +18,19 @@ enum Shape =
18
18
  Circle(r) => "circle"
19
19
  Rect(w, h) => "rect"
20
20
 
21
+ record Circle(r: Float)
22
+ record Rect(w: Float, h: Float)
23
+
24
+ fun areaOf(s: Shape) -> Float =
25
+ s.area()
26
+
27
+ fun nameOf(s: Shape) -> Str =
28
+ s.name()
29
+
21
30
  test "area dispatches on the variant"
22
- assert Circle(r: 1.5).area() == 6.75
31
+ assert areaOf(Circle(r: 1.5)) == 6.75
23
- assert Rect(w: 2.0, h: 3.0).area() == 6.0
32
+ assert areaOf(Rect(w: 2.0, h: 3.0)) == 6.0
24
33
 
25
34
  test "name dispatches on the variant"
26
- assert Circle(r: 1.0).name() == "circle"
35
+ assert nameOf(Circle(r: 1.0)) == "circle"
27
- assert Rect(w: 1.0, h: 1.0).name() == "rect"
36
+ assert nameOf(Rect(w: 1.0, h: 1.0)) == "rect"
plum-examples/types.plum CHANGED
@@ -70,9 +70,10 @@ fun stepToNumber(s: Step) -> Int =
70
70
  fun unwrapOrDefault(o: Int?, default: Int) -> Int =
71
71
  o ?: default
72
72
 
73
- enum ShapeKind =
73
+ enum ShapeKind = Rect | Circle
74
+
74
- | Rect(Int, Int)
75
+ record Rect(v0: Int, v1: Int)
75
- | Circle(Int)
76
+ record Circle(v: Int)
76
77
 
77
78
  fun area(s: ShapeKind) -> Int =
78
79
  match s
@@ -83,9 +84,10 @@ fun area(s: ShapeKind) -> Int =
83
84
 
84
85
  record Vec2(x: Int, y: Int)
85
86
 
86
- enum ShapeWithFields =
87
+ enum ShapeWithFields = CircleField | SquareField
88
+
87
- | CircleField(radius: Int)
89
+ record CircleField(radius: Int)
88
- | SquareField(side: Int)
90
+ record SquareField(side: Int)
89
91
 
90
92
  record MaybeBox(value: Int?) =
91
93
 
plum-examples/values.plum CHANGED
@@ -36,8 +36,7 @@ struct Color(r: Int, g: Int, b: Int, a: Int)
36
36
 
37
37
  struct Cell(pos: Vec2, solid: Bool, tint: Color)
38
38
 
39
- enum Player =
40
- | Player(pos: Vec2, hp: Int)
39
+ record Player(pos: Vec2, hp: Int)
41
40
 
42
41
  fun applyTo(v: Vec2, cb: fn(Vec2) -> Vec2) -> Vec2 =
43
42
  cb(v)
plum-wasm-codegen/tests/codegen_tests.rs CHANGED
@@ -230,8 +230,7 @@ fn floatArithmeticAndNegationCompile() {
230
230
  #[test]
231
231
  fn classFieldAndMethodCompile() {
232
232
  let src = "\
233
- enum Cat =
234
- | Cat(name: Str, age: Int)
233
+ record Cat(name: Str, age: Int) =
235
234
 
236
235
  fun getAge() -> Int =
237
236
  self.age
@@ -246,11 +245,9 @@ fun makeCat() -> Int =
246
245
  #[test]
247
246
  fn nestedClassCallCompiles() {
248
247
  let src = "\
249
- enum Pair =
250
- | Pair(a: Int, b: Int)
248
+ record Pair(a: Int, b: Int)
251
249
 
252
- enum Wrapper =
253
- | Wrapper(inner: Pair, tag: Int)
250
+ record Wrapper(inner: Pair, tag: Int)
254
251
 
255
252
  fun make() -> Int =
256
253
  w = Wrapper(inner: Pair(a: 1, b: 2), tag: 9)
@@ -1113,14 +1110,17 @@ import std/Float
1113
1110
  import std/List
1114
1111
 
1115
1112
  enum Shape =
1116
- | Circle(r: Float)
1113
+ | Circle
1117
- | Square(s: Float)
1114
+ | Square
1118
1115
 
1119
1116
  fun area(self) -> Float =
1120
1117
  match self
1121
1118
  Circle(r) => r * r * 3.0
1122
1119
  Square(s) => s * s
1123
1120
 
1121
+ record Circle(r: Float)
1122
+ record Square(s: Float)
1123
+
1124
1124
  fun total(xs: List[Shape]) -> Float =
1125
1125
  sum := 0.0
1126
1126
  for x := range xs
@@ -1611,8 +1611,7 @@ fun main() -> Int =
1611
1611
  #[test]
1612
1612
  fn fn_typed_field_call() {
1613
1613
  let src = format!("{}\
1614
- enum Ops =
1615
- | Ops(inc: fn(Int) -> Int, base: Int)
1614
+ record Ops(inc: fn(Int) -> Int, base: Int)
1616
1615
 
1617
1616
  fun main() -> Int =
1618
1617
  o := Ops(inc: |n| n + 1, base: 40)
@@ -1624,8 +1623,7 @@ fun main() -> Int =
1624
1623
  #[test]
1625
1624
  fn method_beats_field_precedence() {
1626
1625
  let src = format!("{}\
1627
- enum Combo =
1628
- | Combo(add: fn(Int, Int) -> Int)
1626
+ record Combo(add: fn(Int, Int) -> Int) =
1629
1627
 
1630
1628
  fun add(self, a: Int, b: Int) -> Int =
1631
1629
  a * b
@@ -1652,8 +1650,7 @@ fun main() -> Int =
1652
1650
  #[test]
1653
1651
  fn record_of_fns_passed_explicitly() {
1654
1652
  let src = format!("{}{}\
1655
- enum Num[T] =
1656
- | Num(add: fn(T, T) -> T, zero: T)
1653
+ record Num[T](add: fn(T, T) -> T, zero: T)
1657
1654
 
1658
1655
  fun sumWith(xs: List[T], num: Num[T]) -> T =
1659
1656
  reduceList(xs, num.zero, |a, b| num.add(a, b))
@@ -1672,8 +1669,7 @@ fun main() -> Int =
1672
1669
  }
1673
1670
 
1674
1671
  const NULLABLE_SRC: &str = "\
1675
- enum Person =
1676
- | Person(name: Str, age: Int)
1672
+ record Person(name: Str, age: Int)
1677
1673
 
1678
1674
  fun findNullable(n: Int) -> Person? =
1679
1675
  if n < 0
@@ -1734,8 +1730,7 @@ fun main() -> Int =
1734
1730
  #[test]
1735
1731
  fn nullable_turns_into_an_error_with_elvis() {
1736
1732
  let src = format!("{}{}", STD_HEAD, "\
1737
- enum Person =
1738
- | Person(name: Str, age: Int)
1733
+ record Person(name: Str, age: Int)
1739
1734
 
1740
1735
  fun findIt(n: Int) -> Person? =
1741
1736
  if n < 0
@@ -1897,8 +1892,7 @@ fn fn_field_nested_call_arg() {
1897
1892
  let src = format!("{}\
1898
1893
  import std/Array
1899
1894
 
1900
- enum Keyed[T] =
1901
- | Keyed(items: List[T], key: fn(T) -> Int)
1895
+ record Keyed[T](items: List[T], key: fn(T) -> Int) =
1902
1896
 
1903
1897
  fun first(self) -> Int =
1904
1898
  self.key(self.items.data.get(0))
@@ -1947,7 +1941,7 @@ fn local_value_subfield_assign() {
1947
1941
  #[test]
1948
1942
  fn heap_field_value_subfield_assign() {
1949
1943
  let src = format!(
1950
- "{VEC2}enum Player =\n | Player(pos: Vec2, hp: Int)\n\nstruct Rect(pos: Vec2, size: Vec2)\n\nenum Body =\n | Body(bounds: Rect, id: Int)\n\nfun main() -> Int =\n p := Player(pos: Vec2(x: 1.0, y: 2.0), hp: 5)\n p.pos.x = 8.0\n b := Body(bounds: Rect(pos: Vec2(x: 1.0, y: 2.0), size: Vec2(x: 3.0, y: 4.0)), id: 1)\n b.bounds.size.y = 6.0\n if p.pos.x == 8.0 && p.pos.y == 2.0 && p.hp == 5 && b.bounds.size.y == 6.0 && b.bounds.size.x == 3.0 && b.bounds.pos.y == 2.0\n return 1\n return 0\n"
1944
+ "{VEC2}record Player(pos: Vec2, hp: Int)\n\nstruct Rect(pos: Vec2, size: Vec2)\n\nrecord Body(bounds: Rect, id: Int)\n\nfun main() -> Int =\n p := Player(pos: Vec2(x: 1.0, y: 2.0), hp: 5)\n p.pos.x = 8.0\n b := Body(bounds: Rect(pos: Vec2(x: 1.0, y: 2.0), size: Vec2(x: 3.0, y: 4.0)), id: 1)\n b.bounds.size.y = 6.0\n if p.pos.x == 8.0 && p.pos.y == 2.0 && p.hp == 5 && b.bounds.size.y == 6.0 && b.bounds.size.x == 3.0 && b.bounds.pos.y == 2.0\n return 1\n return 0\n"
1951
1945
  );
1952
1946
  assert_eq!(runMerged::<i64>("heap_field_value_subfield_assign", &src), 1);
1953
1947
  }
@@ -1957,7 +1951,7 @@ fn value_nested_pattern_mixed() {
1957
1951
  // Whole-value binding next to a nested destructure, and a heap container
1958
1952
  // (class-like enum) holding a value-enum field.
1959
1953
  let src = format!(
1960
- "{VEC2}struct Rect(pos: Vec2, size: Vec2)\n\nenum Player =\n | Player(pos: Vec2, hp: Float)\n\nfun f(r: Rect) -> Float =\n match r\n Rect(p, Vec2(w, h)) => p.x + p.y * 10.0 + w * 100.0 + h * 1000.0\n\nfun g(p: Player) -> Float =\n match p\n Player(Vec2(x, y), hp) => x + y * 10.0 + hp * 100.0\n\nfun main() -> Int =\n r := Rect(pos: Vec2(x: 1.0, y: 2.0), size: Vec2(x: 3.0, y: 4.0))\n p := Player(pos: Vec2(x: 1.0, y: 2.0), hp: 3.0)\n if f(r) == 4321.0 && g(p) == 321.0\n return 1\n return 0\n"
1954
+ "{VEC2}struct Rect(pos: Vec2, size: Vec2)\n\nrecord Player(pos: Vec2, hp: Float)\n\nfun f(r: Rect) -> Float =\n match r\n Rect(p, Vec2(w, h)) => p.x + p.y * 10.0 + w * 100.0 + h * 1000.0\n\nfun g(p: Player) -> Float =\n match p\n Player(Vec2(x, y), hp) => x + y * 10.0 + hp * 100.0\n\nfun main() -> Int =\n r := Rect(pos: Vec2(x: 1.0, y: 2.0), size: Vec2(x: 3.0, y: 4.0))\n p := Player(pos: Vec2(x: 1.0, y: 2.0), hp: 3.0)\n if f(r) == 4321.0 && g(p) == 321.0\n return 1\n return 0\n"
1961
1955
  );
1962
1956
  assert_eq!(runMerged::<i64>("value_nested_pattern_mixed", &src), 1);
1963
1957
  }
@@ -2192,8 +2186,7 @@ fun main() -> Int =
2192
2186
  fn fun_star_method_no_closure() {
2193
2187
  // A `fun*` method is expanded at the use site: no closure, no call_indirect, `self` bound once.
2194
2188
  let src = format!("{}\
2195
- enum Bag =
2196
- | Bag(items: List[Int])
2189
+ record Bag(items: List[Int]) =
2197
2190
 
2198
2191
  fun* doubled(self) *Int =
2199
2192
  for x := range self.items
@@ -2595,8 +2588,7 @@ fun main() -> Int =
2595
2588
  #[test]
2596
2589
  fn error_union_in_class_field() {
2597
2590
  let src = "\
2598
- enum Box =
2599
- | Box(v: !Int)
2591
+ record Box(v: !Int)
2600
2592
 
2601
2593
  fun f(n: Int) -> !Int =
2602
2594
  if n < 0
@@ -2614,8 +2606,7 @@ fun main() -> Int =
2614
2606
  #[test]
2615
2607
  fn safe_nav_on_error_union() {
2616
2608
  let src = "\
2617
- enum User =
2618
- | User(age: Int)
2609
+ record User(age: Int)
2619
2610
 
2620
2611
  fun load(n: Int) -> !User =
2621
2612
  if n < 0
@@ -2638,8 +2629,7 @@ import std/Str
2638
2629
  import std/Int
2639
2630
  import std/Float
2640
2631
 
2641
- enum Item =
2642
- | Item(v: Int)
2632
+ record Item(v: Int)
2643
2633
 
2644
2634
  fun find(n: Int) -> Item? =
2645
2635
  if n < 0
@@ -2704,8 +2694,7 @@ import std/Str
2704
2694
  import std/Int
2705
2695
  import std/Float
2706
2696
 
2707
- enum Person =
2708
- | Person(name: Str, age: Int)
2697
+ record Person(name: Str, age: Int)
2709
2698
 
2710
2699
  fun nameOf(p: Person?) -> Str? =
2711
2700
  p?.name
@@ -2809,8 +2798,7 @@ import std/Str
2809
2798
  import std/Int
2810
2799
  import std/Float
2811
2800
 
2812
- enum Item =
2813
- | Item(v: Int)
2801
+ record Item(v: Int)
2814
2802
 
2815
2803
  fun mi(n: Int) -> Int? =
2816
2804
  if n > 0
plum-wasm-codegen/tests/examples_test.rs CHANGED
@@ -249,7 +249,7 @@ fn const_table_1000_entries() {
249
249
  src.push_str(&format!(" E{} = {}\n", i, i * 3 + 1));
250
250
  }
251
251
  src.push('\n');
252
- src.push_str("enum Holder =\n | Holder(n: Int)\n\n fun at(self) -> Int = Table.E499 + self.n\n\nprimitive More =\n");
252
+ src.push_str("record Holder(n: Int) =\n\n fun at(self) -> Int = Table.E499 + self.n\n\nprimitive More =\n");
253
253
  for i in 500..1000 {
254
254
  src.push_str(&format!(" E{} = {}\n", i, i * 3 + 1));
255
255
  }