plum

#treesitter#compiler#wasm

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

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


e91520f — Peter John 2026-11-01T23:09:45+05:30
test: dedupe codegen Rust tests (152 -> 82); drop unused greetForInterpolationTest
plum-std/Str.plum CHANGED
@@ -635,9 +635,6 @@ fun deburrLatinExtA() -> Str =
635
635
  # `"{expr}"` interpolation is a compiler/codegen feature (not a Str method
636
636
  # above), but Str is what it produces, so its regression coverage lives here.
637
637
 
638
- fun greetForInterpolationTest(name: Str) -> Str =
639
- "Hello, {name}!"
640
-
641
638
  fun bigAscii(n: Int, b: Int) -> Str =
642
639
  buf := Buffer()
643
640
  i := 0
plum-wasm-codegen/tests/codegen_tests.rs CHANGED
@@ -26,16 +26,6 @@ fn gcEngine() -> wasmtime::Engine {
26
26
  wasmtime::Engine::new(&config).expect("engine with GC config should construct")
27
27
  }
28
28
 
29
- #[test]
30
- fn compilesToValidWasm() {
31
- let src = "fun add(a: Int, b: Int) -> Int =\n a + b\n";
32
- let source = parse(src);
33
- let bytes = compileSource(&source).expect("compile failed");
34
- // Valid WASM starts with the magic number
35
- assert_eq!(&bytes[0..4], b"\0asm");
36
- assert_eq!(&bytes[4..8], &[1, 0, 0, 0]); // version 1
37
- }
38
-
39
29
  #[test]
