Summary
In a multi-clause defn whose pattern dispatch decomposes a compound parameter (e.g. cons r rest) and whose body recurses on a sub-binding (e.g. [recurse rest]), compile-match-tree lowers variable bindings to positional let v := __cons_N references that point at outer-parameter storage already overwritten by a later dispatch column. The recursive call receives a corrupted value and produces wrong results silently.
Reproducer
spec sum-rows [List Nat] -> Nat
defn sum-rows
| nil -> 0N
| cons r nil -> r
| cons r rest -> [+ r [sum-rows rest]]
[sum-rows '[1N 2N 3N]]
Expected: 6N
Actual: incorrect value (rest is corrupted; recursion produces wrong result)
The bug reproduces on current main using the bracketed form [[cons r rest]] which compiles to the same internal AST. PR #16 (auto-pack of bare-token compound patterns) makes the bug easier to hit at the syntax level but does not introduce it — the bug pre-exists.
Diagnosis (from PR #16's commit message and disclosure)
Variable patterns named in a sub-position get bound via let v := __cons_1, referring to a param that's been destructured by a later dispatch column.
Bug location: compile-match-tree. The variable lowering emits let bindings against positional storage names (__cons_0, __cons_1) that aren't preserved across dispatch columns — when a later column inspects the same parameter slot, the storage is overwritten and let v := __cons_1 reads the new contents.
What works (covered by PR #16's tests)
[sum-rows '[]] — first clause matches, no compound dispatch
[sum-rows '[42N]] — second clause matches (cons r nil), r extracted directly, no recursive descent into the buggy slice
What's broken
- Any input ≥ 2 elements that traverses a recursive compound clause
- The general idiom: structural-decomposition pattern + recursive call on a sub-bound variable
- Likely affects List traversal (
cons), Tree traversal, Pair destructuring — a load-bearing functional-programming pattern
Scope
Provenance
Severity
Medium-high. Silent wrong results (not an error) on a load-bearing functional-programming idiom. Especially dangerous because users writing standard recursive structural code over Lists or Trees can hit it without knowing.
Summary
In a multi-clause
defnwhose pattern dispatch decomposes a compound parameter (e.g.cons r rest) and whose body recurses on a sub-binding (e.g.[recurse rest]),compile-match-treelowers variable bindings to positionallet v := __cons_Nreferences that point at outer-parameter storage already overwritten by a later dispatch column. The recursive call receives a corrupted value and produces wrong results silently.Reproducer
Expected:
6NActual: incorrect value (
restis corrupted; recursion produces wrong result)The bug reproduces on current
mainusing the bracketed form[[cons r rest]]which compiles to the same internal AST. PR #16 (auto-pack of bare-token compound patterns) makes the bug easier to hit at the syntax level but does not introduce it — the bug pre-exists.Diagnosis (from PR #16's commit message and disclosure)
Bug location:
compile-match-tree. The variable lowering emitsletbindings against positional storage names (__cons_0,__cons_1) that aren't preserved across dispatch columns — when a later column inspects the same parameter slot, the storage is overwritten andlet v := __cons_1reads the new contents.What works (covered by PR #16's tests)
[sum-rows '[]]— first clause matches, no compound dispatch[sum-rows '[42N]]— second clause matches (cons r nil),rextracted directly, no recursive descent into the buggy sliceWhat's broken
cons), Tree traversal, Pair destructuring — a load-bearing functional-programming patternScope
compile-match-tree's variable loweringProvenance
docs/tracking/2026-04-23_eigentrust_pitfalls.md(currently in PR Add EigenTrust reputation algorithm: implementation, tests, benchmark #2)Severity
Medium-high. Silent wrong results (not an error) on a load-bearing functional-programming idiom. Especially dangerous because users writing standard recursive structural code over Lists or Trees can hit it without knowing.