Skip to content

[pull] master from GaijinEntertainment:master#1020

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

[pull] master from GaijinEntertainment:master#1020
pull[bot] merged 11 commits into
forksnd:masterfrom
GaijinEntertainment:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 21, 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 11 commits May 21, 2026 01:45
New `plan_decs_distinct` planner mirrors `plan_distinct`, inserted in the
cascade BEFORE `plan_distinct` (same rule as 5d/5e — decs bridge match is
stricter). Streaming dedup via `var inscope seen : table<typedecl(_::unique_key
(...))>` hoisted above `for_each_archetype` so the seen-table persists across
archetypes. Two emit shapes:

- **Buffer-required** (`to_array` default, optionally `take(N)`): per-element
  splice computes the key, `key_exists` check, on miss does
  `taken++ → seen|>insert → buf|>push_clone`. When `take(N)` is present, outer
  iteration switches to `for_each_archetype_find` and the take-cap
  `return true` propagates across archetypes — true streaming early-exit.
  Take-limit bound to a `let` at outer scope so a side-effecting `take(arg)`
  evaluates exactly once.
- **Buffer-elided** (`count`/`long_count` → `length(seen)` /
  `int64(length(seen))`; `sum` folds inline at fresh-key site via
  `acc += <projection>`): no buffer allocation, only the seen-table is
  materialized. count + take both present cascades to tier 2 (matches array-
  side `plan_distinct`).

Side-effecting `_select(proj)` upstream binds once per element to a fresh
`decs_pv` local — key + buf push (or sum fold) share the bind, matching array-
side single-eval per source element. `distinct_by(key)` wraps the key block in
`invoke(<keyBlock>, <projection-or-tup>)` and the seen-table's value type is
derived via `_::unique_key(invoke(<keyBlock>, default<elemT>))`.

Copilot review fixes:
- **plan_distinct predicate-drop bug (pre-existing).** Adds the symmetric
  arg-count guard on the terminator pop. `plan_distinct` was popping
  `count`/`long_count`/`sum` unconditionally — so
  `each(arr).distinct().count(predicate)` silently dropped the predicate and
  emitted `length(seen)` instead of filtering. Repro returned 5 instead of 3
  for `[1,1,2,2,3,3,4,4,5,5].distinct().count(_>2)`. Fix mirrors the guard
  already in `plan_decs_distinct`: pop only when `length(args)==1`; otherwise
  cascade to tier-2 which honors the predicate via `count(seq, pred)`. +4
  parity tests (2 array, 2 decs) that fail pre-fix and pass post-fix.
- Comment wording in `plan_decs_distinct` no longer references the
  non-existent `sum(src, sel)` overload.

Benchmarks (100K rows, INTERP):

| benchmark | m1 sql | m3 | m3f | m4 (was) | m4 (now) | win |
|---|---:|---:|---:|---:|---:|---:|
| distinct_count | 41 | 44 | 15 | 97 | **28** | 3.5× |
| distinct_take  |  0 | 30 |  0 | 34 |  **0** | matches m3f |

Coverage: `_select(_.brand).distinct()` + to_array / count / long_count / sum /
take(N) / take(0) / take(N>num_distinct), `_where(_.id<8)._select(_.brand).
distinct()`, `_distinct_by(_.brand)` + to_array / count / take(N), empty-decs
distinct yields empty, side-effecting take(N) arg evaluates exactly once.
`count(pred)`/`long_count(pred)` after distinct (both array + decs sources)
must honor the predicate. AST shape gates for: distinct+count (no
to_sequence, no decs_buf, single key_exists, plain for_each_archetype),
distinct+take (for_each_archetype_find + decs_buf + decs_seen + decs_taken
counters), distinct_by+to_array (unique_key wrapping the key invocation),
distinct+sum (no decs_buf, decs_acc declared+folded). +19 tests (122 → 141
in test_linq_from_decs; +2 in test_linq_fold).

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

