Skip to content

Fix exponential LIR blowup for long list-pattern matches#9707

Merged
rtfeldman merged 1 commit into
mainfrom
fix-9700-list-match-exponential-lir
Jun 19, 2026
Merged

Fix exponential LIR blowup for long list-pattern matches#9707
rtfeldman merged 1 commit into
mainfrom
fix-9700-list-match-exponential-lir

Conversation

@rtfeldman

Copy link
Copy Markdown
Contributor

Fixes #9700.

A match on a List was desugared into a tree of nested if/two-armed-match expressions whose miss arm pointed at the shared "rest of the match" expression. The Monotype IR kept that fallback shared by id, so it stayed linear, but LIR lowering re-lowers each referenced expression, so a k-element list pattern re-materialized the entire fallback k+1 times (one per element test plus the length test). Composed across branches this produced roughly (elements+1)^branches LIR statements, which exhausted memory or hung the compiler — e.g. the codon example in the issue grew ~4x per added branch (1136 → 4272 → 16816 statements for 3 → 4 → 5 branches). String-literal branches were unaffected because they share a single on_miss, and tag/tuple/record patterns were unaffected because they already share each branch's miss through a join point.

List patterns are now first-class through the post-check pipeline (Monotype, Lifted, Lambda Solved, Lambda Mono) and are lowered by the same decision machinery as every other pattern: lowerListPatternThen emits a length test, per-element list_get_unsafe extraction, and the optional captured rest, all jumping to the one shared miss join that lowerBranchChain already creates per branch. The rest slice uses list_take_first/list_take_last with scalar counts rather than reconstructing a {start, len} record. This makes the generated LIR linear in the pattern size, so the issue's program now compiles in well under a second instead of running out of memory. The separate refutable let-binding path for list patterns is unchanged.

A match on a List desugared each branch into a tree of nested if/match
expressions whose miss arm was the shared "rest of the match" expression.
The Monotype IR kept that shared by id (so it stayed linear), but LIR
lowering re-lowers each referenced expression, so a k-element list pattern
re-materialized the entire fallback k+1 times, compounding to roughly
(elements+1)^branches LIR statements and exhausting memory at compile time.

List patterns are now first-class through the post-check pipeline and are
lowered by the same decision machinery as tags/tuples/records, sharing one
miss join per branch via lowerBranchChain. Compilation is now linear in the
pattern size.
@rtfeldman rtfeldman marked this pull request as ready for review June 19, 2026 11:27
@rtfeldman rtfeldman merged commit 569c8fd into main Jun 19, 2026
69 of 72 checks passed
@rtfeldman rtfeldman deleted the fix-9700-list-match-exponential-lir branch June 19, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compiler out-of-memory due to long match of List(U8)

1 participant