plum

#treesitter#compiler#wasm

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

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


f9467e7 — Peter John 2026-10-25T16:02:16+05:30
fix(checker): refutable payload patterns don't cover a tag in exhaustiveness
plum-checker/src/lib.rs CHANGED
@@ -1262,8 +1262,18 @@ fn checkMatch(
1262
1262
  catch_all = true; // a binding
1263
1263
  }
1264
1264
  }
1265
- Some(ast::CasePattern::Class { name, .. }) => {
1265
+ Some(ast::CasePattern::Class { name, fields }) => {
1266
+ // `B(0)` is refutable and does not cover `B`.
1267
+ let irrefutable = |p: &ast::CasePattern| match p {
1268
+ ast::CasePattern::Wildcard => true,
1269
+ ast::CasePattern::Name(n) => {
1270
+ !n.chars().next().is_some_and(|c| c.is_uppercase())
1271
+ }
1272
+ _ => false,
1273
+ };
1274
+ if fields.iter().all(irrefutable) {
1266
- covered.insert(name.as_str());
1275
+ covered.insert(name.as_str());
1276
+ }
1267
1277
  }
1268
1278
  _ => {}
1269
1279
  }
plum-checker/tests/checker_tests.rs CHANGED
@@ -1626,3 +1626,57 @@ fun f() -> Try[Int, [Mapped]] =
1626
1626
  );
1627
1627
  assert!(r.is_ok(), "got {:?}", r.err());
1628
1628
  }
1629
+
1630
+ #[test]
1631
+ fn refutable_payload_pattern_does_not_cover_tag() {
1632
+ let r = errText(checkTry(
1633
+ "\
1634
+ fun f(t: [A, B(Int)]) -> Int =
1635
+ match t
1636
+ A => 1
1637
+ B(0) => 2
1638
+ ",
1639
+ ));
1640
+ assert!(r.contains("non-exhaustive") && r.contains("B"), "got: {}", r);
1641
+ }
1642
+
1643
+ #[test]
1644
+ fn question_mark_wider_inner_than_outer_with_payload() {
1645
+ let r = errText(checkTry(
1646
+ "\
1647
+ fun g() -> Try[Int, [A, B(Str)]] =
1648
+ Err(A)
1649
+
1650
+ fun f() -> Try[Int, [A, B(Int)]] =
1651
+ x := g()?
1652
+ Ok(x)
1653
+ ",
1654
+ ));
1655
+ assert!(r.contains("not part of"), "got: {}", r);
1656
+ }
1657
+
1658
+ #[test]
1659
+ fn tag_literal_does_not_shadow_declared_enum_variant() {
1660
+ let r = checkTry(
1661
+ "\
1662
+ enum Color =
1663
+ | Red
1664
+ | Blue
1665
+
1666
+ fun f() -> Color =
1667
+ Red
1668
+ ",
1669
+ );
1670
+ assert!(r.is_ok(), "got {:?}", r.err());
1671
+ let bad = checkTry(
1672
+ "\
1673
+ enum Color =
1674
+ | Red
1675
+ | Blue
1676
+
1677
+ fun f() -> Try[Int, [Red]] =
1678
+ Err(Red)
1679
+ ",
1680
+ );
1681
+ assert!(bad.is_err());
1682
+ }