linq_fold: plan_decs_unroll Slice 5c — distinct/distinct_by on decs
Adds unambiguous bulk-append names mirroring push/push_clone/emplace,
backfills the existing bulk forms with reserve() so they stop paying
N reallocations, and ships a lint rule (PERF022) that flags the
hand-rolled for-loop element-at-a-time shape and recommends the
new _from name.

The single names push/push_clone are overloaded between single-
element and bulk forms - B |> push(arr) silently flips meaning
depending on whether B is array of T or array of T-array. The _from
suffix names the bulk intent unambiguously.

PERF022 details:
  - Fires on for (s in A) { B |> push(s) } and push_clone, all
    three pipe/dot/plain-call shapes (compiler folds to the same
    ExprCall).
  - Body must be exactly one statement; if-guarded, multi-stmt,
    transform-then-push, multi-source, and nested for-loop shapes
    do NOT match (no clean bulk migration).
  - Source must be array of T or C-array - range/string/iterator/
    generator sources have no bulk overload.
  - Destination must be array of T - generic auto destinations
    (e.g. [expect_any_vector] clone(args, ...) in builtin.das)
    are skipped because push_from doesn't exist for dasvector.
  - Warns at the for-loop's .at (not the inner push's) so the
    same-location dedup in perf_warning doesn't collide with
    PERF006 which fires at the push.
  - emplace is out of scope: for-iter-var is const-ref, but
    emplace requires var-ref, so the hand-rolled shape doesn't
    compile in the first place.

builtin.das additions (11 new overloads): push_from x4,
push_clone_from x4 (fills the array of numT source gap that
push_clone didn't have), emplace_from x3 (gap-fill too). Each calls
reserve(long_length(Arr) + long_length(varr)) before iterating.

Existing bulk push/push_clone/emplace overloads also get the
reserve() backfill so the old name stays efficient for callers
that don't migrate. array-of-arrays single-element overloads
(push(array of T-array, T-array)) are out of scope - those push a
sub-array as ONE element, not bulk.

Codebase sweep - daslib + utils:
  daslib/{algorithm,daspkg,fio,linq,linq_fold,lint,perf_lint,regex,
  style_lint}, utils/{daspkg/index,detect-dupe/main,detect-dupe/
  test_detect_dupe,mcp/tools/common,mcp/tools/cpp_common,mcp/
  tools/cpp_grep_usage} all migrated. tests/, modules/, examples/,
  tutorials/ left as-is - CI lint gate catches new code organically.

Tests:
  - utils/lint/tests/perf022_for_push_bulk.das - 6 positives
    (push/push_clone x pipe/dot/plain), 7 negatives (if-guard,
    multi-stmt, transform, multi-source, nested-for, push-other,
    baseline push_from).
  - tests/language/array.das gains testMultiPushFrom covering each
    new overload (array of T + C-array sources, "Arr already has
    elements" reserve case).
  - utils/lint/tests/perf006_push_without_reserve.das: nolint
    PERF022 on two cases where the new rule co-fires.

Docs:
  - doc/source/reference/language/lint.rst - PERF022 section.
  - tutorials/language/06_arrays.das + .rst - "Bulk append" example.
  - doc/reflections/das2rst.das Containers regex picks up the
    three new public names.
  - doc/source/stdlib/handmade/function-builtin-{push,push_clone,
    emplace}_from-*.rst - 1-line descriptions.
  - CLAUDE.md table + skills/perf_lint.md table - PERF022 row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reuse them only on pull request. Rebuild clean repo on merge
to master.
Fix linter errors
Now we can return bool from main in jit exe.
Now WASM generation supported for daScript. It uses LLVM
infrastructure again.
…h-from

PERF022 + push_from / push_clone_from / emplace_from bulk overloads
…builds

ci: cache build results on pull request
@pull pull Bot locked and limited conversation to collaborators May 21, 2026
@pull pull Bot added the ⤵️ pull label May 21, 2026
@pull pull Bot merged commit 522313b into forksnd:master May 21, 2026
@pull pull Bot had a problem deploying to github-pages May 21, 2026 14:58 Error
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.

2 participants