40
30
  fn outputValidates() {
41
31
  let src = "fun add(a: Int, b: Int) -> Int =\n a + b\n";
@@ -46,31 +36,6 @@ fn outputValidates() {
46
36
  assert!(result.is_ok(), "wasm validation failed: {:?}", result.err());
47
37
  }
48
38
 
49
- #[test]
50
- fn factorialCompiles() {
51
- let src = "\
52
- fun factorial(x: Int) -> Int =
53
- if x < 2
54
- return 1
55
- return x * factorial(x - 1)
56
- ";
57
- let source = parse(src);
58
- let bytes = compileSource(&source).expect("factorial should compile");
59
- assert_eq!(&bytes[0..4], b"\0asm");
60
- let result = wasmparser::validate(&bytes);
61
- assert!(result.is_ok(), "wasm validation failed: {:?}", result.err());
62
- }
63
-
64
- #[test]
65
- fn give42CompilesAndExports() {
66
- let src = "fun give42() -> Int =\n 42\n";
67
- let source = parse(src);
68
- let bytes = compileSource(&source).expect("compile failed");
69
- assert_eq!(&bytes[0..4], b"\0asm");
70
- let result = wasmparser::validate(&bytes);
71
- assert!(result.is_ok(), "wasm validation failed: {:?}", result.err());
72
- }
73
-
74
39
  /// Reads a `Str` (wasm-gc `array<i8>`) result value's bytes into a Rust
75
40
  /// `String` — a test-only counterpart to `plum-cli`'s own `readStrResult`,
76
41
  /// since each `test#<name>` export now returns a `Str` report (empty = every
@@ -271,23 +236,6 @@ fn assertValid(src: &str) -> Vec<u8> {
271
236
  bytes
272
237
  }
273
238
 
274
- #[test]
275
- fn boolLiteralsCompile() {
276
- let boolDecl = "";
277
- assertValid(&format!("{}fun main() -> Bool =\n true\n", boolDecl));
278
- assertValid(&format!("{}fun main() -> Bool =\n false\n", boolDecl));
279
- }
280
-
281
- #[test]
282
- fn stringLiteralCompiles() {
283
- assertValid("fun main() -> Str =\n \"hello\"\n");
284
- }
285
-
286
- #[test]
287
- fn emptyStringLiteralCompiles() {
288
- assertValid("fun main() -> Str =\n \"\"\n");
289
- }
290
-
291
239
  #[test]
292
240
  fn stringInterpolationOfAFloatIsAClearError() {
293
241
  // Float-to-decimal-string formatting is a substantial separate undertaking
@@ -299,65 +247,6 @@ fn stringInterpolationOfAFloatIsAClearError() {
299
247
  assert!(err.contains("Float"), "got: {}", err);
300
248
  }
301
249
 
302
- #[test]
303
- fn floatArithmeticAndNegationCompile() {
304
- assertValid("fun main(x: Float) -> Float =\n y = -x\n y + 1.5\n");
305
- }
306
-
307
- #[test]
308
- fn classFieldAndMethodCompile() {
309
- let src = "\
310
- record Cat(name: Str, age: Int) =
311
-
312
- fun getAge() -> Int =
313
- self.age
314
-
315
- fun makeCat() -> Int =
316
- c = Cat(name: \"x\", age: 3)
317
- c.getAge()
318
- ";
319
- assertValid(src);
320
- }
321
-
322
- #[test]
323
- fn nestedClassCallCompiles() {
324
- let src = "\
325
- record Pair(a: Int, b: Int)
326
-
327
- record Wrapper(inner: Pair, tag: Int)
328
-
329
- fun make() -> Int =
330
- w = Wrapper(inner: Pair(a: 1, b: 2), tag: 9)
331
- w.tag
332
- ";
333
- assertValid(src);
334
- }
335
-
336
- #[test]
337
- fn matchWithIntAndWildcardCompiles() {
338
- let src = "fun main(a: Int) -> Int =\n match a\n 1 =>\n return 10\n _ =>\n return 0\n";
339
- assertValid(src);
340
- }
341
-
342
- #[test]
343
- fn matchBindingPatternCompiles() {
344
- let src = "fun main(a: Int) -> Int =\n match a\n x =>\n return x\n";
345
- assertValid(src);
346
- }
347
-
348
- #[test]
349
- fn matchInlineCaseBodyCompiles() {
350
- // Case bodies can be a single inline expression, not just an indented block.
351
- let src = "fun main(a: Int) =\n match a\n 1 => 10\n _ => 0\n";
352
- assertValid(src);
353
- }
354
-
355
- #[test]
356
- fn matchBoolVariantPatternCompiles() {
357
- let src = "fun main(a: Bool) -> Int =\n match a\n true =>\n return 1\n false =>\n return 0\n";
358
- assertValid(src);
359
- }
360
-
361
250
  #[test]
362
251
  fn matchStringPatternIsAClearError() {
363
252
  let src = "fun main(a: Str) -> Int =\n match a\n \"x\" =>\n 1\n _ =>\n 0\n";
@@ -429,18 +318,6 @@ fun main() -> Int =
429
318
  );
430
319
  }
431
320
 
432
- #[test]
433
- fn payloadFreeVariantConstructionCompiles() {
434
- let src = "\
435
- enum Color =
436
- | Red
437
- | Green
438
- | Blue
439
-
440
- fun main() -> Int =\n x = Green\n 0\n";
441
- assertValid(src);
442
- }
443
-
444
321
  #[test]
445
322
  fn tailIfWithoutElseIsAClearError() {
446
323
  let src = "\
@@ -1182,84 +1059,8 @@ fn runMerged<R: wasmtime::WasmResults>(name: &str, src: &str) -> R {
1182
1059
  r
1183
1060
  }
1184
1061
 
1185
- #[test]
1186
- fn heterogeneous_list_dispatch() {
1187
- let src = "\
1188
- import std/Str
1189
- import std/Int
1190
- import std/Float
1191
- import std/List
1192
-
1193
- enum Shape =
1194
- | Circle
1195
- | Square
1196
-
1197
- fun area(self) -> Float =
1198
- match self
1199
- Circle(r) => r * r * 3.0
1200
- Square(s) => s * s
1201
-
1202
- record Circle(r: Float)
1203
- record Square(s: Float)
1204
-
1205
- fun total(xs: List[Shape]) -> Float =
1206
- sum := 0.0
1207
- for x := range xs
1208
- sum = sum + x.area()
1209
- sum
1210
-
1211
- fun main() -> Float =
1212
- xs := List[Shape]()
1213
- xs.add(Circle(r: 1.5))
1214
- xs.add(Square(s: 1.5))
1215
- xs.add(Circle(r: 0.5))
1216
- total(xs)
1217
- ";
1218
- // 6.75 + 2.25 + 0.75, exact in binary floating point.
1219
- assert_eq!(runMerged::<f64>("hetero", src), 9.75);
1220
- }
1221
-
1222
1062
  // ---- erased generics (T4) ----
1223
1063
 
1224
- #[test]
1225
- fn boxed_i64_extremes_through_generic() {
1226
- let src = "\
1227
- import std/Str
1228
- import std/Int
1229
- import std/Float
1230
-
1231
- fun identity(x: T) -> T =
1232
- x
1233
-
1234
- fun main() -> Int =
1235
- lo := 0 - 9223372036854775807 - 1
1236
- big := 9007199254740993
1237
- nz := identity(-0.0)
1238
- ok := identity(lo) == lo && identity(big) == big && identity(big + 0) - big == 0
1239
- neg := 1.0 / nz < 0.0
1240
- if ok && neg && identity(0 - 5) == 0 - 5
1241
- return 1
1242
- return 0
1243
- ";
1244
- assert_eq!(runMerged::<i64>("extremes", src), 1);
1245
- }
1246
-
1247
- #[test]
1248
- fn generic_closure_thunk() {
1249
- let src = "\
1250
- import std/Str
1251
- import std/Int
1252
- import std/Float
1253
- import std/List
1254
-
1255
- fun main() -> Int =
1256
- xs := mapList(List(1, 2, 3), |n| n * 2)
1257
- reduceList(xs, 0, |acc, x| acc + x)
1258
- ";
1259
- let src = format!("{src}\n{LIST_FNS}");
1260
- assert_eq!(runMerged::<i64>("thunk", &src), 12);
1261
- }
1262
-
1263
1064
 
1264
1065
  /// Compiles `src` (merged with the real std) and returns every compiled function key/name.
1265
1066
  fn compiledKeys(name: &str, src: &str) -> (Vec<u8>, Vec<String>) {
@@ -1488,31 +1289,6 @@ fun main() -> Int =
1488
1289
 
1489
1290
  // ---- built-in Bool (i32; no `import std/Bool`) ----
1490
1291
 
1491
- #[test]
1492
- fn builtin_bool_without_import() {
1493
- let src = "\
1494
- import std/Str
1495
- import std/Int
1496
- import std/Float
1497
-
1498
- fun flip(b: Bool) -> Bool =
1499
- !b
1500
-
1501
- fun name(b: Bool) -> Str =
1502
- match b
1503
- true => \"yes\"
1504
- false => \"no\"
1505
-
1506
- fun main() -> Int =
1507
- x := true
1508
- if flip(false) && x == true && !false || false
1509
- if name(x) == \"yes\" && name(false) == \"no\" && \"{x}\" == \"true\"
1510
- return 1
1511
- return 0
1512
- ";
1513
- assert_eq!(runMerged::<i64>("bool_noimport", src), 1);
1514
- }
1515
-
1516
1292
  #[test]
1517
1293
  fn builtin_bool_list_round_trip() {
1518
1294
  let src = "\
@@ -1638,57 +1414,6 @@ fn extern_int_overflow_traps() {
1638
1414
  assert!(externEcho("0 - 2147483649").1.is_err(), "Int < i32::MIN must trap");
1639
1415
  }
1640
1416
 
1641
- #[test]
1642
- fn bool_tostr_intrinsic() {
1643
- let src = "\
1644
- import std/Int
1645
- import std/Float
1646
- import std/Str
1647
- import std/Bool
1648
-
1649
- fun main() -> Int =
1650
- t := true
1651
- if t.toStr() == \"true\" && false.toStr() == \"false\" && \"{t}\" == \"true\"
1652
- return 1
1653
- return 0
1654
- ";
1655
- assert_eq!(runMerged::<i64>("bool_tostr_intrinsic", src), 1);
1656
- }
1657
-
1658
- #[test]
1659
- fn bool_tostr_lowercase() {
1660
- let src = "\
1661
- import std/Int
1662
- import std/Float
1663
- import std/Str
1664
- import std/Bool
1665
-
1666
- fun main() -> Int =
1667
- t := true
1668
- f := t && false
1669
- if t.toStr() == \"true\" && f.toStr() == \"false\" && \"{t}-{f}\" == \"true-false\"
1670
- return 1
1671
- return 0
1672
- ";
1673
- assert_eq!(runMerged::<i64>("bool_tostr_lowercase", src), 1);
1674
- }
1675
-
1676
- #[test]
1677
- fn bool_interpolation_with_import() {
1678
- let src = "\
1679
- import std/Int
1680
- import std/Float
1681
- import std/Str
1682
- import std/Bool
1683
-
1684
- fun main() -> Int =
1685
- if \"{true}\" == \"true\" && \"{1 > 2}\" == \"false\"
1686
- return 1
1687
- return 0
1688
- ";
1689
- assert_eq!(runMerged::<i64>("bool_interp_import", src), 1);
1690
- }
1691
-
1692
1417
  #[test]
1693
1418
  fn bool_tostr_without_import_is_rejected() {
1694
1419
  let src = "\
@@ -1730,25 +1455,6 @@ fun main() -> Str =
1730
1455
  );
1731
1456
  }
1732
1457
 
1733
- #[test]
1734
- fn parse_bool() {
1735
- let src = "\
1736
- import std/Int
1737
- import std/Float
1738
- import std/Str
1739
- import std/Bool
1740
-
1741
- fun main() -> Int =
1742
- a := Bool.parse(\"true\") catch false
1743
- b := Bool.parse(\"false\") catch true
1744
- c := Bool.parse(\"nope\") catch false
1745
- if a && !b && !c
1746
- return 1
1747
- return 0
1748
- ";
1749
- assert_eq!(runMerged::<i64>("parse_bool", src), 1);
1750
- }
1751
-
1752
1458
  // ---- fn-typed fields ("record of functions") + closures calling fn params ----
1753
1459
 
1754
1460
  #[test]
@@ -1850,57 +1556,6 @@ fn nullable_ref_no_alloc() {
1850
1556
  assert!(!ops.iter().any(|o| o.starts_with("StructNew")), "{ops:?}");
1851
1557
  }
1852
1558
 
1853
- #[test]
1854
- fn nullable_runs() {
1855
- let src = format!("{}{}", STD_HEAD, NULLABLE_SRC);
1856
- assert_eq!(runMerged::<i64>("nullrun", &src), 5 + 100 + 7);
1857
- }
1858
-
1859
- #[test]
1860
- fn nullable_primitive_keeps_struct_layout() {
1861
- let src = format!("{}\
1862
- fun half(n: Int) -> Int? =
1863
- if n % 2 != 0
1864
- return null
1865
- n / 2
1866
-
1867
- fun main() -> Int =
1868
- {{half(8) ?: 0}} + {{half(3) ?: 100}}
1869
- ", STD_HEAD);
1870
- assert_eq!(runMerged::<i64>("nullprim", &src), 104);
1871
- }
1872
-
1873
- #[test]
1874
- fn nullable_turns_into_an_error_with_elvis() {
1875
- let src = format!("{}{}", STD_HEAD, "\
1876
- record Person(name: Str, age: Int)
1877
-
1878
- fun findIt(n: Int) -> Person? =
1879
- if n < 0
1880
- return null
1881
- Person(name: \"p\", age: n)
1882
-
1883
- fun viaElvis(n: Int) -> error{Absent, Bad}!Person =
1884
- p := findIt(n) ?: return error.Absent
1885
- if p.age > 50
1886
- return error.Bad
1887
- p
1888
-
1889
- fun code(n: Int) -> Int =
1890
- p := viaElvis(n) catch e
1891
- match e
1892
- Absent =>
1893
- return 1000
1894
- Bad =>
1895
- return 2000
1896
- p.age
1897
-
1898
- fun main() -> Int =
1899
- code(3) + code(-1) + code(99)
1900
- ");
1901
- assert_eq!(runMerged::<i64>("nullwide", &src), 3 + 1000 + 2000);
1902
- }
1903
-
1904
1559
  // ---- L2: structs (flattened, copy semantics) ----
1905
1560
 
1906
1561
  const VEC2: &str = "\
@@ -1934,14 +1589,6 @@ fn funcTypes(bytes: &[u8]) -> Vec<(Vec<wasmparser::ValType>, Vec<wasmparser::Val
1934
1589
  out
1935
1590
  }
1936
1591
 
1937
- #[test]
1938
- fn value_copy_semantics() {
1939
- let src = format!(
1940
- "{VEC2}fun main() -> Int =\n a := Vec2(x: 1.0, y: 2.0)\n b := a\n b.x = 9.0\n if a.x == 1.0 && b.x == 9.0 && b.y == 2.0\n return 1\n return 0\n"
1941
- );
1942
- assert_eq!(runMerged::<i64>("value_copy_semantics", &src), 1);
1943
- }
1944
-
1945
1592
  #[test]
1946
1593
  fn value_multi_value_return() {
1947
1594
  use wasmparser::ValType::F64;
@@ -1956,22 +1603,6 @@ fn value_multi_value_return() {
1956
1603
  assert_eq!(runMerged::<i64>("value_multi_value_return", &src), 1);
1957
1604
  }
1958
1605
 
1959
- #[test]
1960
- fn value_in_list_roundtrip() {
1961
- let src = format!(
1962
- "import std/List\n{VEC2}fun main() -> Int =\n l := List[Vec2]()\n l.push(Vec2(x: 1.0, y: 2.0))\n l.push(Vec2(x: 3.0, y: 4.0))\n b := l.get(1)!\n if l.length() == 2 && b.x == 3.0 && b.y == 4.0 && l.get(0)!.y == 2.0\n return 1\n return 0\n"
1963
- );
1964
- assert_eq!(runMerged::<i64>("value_in_list_roundtrip", &src), 1);
1965
- }
1966
-
1967
- #[test]
1968
- fn value_eq_fieldwise() {
1969
- let src = format!(
1970
- "{VEC2}fun main() -> Int =\n a := Vec2(x: 1.0, y: 2.0)\n if a == Vec2(x: 1.0, y: 2.0) && a != Vec2(x: 1.0, y: 3.0)\n return 1\n return 0\n"
1971
- );
1972
- assert_eq!(runMerged::<i64>("value_eq_fieldwise", &src), 1);
1973
- }
1974
-
1975
1606
  #[test]
1976
1607
  fn value_nested_flatten() {
1977
1608
  use wasmparser::ValType::F64;
@@ -1986,14 +1617,6 @@ fn value_nested_flatten() {
1986
1617
  assert_eq!(runMerged::<i64>("value_nested_flatten", &src), 1);
1987
1618
  }
1988
1619
 
1989
- #[test]
1990
- fn value_operator_add_mul() {
1991
- let src = format!(
1992
- "{VEC2}fun main() -> Int =\n a := Vec2(x: 1.0, y: 2.0)\n b := Vec2(x: 3.0, y: 4.0)\n r := a + b * 2.0\n if r.x == 7.0 && r.y == 10.0\n return 1\n return 0\n"
1993
- );
1994
- assert_eq!(runMerged::<i64>("value_operator_add_mul", &src), 1);
1995
- }
1996
-
1997
1620
  #[test]
1998
1621
  fn extern_value_multi_value() {
1999
1622
  let src = format!(
@@ -2073,22 +1696,6 @@ fn value_nested_pattern() {
2073
1696
  assert_eq!(runMerged::<i64>("value_nested_pattern", &src), 1);
2074
1697
  }
2075
1698
 
2076
- #[test]
2077
- fn local_value_subfield_assign() {
2078
- let src = format!(
2079
- "{VEC2}struct Rect(pos: Vec2, size: Vec2)\n\nfun main() -> Int =\n r := Rect(pos: Vec2(x: 1.0, y: 2.0), size: Vec2(x: 3.0, y: 4.0))\n r.pos.x = 7.0\n r.size.y = 9.0\n if r.pos.x == 7.0 && r.pos.y == 2.0 && r.size.x == 3.0 && r.size.y == 9.0\n return 1\n return 0\n"
2080
- );
2081
- assert_eq!(runMerged::<i64>("local_value_subfield_assign", &src), 1);
2082
- }
2083
-
2084
- #[test]
2085
- fn heap_field_value_subfield_assign() {
2086
- let src = format!(
2087
- "{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"
2088
- );
2089
- assert_eq!(runMerged::<i64>("heap_field_value_subfield_assign", &src), 1);
2090
- }
2091
-
2092
1699
  #[test]
2093
1700
  fn value_nested_pattern_mixed() {
2094
1701
  // Whole-value binding next to a nested destructure, and a heap container
@@ -2184,48 +1791,6 @@ fun main() -> Int =
2184
1791
  assert_eq!(runMerged::<i64>("rangeinttight_run", &src), 45);
2185
1792
  }
2186
1793
 
2187
- #[test]
2188
- fn until_to_downTo_bounds() {
2189
- // Each loop sums its values and counts iterations; empty ranges iterate nothing.
2190
- let src = format!("{}\
2191
- fun main() -> Int =
2192
- a := 0
2193
- for i := range 2.until(6)
2194
- a = a + i
2195
- b := 0
2196
- for i := range 1.to(4)
2197
- b = b + i
2198
- c := 0
2199
- for i := range 4.downTo(1)
2200
- c = c * 10 + i
2201
- n := 0
2202
- for _ := range 5.until(5)
2203
- n = n + 1
2204
- for _ := range 1.downTo(2)
2205
- n = n + 1
2206
- for _ := range 0
2207
- n = n + 1
2208
- for _ := range 3.to(2)
2209
- n = n + 1
2210
- a * 1000000 + b * 100000 + c + n * 100000000
2211
- ", iterHead());
2212
- assert_eq!(runMerged::<i64>("untilbounds", &src), 14 * 1000000 + 10 * 100000 + 4321);
2213
- }
2214
-
2215
- #[test]
2216
- fn stepBy_on_until() {
2217
- let src = format!("{}\
2218
- fun main() -> Int =
2219
- total := 0
2220
- for i := range 0.until(10).stepBy(3)
2221
- total = total + i
2222
- for i := range 1.to(10).stepBy(4)
2223
- total = total + 100 * i
2224
- total
2225
- ", iterHead());
2226
- assert_eq!(runMerged::<i64>("untilstep", &src), 0 + 3 + 6 + 9 + 100 * (1 + 5 + 9));
2227
- }
2228
-
2229
1794
  #[test]
2230
1795
  fn no_closure_in_expansion() {
2231
1796
  let src = format!("{}\
@@ -2260,55 +1825,8 @@ fun main() -> Int =
2260
1825
  }
2261
1826
 
2262
1827
  #[test]
2263
- fn nested_adapters_skip_take() {
2264
- let src = format!("{}\
2265
- fun main() -> Int =
2266
- xs := List(5, 6, 7, 8, 9)
2267
- total := 0
2268
- for x := range xs.take(4).skip(1)
2269
- total = total * 10 + x
2270
- total
2271
- ", iterHead());
2272
- // skip(1, take(4, xs)) = 6, 7, 8
2273
- assert_eq!(runMerged::<i64>("nestedskiptake", &src), 678);
2274
- }
2275
-
2276
- #[test]
2277
- fn zip_two_lists() {
2278
- let src = format!("{}\
2279
- fun main() -> Int =
2280
- xs := List(1, 2, 3)
2281
- ys := List(10, 20, 30, 40)
2282
- dot := 0
2283
- for a, b := range xs.zip(ys)
2284
- dot = dot + a * b
2285
- dot
2286
- ", iterHead());
2287
- assert_eq!(runMerged::<i64>("ziplists", &src), 140);
2288
- }
2289
-
2290
- #[test]
2291
- fn chunks_and_stepBy() {
2292
- let src = format!("{}\
2293
- fun main() -> Int =
2294
- xs := List(1, 2, 3, 4, 5, 6, 7)
2295
- pieces := 0
2296
- last := 0
2297
- for c := range xs.chunks(3)
2298
- pieces = pieces + 1
2299
- last = c.length()
2300
- stepped := 0
2301
- for x := range xs.stepBy(3)
2302
- stepped = stepped + x
2303
- pieces * 1000 + last * 100 + stepped
2304
- ", iterHead());
2305
- // chunks: [1 2 3] [4 5 6] [7] -> 3 pieces, last has 1; stepBy 3: 1 + 4 + 7 = 12
2306
- assert_eq!(runMerged::<i64>("chunksstep", &src), 3000 + 100 + 12);
2307
- }
2308
-
2309
- #[test]
2310
- fn pack_binder_passes_through_map() {
1828
+ fn pack_binder_passes_through_map() {
2311
- // map over a two-value source: map's single name `x` holds both values and is passed to yield
1829
+ // map over a two-value source: map's single name `x` holds both values and is passed to yield
2312
1830
  let src = format!("{}\
2313
1831
  fun* pairs(src: List[Int]) *(Int, Int) =
2314
1832
  for i, x := range src
@@ -2348,21 +1866,6 @@ fun main() -> Int =
2348
1866
  assert_eq!(runMerged::<i64>("starmethod_run", &src), 12);
2349
1867
  }
2350
1868
 
2351
- #[test]
2352
- fn underscore_binder_skips_binding() {
2353
- let src = format!("{}\
2354
- fun main() -> Int =
2355
- xs := List(5, 6, 7)
2356
- total := 0
2357
- for _, x := range xs
2358
- total = total + x
2359
- for _ := range 4
2360
- total = total + 1
2361
- total
2362
- ", iterHead());
2363
- assert_eq!(runMerged::<i64>("underscore", &src), 18 + 4);
2364
- }
2365
-
2366
1869
  /// The checker's error messages for `src` compiled as a real file ("" when it checks).
2367
1870
  fn checkErrorsMerged(name: &str, src: &str) -> String {
2368
1871
  let dir = std::env::temp_dir().join(format!("plum_seq_{}_{}", name, std::process::id()));
@@ -2509,62 +2012,6 @@ fn error_union_multi_value() {
2509
2012
  assert!(found, "no (i64)->(i64,i32) function type");
2510
2013
  }
2511
2014
 
2512
- #[test]
2513
- fn fail_and_question_propagate() {
2514
- let src = "\
2515
- fun a(n: Int) -> !Int =
2516
- if n < 0
2517
- return error.Negative
2518
- n * 2
2519
-
2520
- fun b(n: Int) -> !Int =
2521
- x := try a(n)
2522
- x + 1
2523
-
2524
- fun main() -> Int =
2525
- p := b(5) catch 100
2526
- q := b(-5) catch 200
2527
- p * 1000 + q
2528
- ";
2529
- assert_eq!(runInt(src), 11 * 1000 + 200);
2530
- }
2531
-
2532
- #[test]
2533
- fn catch_default_runtime() {
2534
- let src = "\
2535
- fun f(n: Int) -> !Int =
2536
- if n == 0
2537
- return error.Zero
2538
- n
2539
-
2540
- fun main() -> Int =
2541
- {f(7) catch 1} * 10 + {f(0) catch 3}
2542
- ";
2543
- assert_eq!(runInt(src), 73);
2544
- }
2545
-
2546
- #[test]
2547
- fn catch_return_error_maps() {
2548
- let src = "\
2549
- fun inner(n: Int) -> !Int =
2550
- if n == 0
2551
- return error.Zero
2552
- n
2553
-
2554
- fun outer(n: Int) -> !Int =
2555
- x := inner(n) catch return error.Mapped
2556
- x + 1
2557
-
2558
- fun main() -> Int =
2559
- a := outer(0) catch e
2560
- match e
2561
- Mapped => 40
2562
- b := outer(1) catch 99
2563
- a + b
2564
- ";
2565
- assert_eq!(runInt(src), 42);
2566
- }
2567
-
2568
2015
  #[test]
2569
2016
  fn catch_crash_default() {
2570
2017
  let src = "\
@@ -2612,53 +2059,6 @@ fun main() -> Int =
2612
2059
  assert_eq!(runInt(src), 30 + 2 + 6);
2613
2060
  }
2614
2061
 
2615
- #[test]
2616
- fn catch_block_binds_error() {
2617
- let src = "\
2618
- fun f(n: Int) -> !Int =
2619
- if n == 1
2620
- return error.One
2621
- if n == 2
2622
- return error.Two
2623
- n
2624
-
2625
- fun main() -> Int =
2626
- a := f(1) catch e
2627
- match e
2628
- One => 10
2629
- Two => 20
2630
- b := f(2) catch e
2631
- match e
2632
- One => 10
2633
- Two => 20
2634
- c := f(3) catch e
2635
- return 0
2636
- a * 100 + b * 10 + c
2637
- ";
2638
- assert_eq!(runInt(src), 10 * 100 + 20 * 10 + 3);
2639
- }
2640
-
2641
- #[test]
2642
- fn e_name_runtime() {
2643
- let src = "\
2644
- import std/Str
2645
- import std/Int
2646
- import std/Float
2647
-
2648
- fun f() -> !Int =
2649
- return error.NotFound
2650
-
2651
- fun main() -> Int =
2652
- s := f() catch e
2653
- e.name()
2654
- s.length()
2655
- ";
2656
- // the catch block must yield Int, so name() is used for its length instead
2657
- let src = src.replace(" s := f() catch e\n e.name()\n s.length()", " n := f() catch e\n e.name().length()\n n");
2658
- let (r, _) = runWithCrash("ename", &src);
2659
- assert_eq!(r.unwrap(), 8);
2660
- }
2661
-
2662
2062
  #[test]
2663
2063
  fn bang_crashes_with_message() {
2664
2064
  let src = "\
@@ -2683,47 +2083,6 @@ fun main() -> Int =
2683
2083
  assert_eq!(runWithCrash("bangok", ok).0.unwrap(), 7);
2684
2084
  }
2685
2085
 
2686
- #[test]
2687
- fn unit_error_union() {
2688
- let src = "\
2689
- fun check(n: Int) -> !Unit =
2690
- if n < 0
2691
- return error.Neg
2692
-
2693
- fun run(n: Int) -> !Int =
2694
- try check(n)
2695
- n
2696
-
2697
- fun main() -> Int =
2698
- {run(4) catch 0} + {run(-1) catch 5}
2699
- ";
2700
- assert_eq!(runInt(src), 9);
2701
- }
2702
-
2703
- #[test]
2704
- fn error_union_boxed_in_list() {
2705
- let src = "\
2706
- import std/Str
2707
- import std/Int
2708
- import std/Float
2709
- import std/List
2710
-
2711
- fun f(n: Int) -> !Int =
2712
- if n < 0
2713
- return error.Neg
2714
- n
2715
-
2716
- fun main() -> Int =
2717
- xs := List[!Int]()
2718
- xs.push(f(4))
2719
- xs.push(f(-1))
2720
- a := xs.at(0) catch 0
2721
- b := xs.at(1) catch 100
2722
- a + b
2723
- ";
2724
- assert_eq!(runWithCrash("boxlist", src).0.unwrap(), 104);
2725
- }
2726
-
2727
2086
  #[test]
2728
2087
  fn error_union_in_class_field() {
2729
2088
  let src = "\
@@ -2761,28 +2120,6 @@ fun main() -> Int =
2761
2120
  assert_eq!(runInt(src), 31);
2762
2121
  }
2763
2122
 
2764
- #[test]
2765
- fn nullable_ref_still_null() {
2766
- let src = "\
2767
- import std/Str
2768
- import std/Int
2769
- import std/Float
2770
-
2771
- record Item(v: Int)
2772
-
2773
- fun find(n: Int) -> Item? =
2774
- if n < 0
2775
- return null
2776
- Item(v: n)
2777
-
2778
- fun main() -> Int =
2779
- a := find(3) ?: Item(v: 0)
2780
- b := find(-3) ?: Item(v: 5)
2781
- a.v * 10 + b.v
2782
- ";
2783
- assert_eq!(runWithCrash("nullable", src).0.unwrap(), 35);
2784
- }
2785
-
2786
2123
  #[test]
2787
2124
  fn no_alloc_on_error_path() {
2788
2125
  let src = "\
@@ -2826,55 +2163,6 @@ fun main() -> Int =
2826
2163
  assert!(msgs[0].contains("main.plum:"), "{}", msgs[0]);
2827
2164
  }
2828
2165
 
2829
- #[test]
2830
- fn nullable_safe_navigation_keeps_the_reference_layout() {
2831
- let src = "\
2832
- import std/Str
2833
- import std/Int
2834
- import std/Float
2835
-
2836
- record Person(name: Str, age: Int)
2837
-
2838
- fun nameOf(p: Person?) -> Str? =
2839
- p?.name
2840
-
2841
- fun ageOf(p: Person?) -> Int? =
2842
- p?.age
2843
-
2844
- fun none() -> Person? =
2845
- return null
2846
-
2847
- fun main() -> Int =
2848
- a := nameOf(Person(name: \"abc\", age: 4)) ?: \"\"
2849
- b := nameOf(none()) ?: \"zz\"
2850
- c := ageOf(Person(name: \"x\", age: 40)) ?: 0
2851
- d := ageOf(none()) ?: 7
2852
- a.length() * 1000 + b.length() * 100 + c + d
2853
- ";
2854
- assert_eq!(runWithCrash("nullnav", src).0.unwrap(), 3000 + 200 + 40 + 7);
2855
- }
2856
-
2857
- #[test]
2858
- fn expression_bodied_unit_error_union() {
2859
- let src = "\
2860
- fun check(n: Int) -> error{Neg}!Unit =
2861
- if n < 0
2862
- return error.Neg
2863
-
2864
- fun passthrough(n: Int) -> error{Neg}!Unit =
2865
- try check(n)
2866
-
2867
- fun main() -> Int =
2868
- a := 0
2869
- passthrough(1) catch e
2870
- a = a + 1
2871
- passthrough(-1) catch e
2872
- a = a + 10
2873
- a
2874
- ";
2875
- assert_eq!(runInt(src), 10);
2876
- }
2877
-
2878
2166
  #[test]
2879
2167
  fn open_error_set_name_through_a_parameter() {
2880
2168
  let src = "\
@@ -2930,76 +2218,6 @@ fun main() -> Int =
2930
2218
  assert!(msgs[0].contains("main.plum:"), "{}", msgs[0]);
2931
2219
  }
2932
2220
 
2933
- #[test]
2934
- fn null_comparisons_and_bindings_run() {
2935
- let src = "\
2936
- import std/Str
2937
- import std/Int
2938
- import std/Float
2939
-
2940
- record Item(v: Int)
2941
-
2942
- fun mi(n: Int) -> Int? =
2943
- if n > 0
2944
- return n
2945
- null
2946
-
2947
- fun mr(n: Int) -> Item? =
2948
- if n > 0
2949
- return Item(v: n)
2950
- null
2951
-
2952
- fun nested(n: Int) -> error{Bad}!Int? =
2953
- if n < 0
2954
- return error.Bad
2955
- if n == 0
2956
- return null
2957
- n
2958
-
2959
- fun main() -> Int =
2960
- score := 0
2961
- if mi(1) != null
2962
- score = score + 1
2963
- if null == mi(0)
2964
- score = score + 10
2965
- if mr(0) == null
2966
- score = score + 100
2967
- if x := mi(5)
2968
- score = score + x * 1000
2969
- if it := mr(2)
2970
- score = score + it.v * 10000
2971
- k := 3
2972
- while y := mi(k)
2973
- score = score + 100000
2974
- k = k - 1
2975
- n := nested(0) catch 0
2976
- score + {if n == null then 1000000 else 0}
2977
- ";
2978
- assert_eq!(runWithCrash("nullbind", src).0.unwrap(), 1 + 10 + 100 + 5000 + 20000 + 300000 + 1000000);
2979
- }
2980
-
2981
- #[test]
2982
- fn nullable_value_enum_uses_the_code_word() {
2983
- let src = "\
2984
- import std/Str
2985
- import std/Int
2986
- import std/Float
2987
-
2988
- struct Pair(x: Int, y: Int)
2989
-
2990
- fun make(n: Int) -> Pair? =
2991
- if n < 0
2992
- return null
2993
- Pair(x: n, y: n + 1)
2994
-
2995
- fun main() -> Int =
2996
- p := make(4)!
2997
- q := make(-1) ?: Pair(x: 0, y: 9)
2998
- p.x * 100 + p.y * 10 + q.y
2999
- ";
3000
- assert_eq!(runWithCrash("nullvalue", src).0.unwrap(), 400 + 50 + 9);
3001
- }
3002
-
3003
2221
  #[test]
3004
2222
  fn list_of_error_unions_across_sets_valid_wasm() {
3005
2223
  let src = "\
@@ -3026,31 +2244,6 @@ fun main() -> Int =
3026
2244
  assert_eq!(runWithCrash("listsets", src).0.unwrap(), 104);
3027
2245
  }
3028
2246
 
3029
- #[test]
3030
- fn map_value_error_union_across_sets() {
3031
- let src = "\
3032
- import std/Str
3033
- import std/Int
3034
- import std/Float
3035
- import std/Map
3036
-
3037
- fun chk(n: Int) -> !Int =
3038
- if n < 0
3039
- return error.Neg
3040
- n
3041
-
3042
- fun look(m: Map[Int, error{Neg}!Int]) -> Int =
3043
- v := m.get(1) catch chk(-1)
3044
- v catch 0
3045
-
3046
- fun main() -> Int =
3047
- m := Map[Int, !Int]()
3048
- m.set(1, chk(7))
3049
- look(m)
3050
- ";
3051
- assert_eq!(runWithCrash("mapsets", src).0.unwrap(), 7);
3052
- }
3053
-
3054
2247
  #[test]
3055
2248
  fn error_value_runtime_in_slots() {
3056
2249
  let src = "\
@@ -3101,27 +2294,6 @@ fun main() -> Int =
3101
2294
 
3102
2295
  // ---- associated consts, statics and `primitive` ----
3103
2296
 
3104
- #[test]
3105
- fn primitive_consts_and_statics() {
3106
- let src = format!("{}\
3107
- primitive Geo =
3108
- SIDES = 4
3109
- AREA = SIDES * SIDES
3110
- NAMES = List(\"a\", \"b\", \"c\")
3111
-
3112
- fun twice(n: Int) -> Int =
3113
- n * 2
3114
-
3115
- fun sidesTwice() -> Int =
3116
- twice(SIDES)
3117
-
3118
- fun main() -> Int =
3119
- Geo.SIDES + Geo.AREA + Geo.twice(5) + Geo.sidesTwice() + Geo.NAMES.length()
3120
- ", STD_HEAD);
3121
- // 4 + 16 + 10 + 8 + 3
3122
- assert_eq!(runMerged::<i64>("prim_consts", &src), 41);
3123
- }
3124
-
3125
2297
  #[test]
3126
2298
  fn enum_assoc_const() {
3127
2299
  let src = format!("{}\
@@ -3140,58 +2312,8 @@ fun main() -> Int =
3140
2312
  assert_eq!(runMerged::<i64>("enum_const", &src), 23);
3141
2313
  }
3142
2314
 
3143
- #[test]
3144
- fn value_enum_assoc_const() {
3145
- let src = format!("{}\
3146
- struct Vec2(x: Float, y: Float) =
3147
-
3148
- ZERO = Vec2(x: 0.0, y: 0.0)
3149
- UNIT = Vec2(x: 1.0, y: 1.0)
3150
-
3151
- fun main() -> Int =
3152
- z := Vec2.ZERO
3153
- u := Vec2.UNIT
3154
- if z.x == 0.0 && u.y == 1.0
3155
- return 1
3156
- return 0
3157
- ", STD_HEAD);
3158
- assert_eq!(runMerged::<i64>("value_enum_const", &src), 1);
3159
- }
3160
-
3161
- #[test]
3162
- fn extend_float_consts() {
3163
- let src = format!("{}\
3164
- fun main() -> Int =
3165
- if Float.PI > 3.14 && Float.PI < 3.15 && Float.TAU == Float.PI * 2.0 && Int.MAX > 0
3166
- return 1
3167
- return 0
3168
- ", STD_HEAD);
3169
- assert_eq!(runMerged::<i64>("float_consts", &src), 1);
3170
- }
3171
-
3172
- #[test]
3173
- fn int_parse_static() {
3174
- let src = format!("{}\
3175
- fun main() -> Int =
3176
- a := Int.parse(\"42\") catch -1
3177
- b := Int.parse(\"x\") catch -1
3178
- c := Float.parse(\"2.5\") catch -1.0
3179
- a + b + Int(c * 2.0)
3180
- ", STD_HEAD);
3181
- assert_eq!(runMerged::<i64>("int_parse", &src), 46);
3182
- }
3183
-
3184
2315
  #[test]
3185
2316
  fn number_enum_removed() {
3186
- let src = format!("{}\
3187
- fun main() -> Int =
3188
- x := -7
3189
- f := 9.0
3190
- if x.abs() == 7 && f.sqrt() == 3.0 && 3.min(4) == 3 && 2.5.max(1.5) == 2.5
3191
- return 1
3192
- return 0
3193
- ", STD_HEAD);
3194
- assert_eq!(runMerged::<i64>("number_removed", &src), 1);
3195
2317
  // `Number` is no longer a type.
3196
2318
  let msg = checkErrorsMerged("number_type", &format!("{}\
3197
2319
  fun f(n: Number) -> Int =
@@ -3203,40 +2325,6 @@ fun main() -> Int =
3203
2325
  assert!(!msg.is_empty(), "Number must not resolve as a type");
3204
2326
  }
3205
2327
 
3206
- #[test]
3207
- fn assoc_const_names_per_type() {
3208
- let src = format!("{}\
3209
- primitive Alpha =
3210
- X = 1
3211
-
3212
- primitive Beta =
3213
- X = 20
3214
-
3215
- record Gamma(n: Int) =
3216
- X = 300
3217
-
3218
- fun sum(self) -> Int =
3219
- X + Alpha.X + Beta.X
3220
-
3221
- fun main() -> Int =
3222
- g := Gamma(n: 0)
3223
- Alpha.X + Beta.X + Gamma.X + g.sum()
3224
- ", STD_HEAD);
3225
- assert_eq!(runMerged::<i64>("const_names", &src), 1 + 20 + 300 + 321);
3226
- }
3227
-
3228
- #[test]
3229
- fn primitive_builtin_declares_methods() {
3230
- // `x.abs()` resolves through `primitive Int` in plum-std/Int.plum.
3231
- let text = std::fs::read_to_string(repoRoot().join("plum-std/Int.plum")).unwrap();
3232
- assert!(text.contains("primitive Int ="));
3233
- let src = format!("{}\
3234
- fun main() -> Int =
3235
- {{-5}}.abs() + 3.max(9)
3236
- ", STD_HEAD);
3237
- assert_eq!(runMerged::<i64>("prim_builtin", &src), 14);
3238
- }
3239
-
3240
2328
  #[test]
3241
2329
  fn primitive_not_a_type() {
3242
2330
  let head = STD_HEAD;
@@ -3328,12 +2416,6 @@ fn strIsOneStructWithOneByteArrayField() {
3328
2416
  assert_eq!(runWithCrash("strshape", src).0.unwrap(), 5);
3329
2417
  }
3330
2418
 
3331
- #[test]
3332
- fn bufferToStrLengthExactAfterDoubling() {
3333
- let src = "import std/Str\nimport std/Buffer\nimport std/Int\nimport std/Float\n\nfun main() -> Int =\n b := Buffer()\n i := 0\n while i < 37\n b.write(\"abc\")\n i = i + 1\n b.toStr().length() * 1000 + b.length()\n";
3334
- assert_eq!(runWithCrash("bufexact", src).0.unwrap(), 111111);
3335
- }
3336
-
3337
2419
  // ---- struct-of-arrays layout for Array[struct] ----
3338
2420
 
3339
2421
  #[test]
plum-wasm-codegen/tests/examples_test.rs CHANGED
@@ -43,63 +43,6 @@ fn assertCompiles(name: &str) -> Vec<u8> {
43
43
  bytes
44
44
  }
45
45
 
46
- /// Examples that stick to currently-supported codegen features (primitives, control
47
- /// flow, classes/methods) must actually compile to valid wasm — not just parse and
48
- /// type-check.
49
- #[test]
50
- fn basicsCompiles() {
51
- assertCompiles("basics.plum");
52
- }
53
-
54
- #[test]
55
- fn controlFlowCompiles() {
56
- assertCompiles("control_flow.plum");
57
- }
58
-
59
- #[test]
60
- fn functionsCompiles() {
61
- assertCompiles("functions.plum");
62
- }
63
-
64
- #[test]
65
- fn structsRecordsCompiles() {
66
- assertCompiles("structs_records.plum");
67
- }
68
-
69
- // unions.plum/match.plum/closures.plum's actual runtime-correctness checks
70
- // now live as native `test`/`expect` blocks inside those example files
71
- // themselves (run via `plum test plum-examples/<name>.plum`, see
72
- // scripts/test-plum.sh) — `assertCompiles` below still exercises that each
73
- // still compiles to valid wasm via this crate's direct (non-CLI) path.
74
- #[test]
75
- fn unionsCompiles() {
76
- assertCompiles("unions.plum");
77
- }
78
-
79
- #[test]
80
- fn gameLoopCompiles() {
81
- assertCompiles("game_loop.plum");
82
- }
83
-
84
- #[test]
85
- fn matchExampleCompiles() {
86
- assertCompiles("match.plum");
87
- }
88
-
89
- #[test]
90
- fn closuresExampleCompiles() {
91
- assertCompiles("closures.plum");
92
- }
93
-
94
- /// strings.plum exercises string interpolation (plain literals, `Str`/`Int`
95
- /// interpolation, and escape sequences) — it has no `main` to execute, so (matching
96
- /// `basicsCompiles`/`controlFlowCompiles`/etc. above for other main-less examples)
97
- /// this only checks that it compiles to a valid module.
98
- #[test]
99
- fn stringsExampleCompiles() {
100
- assertCompiles("strings.plum");
101
- }
102
-
103
46
  /// io.plum exercises `extern fun` (see `Fn.is_extern`/`FnBody::Extern` in
104
47
  /// plum-core/src/ast.rs and their handling in plum-wasm-codegen/src/lib.rs) — it
105
48
  /// declares its own `extern fun printLn(s: Str)` (redeclared locally rather than a
@@ -211,9 +154,10 @@ fn ioExampleCompilesAndRunsCorrectly() {
211
154
  /// not the boxed `array (mut anyref)`.
212
155
  #[test]
213
156
  fn array_int_native() {
214
- use wasmparser::{CompositeInnerType, Parser, Payload, StorageType, ValType};
157
+ use wasmparser::{CompositeInnerType, HeapType, Parser, Payload, StorageType, ValType};
215
158
  let bytes = assertCompiles("typed_arrays.plum");
216
159
  let mut elems: Vec<ValType> = Vec::new();
160
+ let mut erased = false;
217
161
  for payload in Parser::new(0).parse_all(&bytes) {
218
162
  if let Payload::TypeSection(reader) = payload.unwrap() {
219
163
  for rec in reader {
@@ -221,6 +165,9 @@ fn array_int_native() {
221
165
  if let CompositeInnerType::Array(a) = &sub.composite_type.inner {
222
166
  if a.0.mutable {
223
167
  if let StorageType::Val(v) = a.0.element_type {
168
+ if let ValType::Ref(r) = v {
169
+ erased |= matches!(r.heap_type(), HeapType::Abstract { .. });
170
+ }
224
171
  elems.push(v);
225
172
  }
226
173
  }
@@ -229,44 +176,12 @@ fn array_int_native() {
229
176
  }
230
177
  }
231
178
  }
179
+ assert!(!erased, "mutable array of erased abstract ref: {:?}", elems);
232
180
  assert!(elems.contains(&ValType::I64), "no (mut i64) array in {:?}", elems);
233
181
  assert!(elems.contains(&ValType::F64), "no (mut f64) array in {:?}", elems);
234
182
  assert!(elems.contains(&ValType::I32), "no (mut i32) array in {:?}", elems);
235
183
  }
236
184
 
237
- /// Game-perf 1: no mutable array is an erased abstract-ref array, and
238
- /// `Array[Int]`/`Array[Float]` element storage is exactly i64/f64.
239
- #[test]
240
- fn array_elements_are_never_erased_anyref() {
241
- use wasmparser::{CompositeInnerType, HeapType, Parser, Payload, StorageType, ValType};
242
- let bytes = assertCompiles("typed_arrays.plum");
243
- let mut mut_elems: Vec<StorageType> = Vec::new();
244
- for payload in Parser::new(0).parse_all(&bytes) {
245
- if let Payload::TypeSection(reader) = payload.unwrap() {
246
- for rec in reader {
247
- for sub in rec.unwrap().into_types() {
248
- if let CompositeInnerType::Array(a) = &sub.composite_type.inner {
249
- if a.0.mutable {
250
- mut_elems.push(a.0.element_type);
251
- }
252
- }
253
- }
254
- }
255
- }
256
- }
257
- for e in &mut_elems {
258
- if let StorageType::Val(ValType::Ref(r)) = e {
259
- assert!(
260
- !matches!(r.heap_type(), HeapType::Abstract { .. }),
261
- "mutable array of erased abstract ref: {:?}",
262
- mut_elems
263
- );
264
- }
265
- }
266
- assert!(mut_elems.contains(&StorageType::Val(ValType::I64)));
267
- assert!(mut_elems.contains(&StorageType::Val(ValType::F64)));
268
- }
269
-
270
185
  /// L1 `const_table_1000_entries`: 1000 associated consts (spread over two
271
186
  /// bodies, before and between other decls) plus a 1000-element list literal
272
187
  /// parse, type-check and are usable inside methods.
plum-wasm-codegen/tests/unions_e2e_test.rs CHANGED
@@ -65,66 +65,6 @@ fn withDefs(body: &str) -> String {
65
65
  format!("{DEFS}{body}")
66
66
  }
67
67
 
68
- #[test]
69
- fn list_of_shapes() {
70
- let src = withDefs(
71
- "\
72
- fun main() -> Int =
73
- xs := List[Shape]()
74
- xs.push(Circle(r: 1.0))
75
- xs.push(Rectangle(w: 2.0, h: 3.0))
76
- sum := 0.0
77
- for x := range xs
78
- sum = sum + x.area()
79
- Int(sum)
80
- ",
81
- );
82
- assert_eq!(runInt("list", &src), 9);
83
- }
84
-
85
- #[test]
86
- fn struct_member_copy() {
87
- let src = withDefs(
88
- "\
89
- fun make() -> Shape = Circle(r: 1.0)
90
-
91
- fun main() -> Int =
92
- s := make()
93
- r := 0.0
94
- if Circle := s
95
- c := s
96
- c.r = 5.0
97
- r = c.r + s.r
98
- Int(r)
99
- ",
100
- );
101
- assert_eq!(runInt("copy", &src), 6);
102
- }
103
-
104
- #[test]
105
- fn narrow_and_dispatch() {
106
- let src = withDefs(
107
- "\
108
- fun pick(big: Bool) -> Shape =
109
- if big
110
- Rectangle(w: 10.0, h: 10.0)
111
- else
112
- Circle(r: 1.0)
113
-
114
- fun wide(s: Shape) -> Float =
115
- match s
116
- Circle => 0.0
117
- Rectangle => s.w
118
-
119
- fun main() -> Int =
120
- a := pick(true)
121
- b := pick(false)
122
- Int(a.area() + b.area() + wide(a))
123
- ",
124
- );
125
- assert_eq!(runInt("narrow", &src), 113);
126
- }
127
-
128
68
  #[test]
129
69
  fn member_into_optional_and_error_unions() {
130
70
  let src = withDefs(
@@ -187,22 +127,3 @@ fun go(p: Pair) -> Int =
187
127
  "{errs:?}"
188
128
  );
189
129
  }
190
-
191
- #[test]
192
- fn bool_is_a_member() {
193
- let src = withDefs(
194
- "\
195
- enum Jb = BNull | Bool | Float
196
-
197
- fun code(j: Jb) -> Int =
198
- match j
199
- BNull => 0
200
- Bool => if j then 1 else 2
201
- Float => 3
202
-
203
- fun main() -> Int =
204
- code(true) * 100 + code(false) * 10 + code(1.5)
205
- ",
206
- );
207
- assert_eq!(runInt("boolmember", &src), 123);
208
- }