sx: step 8 — non-exhaustive match warnings
Emit a warning when a `match` expression on an ADT value misses one or more constructors and lacks an `else`/`_` clause. Behaviour is non-fatal — the match still runs, the warning goes to stderr. - spec/evaluator.sx: helpers `match-clause-is-else?`, `match-clause-ctor-name`, `match-warn-non-exhaustive`, `match-check-exhaustiveness`. The latter reads the `*adt-registry*` (already populated by `define-type`), collects constructor patterns from clauses, and dedupes via an `*adt-warned*` env-bound dict so each (type, missing-set) warns once. Wired into `step-sf-match` via a `do` block before clause dispatch. - hosts/javascript/platform.py: `host-warn` primitive (`console.warn`) + matching `hostWarn` js-id helper so the JS-transpiled spec code can call it directly. Spec code reaches JS via `sx_build target=js`. - hosts/ocaml/lib/sx_runtime.ml + sx_primitives.ml: `host-warn` runtime helper (`prerr_endline`) and registered primitive. - hosts/ocaml/lib/sx_ref.ml: HAND-PATCHED. `step_sf_match` now calls a hand-written `match_check_exhaustiveness` that handles both `AdtValue` and back-compat dict-shape ADT values. The OCaml side is *not* retranspiled because regenerating sx_ref.ml drops several preamble fixes (seq_to_list, string->symbol mangling, empty-dict literal bug). Future retranspile must reapply this patch. - spec/tests/test-adt.sx: 5 new tests covering exhaustive, non-exhaustive (warning is non-fatal), `else` suppression, partial coverage with one missing constructor, and `_` wildcard suppression. Tests assert return values only — warnings go to stderr and are not captured. Warning format: `[sx] match: non-exhaustive — TypeName: missing Ctor1, Ctor2` Both hosts emit identical messages. Tests: OCaml 4540 → 4545 (+5), JS 2586 → 2591 (+5). Zero regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -454,4 +454,33 @@
|
||||
(match
|
||||
(WX (LeftX 9))
|
||||
((WX (LeftX v)) (str "left-" v))
|
||||
((WX (RightX v)) (str "right-" v)))))))
|
||||
((WX (RightX v)) (str "right-" v))))))
|
||||
(deftest
|
||||
"exhaustive match runs without error"
|
||||
(do
|
||||
(define-type ExA1 (CaA1 v) (CbA1))
|
||||
(assert= 1 (match (CaA1 1) ((CaA1 x) x) ((CbA1) 0)))
|
||||
(assert= 0 (match (CbA1) ((CaA1 x) x) ((CbA1) 0)))))
|
||||
(deftest
|
||||
"non-exhaustive match still returns value (warning is non-fatal)"
|
||||
(do
|
||||
(define-type ExA2 (CaA2 v) (CbA2))
|
||||
(assert= 9 (match (CaA2 9) ((CaA2 x) x)))))
|
||||
(deftest
|
||||
"match with else clause suppresses non-exhaustive warning"
|
||||
(do
|
||||
(define-type ExA3 (CaA3 v) (CbA3) (CcA3))
|
||||
(assert= "a" (match (CaA3 1) ((CaA3 x) "a") (else "other")))
|
||||
(assert= "other" (match (CbA3) ((CaA3 x) "a") (else "other")))))
|
||||
(deftest
|
||||
"match with all-but-one constructor still runs"
|
||||
(do
|
||||
(define-type ExA4 (CaA4 v) (CbA4) (CcA4))
|
||||
(assert= 5 (match (CaA4 5) ((CaA4 x) x) ((CbA4) 0)))
|
||||
(assert= 0 (match (CbA4) ((CaA4 x) x) ((CbA4) 0)))))
|
||||
(deftest
|
||||
"match wildcard pattern suppresses non-exhaustive warning"
|
||||
(do
|
||||
(define-type ExA5 (CaA5 v) (CbA5))
|
||||
(assert= 7 (match (CaA5 7) ((CaA5 x) x) (_ 0)))
|
||||
(assert= 0 (match (CbA5) ((CaA5 x) x) (_ 0))))))
|
||||
|
||||
Reference in New Issue
Block a user