Repository navigation
fix(check): a constructor's fields grow as a list, without copying - #1966
Conversation
Overlap with #1965 and #1967#1965 and #1967 each do a smaller version of part of this PR. Suggestion: merge those two and cut this PR down to what they don't cover, rather than closing either of them. All three touch the same code, and each of the two conflicts with this PR if this one lands first, so the order needs deciding up front.
Where each one is better
Suggested plan
That should keep almost all of the speedups for a much smaller diff. It also leaves less AI-written code in Not verified
🤖 Generated with Claude Code |
|
Verified against current main ( The issue is overlap: this conflicts with both #1965 and #1967, and your own comment already suggests landing those two first and cutting this down. I agree. Two review points beyond the overlap:
|
lhs_ext and term_check_mat_goal built a constructor's fields with [...xs, x] and xs.concat([x]), so n fields copied n(n-1)/2 entries. They cons onto the existing List and build the array once with list_to_array. The list is persistent because these closures can be entered more than once. The Ref case reads a call's pending arguments with the same helper. Fixes #1956. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
e954dc9 to
e8bfacb
Compare
Fixes #1956. #1967 did the call spine and
body_flatten, and this does the constructor fields, the last copies #1956 names.What
lhs_extandterm_check_mat_goalbuilt a constructor's fields with[...xs, x]andxs.concat([x]), so a constructor with n fields copied n(n-1)/2 entries. They now cons onto the existingListand build the array once withlist_to_array. The list is persistent because these closures can be entered more than once.The
Refcase from #1967 now reads a call's pending arguments with the same helper, instead of its own loop.Output is unchanged: the fields come out in source order, and
tests/check/list_order.bendpins that.Numbers
--check-onlyon a match over one constructor with 4,000 fields, best of 9, macOS: 257 ms onmain, 220 ms here.bend.tsgoes from 45,100 to 45,150 ttok (cap 48,000).The gain only shows on very large constructors. The point of this PR is finishing #1956.
Dropped from the earlier version
This PR was +151/-54. It no longer has:
ctx_dead: it changed which@unsafebinder type a program hangs on, to save 0.4-0.6 s at 8,000 empty matches whenEmptyisn't first. perf(check): ctx_dead searches the scope in place instead of copying it first #1965 fixes [complexity, checker] O(n^2 log n) — An empty match copies all variables before inspecting the first #1955 with main's order.match_flatten: about 0.02 s saved, and it removed the "a variable pattern" error that guarded the path. That code is back as it is onmain.patt_lams, thepmap_to_arraychange and the list spine interm_infer: perf(check): ctx_dead searches the scope in place instead of copying it first #1965 and perf(check): a call's pending arguments are a cons list, and parameters flatten in one loop #1967 cover those cases, exceptpatt_lams' later-binder branch, which saved about 0.03 s at 4,000 binders.A review suggested recording dead binders in
ctx_bindinstead. I didn't do that: it would have to reduce every binder's type when it's bound, and main only does that when an empty match needs it.def f(x: loop(0n)) -> Natfinishes onmaintoday, so that would add hangs.Testing
maine73e9f98,gates/_run.ts:repo54/54,perf124/124,test1817/1817.01fca262: four more side-by-side runs and three solotestruns all passed. An earlier side-by-side run printedtest1816/1817. I didn't capture which test that was, and it didn't come back.pingwas skipped, since there's no site repo at the path it expects.gates/safe.tsand Metal and CUDA weren't run on this version.list_order.bendalso covers template arguments in a call and the empty-match search on both sides of the scope tree, which #1967 and #1965 change. I removed itsskipcase, which only guarded the batched var columns.🤖 Generated with Claude Code