Iter 14d: remove Term::If as a redundancy

Term::If was semantically a subset of Term::Match on Bool. Per
CLAUDE.md the language must contain no redundancies; two AST nodes
for the same operation produces an authoring decision with no
semantic content and a duplicate codegen path. Removed.

Migration shape (applied to sum, sort, max3 fixtures):
  (if c a b) -> (match c (case (lit-bool true) a) (case _ b))

No schema version bump (per user direction): no third-party consumes
ailang/v0, so version ceremony is pure overhead. Edited AST and
fixtures in place; pinned hashes in hash.rs updated.

Implementer deviation, called out and justified: a tightly-scoped
lower_bool_match helper (~95 LOC) was needed in codegen because
the existing match path rejects i1 scrutinees and Pattern::Lit.
Helper accepts only the canonical two-arm migration shape, errors
on anything else, emits the same br/phi IR Term::If used to. No
generalisation of the ADT-match codegen.

Diff: 13 files, +286/-221 (net +65 LOC). AST got smaller
(one variant gone), form-(A) got smaller (one production gone),
typecheck got smaller (one branch gone). Codegen got slightly
larger by the bool-match helper.

Hash deltas: sum.sum, sort.insert, max3.max, max3.max3 changed.
All other defs (e.g. sum.main, sort.IntList, sort.sort,
sort.print_list, max3.main) kept bit-identical hashes — confirms
canonical-JSON byte format intact.

Verification: 76/76 tests green; sum->55, max3->17, sort->[1,1,2,
3,3,4,5,5,5,6,9] (identical to pre-migration). cargo doc 0 warnings.

Tail-call survey by implementer (informs 14e): print_list
recursions are already in tail position (rhs of seq inside match
arm); map/sort/insert recursions are NOT (constructor-blocked
inside Cons applications). 14e annotation will benefit terminal
recursions; ctor-blocked ones need accumulator-form rewrites in
source, not a compiler-side transform.

Decision 7 added to DESIGN.md. JOURNAL entry has the language-
completion sequence (14d done, 14e tail-calls, 14f GC, 15a stdlib).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 16:56:02 +02:00
parent 706f90bacd
commit 8d97a924de
14 changed files with 419 additions and 223 deletions
+2 -18
View File
@@ -35,7 +35,7 @@
//!
//! term ::= var-ref | int-lit | str-lit | bool-lit | unit-lit
//! | app-term | match-term | ctor-term | do-term | seq-term
//! | lam-term | if-term | let-term
//! | lam-term | let-term
//! var-ref ::= ident ; reserved: true/false → bool-lit
//! int-lit ::= integer ; numeric atom
//! str-lit ::= string ; string atom
@@ -51,7 +51,6 @@
//! "(" "ret" type ")"
//! effects-clause? body-attr ")"
//! typed-param ::= "(" "typed" ident type ")"
//! if-term ::= "(" "if" term term term ")"
//! let-term ::= "(" "let" ident term term ")"
//!
//! pattern ::= pat-var | pat-ctor | pat-lit | pat-wild
@@ -683,7 +682,6 @@ impl<'a> Parser<'a> {
"do" => self.parse_do(),
"seq" => self.parse_seq(),
"lam" => self.parse_lam(),
"if" => self.parse_if(),
"let" => self.parse_let(),
other => {
let pos = self.peek().map(|t| t.span.start).unwrap_or(0);
@@ -691,7 +689,7 @@ impl<'a> Parser<'a> {
production: "term",
message: format!(
"unknown term head `{other}`; expected one of \
`app`, `lam`, `let`, `if`, `match`, `do`, `seq`, \
`app`, `lam`, `let`, `match`, `do`, `seq`, \
`term-ctor`, `lit-unit`"
),
pos,
@@ -872,20 +870,6 @@ impl<'a> Parser<'a> {
})
}
fn parse_if(&mut self) -> Result<Term, ParseError> {
self.expect_lparen("if-term")?;
self.expect_keyword("if")?;
let cond = self.parse_term()?;
let then = self.parse_term()?;
let else_ = self.parse_term()?;
self.expect_rparen("if-term")?;
Ok(Term::If {
cond: Box::new(cond),
then: Box::new(then),
else_: Box::new(else_),
})
}
fn parse_let(&mut self) -> Result<Term, ParseError> {
self.expect_lparen("let-term")?;
self.expect_keyword("let")?;