Skip to content

[pull] master from GaijinEntertainment:master#1012

Merged
pull[bot] merged 8 commits into
forksnd:masterfrom
GaijinEntertainment:master
May 19, 2026
Merged

[pull] master from GaijinEntertainment:master#1012
pull[bot] merged 8 commits into
forksnd:masterfrom
GaijinEntertainment:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 19, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

borisbat and others added 8 commits May 19, 2026 01:58
Closes the deferred follow-up from PR-D: a having predicate may now
reference a reducer absent from the select. plan_group_by scans the
predicate first, appends a hidden ReducerSpec for each unmatched
reducer (bare or inner-select), and extends the named-tuple table
value type with the new slot's acc type. The existing PR-D
rewrite_having_pred runs unchanged once specs cover every reducer
reference; result-build re-synthesizes the user's tuple slot-by-slot,
naturally omitting hidden slots by iterating only the user-visible
argTypes.

Bare-form select with hidden slot cascades to tier 2 — the bare
table type uses `tuple<KeyT; AccT>` synthesized inside a qmacro with
embedded `typedecl(invoke(...))` for the key, which can't be
dynamically grown with extra acc slots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…g (PR-E)

New benchmark groupby_having_hidden_sum mirrors the m1/m3/m3f tri-form
pattern with an inner-select-sum reducer present only in the having
clause (no matching select slot).

Headline: 40 ns/op (m3f) — 2.7x over m3 plain LINQ (109), 4.4x over
m1 SQL (175). groupby_having_count (regression check) stays at 36
ns/op, so the scan-then-rewrite refactor preserves the matching-slot
splice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
C1 (correctness): scanner bail for second same-named hidden inner-select.
A having clause with two same-named inner-select reducers over different
lambdas (e.g., sum(select(λ1)) + sum(select(λ2))) previously routed both
predicate terms to the first hidden slot — silently wrong. Now bails to
tier 2, which evaluates each select() separately and stays correct.
Match against a select-side spec (visible slot ≤ userVisibleSlotCount)
still reuses, preserving PR-D's matching-slot optimization. Pinned by
new parity test `test_group_by_having_two_same_named_inner_select_diff_lambdas`
and AST cascade test `test_group_by_having_two_same_named_inner_select_cascades_to_tier2`.

C2 (cosmetic): align groupby_having_count m3 in the PR-E regression-check
row to PR-D's published 78 (was 92 from a fresh run; ~17% variance on the
~80 ns/op baseline). The row's purpose is to show m3f stayed at 36.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…roupby-having-hidden-slot

linq_fold group_by: hidden-slot reducer-in-having (PR-E)
…ent type)

`aggregate_impl_const` checked `is_workhorse(type<TT>)` where TT is the element
type, but move-vs-return is decided by AGG (return/accumulator type). The two
differ for aggregates that produce a workhorse acc from a non-workhorse element
(e.g. summing prices off `array<Car>` with int seed) — the impl tried
`return <- int_from_const` which fails to compile. Fixed by checking
`is_workhorse(type<AGG>)` to match the public `aggregate(array, ...)` overload's
own static_if at linq.das:1478. Surfaced by adding a benchmark with non-workhorse
element + workhorse seed for the upcoming terminal-walk splice work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…R-F)

Closes the `test_linq_element.das` (last / last_or_default / single /
single_or_default / element_at / element_at_or_default) and the
`test_linq_aggregation.das` (aggregate(seed, fn)) `_fold` cascade gaps.

Builds on the existing EARLY_EXIT lane infrastructure. The function name
`emit_early_exit_lane` is now a misnomer (last/single/aggregate walk the full
source), but the structural shape — one for-loop + prelude/per-match/tail
stmts + optional skip/take wrap — fits all seven new operators perfectly.

Three pieces:

1. `classify_terminator` extension. Adds 7 new names to the EARLY_EXIT bucket
   (the lane now covers any single-return terminator with that emission shape).

2. `fold_linq_cond2` helper. 2-arg sibling of `fold_linq_cond` that peels
   `block<(acc, x):AGG>` bodies — single-return blocks get their formals
   renamed via `Template.renameVariable` on both `acc` and `x`. Non-peelable
   bodies return null so the caller falls back to runtime `invoke(fn, acc, val)`.

3. Per-op emission arms (in `emit_early_exit_lane`):
   - last / last_or_default: prelude `var found; var lastBind`; per-match
     `found = true; lastBind := val`; tail panic / return default.
   - single / single_or_default: per-match panics or returns default on the
     SECOND match; single keeps walking, single_or_default early-exits.
   - element_at / element_at_or_default: counter early-exit at the N-th
     surviving match; negative index pre-loop panics or returns default.
   - aggregate: workhorse seed uses `=` / `return`; non-workhorse uses
     `<-` / `return <-` (matches linq.das:1466 user-side static_if).

Tests:
- 36 new parity subtests across 7 [test] functions (test_linq_fold.das)
  covering bare/where/select/empty/non-workhorse-string/skip-take/parity-vs-
  plain-linq for each operator.
- 9 new AST-shape tests (test_linq_fold_ast.das) asserting splice fires:
  invoke wrapper, single for-loop, panic-presence-or-absence, counter ++,
  peeled aggregate body (zero per-element invoke nodes via new
  `count_invoke_nodes` helper).

342 + 125 tests green in interpreter; same in AOT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four new tri-form (m1 SQL / m3 plain LINQ / m3f _fold splice) benchmarks at
100K rows:

- last_match: m3 29 → m3f 5 ns/op (5.8× over m3)
- single_match: m3 19 → m3f 2 ns/op (9.5× over m3)
- element_at_match: m3 29 → m3f 0 ns/op (early-exit at ~100 source elements;
  splice exits after INDEX+matching_density elements, so total time / N
  bottoms out below dastest's timer granularity)
- aggregate_match: m3 51 → m3f 5 ns/op (10.2× over m3, 6.8× over m1 SQL —
  peeling the block body inline + fusing the upstream where filter
  eliminates BOTH the per-element block invoke AND the where-iterator
  allocation; m3 pays for both, m3f for neither)

LINQ.md gets a "Phase 3+ terminal-walk lane" section documenting design,
bail cases, edge cases, headline numbers, and a deferred-follow-up note
(aggregate with non-peelable multi-statement block bodies cascade to
tier 2 — correct but slower; future `return $b(stmts)` recognizer would
fix that). Coverage checklist rows for test_linq_element.das and
test_linq_aggregation.das flip from ⏳ to ✅ for the relevant operators.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erminal-walk-last-single-element-aggregate

linq_fold: terminal-walk lane for last/single/element_at/aggregate (PR-F)
@pull pull Bot locked and limited conversation to collaborators May 19, 2026
@pull pull Bot added the ⤵️ pull label May 19, 2026
@pull pull Bot merged commit 6d0be5e into forksnd:master May 19, 2026
@pull pull Bot requested a deployment to github-pages May 19, 2026 14:58 Queued
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant