[pull] master from GaijinEntertainment:master#1018
Merged
Conversation
First commit on the longarr phase 8c branch. Widens the decs storage
type-level surface to int64 and rewrites the FixedArrayIterator C++
runtime to int64 sizes. This is preparation -- the actual >INT_MAX
runtime scenario is gated by the entityLookup resize cascade
(decsState.entityLookup |> resize(int(id_base) + count) caps total
entities at INT_MAX globally) and EntityId.id : uint. Both follow in
subsequent commits on this same branch before the PR opens.
Changes:
daslib/decs.das
Archetype.size : int -> int64 (the audit's silent-corruption site)
entityLookup tuple .index : int -> int64
get_eid(... index : int) -> int64
remove_entity(... di : int) -> int64
decs_array(... capacity : int) -> int64 (routes through _i64 builder)
entity_count() : int keeps int return + panics if total > INT_MAX
(mirrors length() / long_length() surface contract)
long_entity_count() : int64 NEW -- int64-safe accessor
for_eid_archetype invoke(blk, ..., int(lookup.index)) cast at boundary
create_entities_from_cmp invoke(fill_blk, ..., int(eidx), ...) cast at boundary
get_default_ro / get_optional cap at INT_MAX with panic guard before
passing to repeat/repeat_ref (functional.das int64
widening is a separate followup)
Stride multiplications (eidx * c.stride, arch.size * c.stride, etc.)
pick up int64(c.stride) casts at every site
Comparisons with literal 0 widened to 0_l
range(arch.size) -> range64(0_l, arch.size)
daslib/decs_state.das
EcsArchetypeView.size : int -> int64
Switched _builtin_make_temp_array -> _i64 variant for the dump path
daslib/builtin.das
each(a : auto(TT)[]) passes int64(typeinfo dim(a)) to the widened
iterator binding -- the only other in-tree caller of
_builtin_make_fixed_array_iterator besides decs.das.
C++ runtime (the "rewrite said iterator to use 64 bit" piece):
include/daScript/simulate/runtime_array.h
FixedArrayIterator::size : uint32_t -> uint64_t
SimNode_FixedArrayIterator::size : uint32_t -> uint64_t
include/daScript/simulate/aot_builtin.h
builtin_make_fixed_array_iterator(... int size, ...) -> int64_t size
src/builtin/module_builtin_runtime.cpp
Same widening in the impl; negative size clamps to empty (same
pattern as builtin_make_temp_array_i64).
Tests
tests/decs/test_archetype.das
Five comparison literals widened to *_l (existing tests, same logic).
tests/decs/test_archetype_size_type.das NEW
static_assert type-contract probes locking Archetype.size : int64,
long_entity_count() : int64, entity_count() : int. Uses
stripped_typename so the test doesn't depend on PR #2764 (typeinfo
is_int64) landing first.
tests/decs/test_archetype_bulk_create_int64.das NEW
Small-N (100K + 1K) runtime regression covering bulk-create + delete
arithmetic neighbors. The genuine >INT_MAX memory-gated probe waits
for the cascade widening to enable past-INT_MAX creation.
Test status: tests/decs/ 244/244 pass. tests/language/ 1003/1003 pass.
tests/long_array_table/ 45/45 pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…MAX cap
Second commit on the longarr phase 8c branch. Widens the daslib-layer
cascade that gated entity creation at INT_MAX globally. After this,
entity allocation is capped only by EntityId.id : uint = UINT_MAX,
which is loudly panicked instead of silently wrapping to
INVALID_ENTITY_ID.
The architectural widening of EntityId.id : uint -> uint64 stays out
of scope (ABI break for embedders).
daslib/decs.das changes:
new_entity_id() per-entity path
length(entityFreeList) -> long_length(entityFreeList) -- last-element
index, can't panic past INT_MAX freeList
length(entityLookup) -> long_length(entityLookup) for id allocation
Adds UINT_MAX cap with panic("decs: entityLookup reached UINT_MAX
entries... EntityId.id (uint) widening is required") -- replaces the
silent wrap to id=0 (INVALID_ENTITY_ID).
create_entities_from_cmp bulk path
uint(length(entityLookup)) -> long_length() with id_base_long : int64
resize(int(id_base) + count) -> resize(id_base_long + int64(count))
-- routes through the i64 resize
overload, no INT_MAX truncation
Same UINT_MAX cap with a tailored panic message including current
count and requested batch size.
is_alive(eid) bounds check
int(eid.id) >= length(entityLookup) -> int64(eid.id) >= long_length(...)
Prevents wrap when entityLookup > INT_MAX entries.
Test status: tests/decs/ 244/244 still pass; cascade widening is
upward-compatible at small N.
The genuine memory-gated >INT_MAX runtime test follows in the next
commit (requires ~50 GB to push entityLookup past INT_MAX with a
single-byte payload archetype, gated on DASLANG_HUGE_HEAP_TESTS=1).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Third (and likely final pre-PR) commit on the longarr phase 8c branch.
Cascade fix:
Three sites in daslib/decs.das resized component byte storage via
c.data |> resize(length(c.data) + c.stride * count)
with `length(c.data) : int`. Past INT_MAX bytes per archetype (which
is reachable as soon as arch.size * stride > INT_MAX), length() would
panic per the long-array surface contract. Widened to:
c.data |> resize(long_length(c.data) + int64(c.stride) * int64(count))
Routes through the int64 resize overload; sites covered:
create_entity (per-entity resize, +1 stride)
create_entities (bulk pre-resize, +stride*count)
create_entities_from_cmp (same, bulk path).
Test:
tests/decs/test_huge_entity_count_int64.das NEW
Memory-gated probe (DASLANG_HUGE_HEAP_TESTS=1, ~46 GB) that creates
2.2 G entities in two 1.1 G chunks (each fits in int) via the bulk
create_entities path. After the second chunk total > INT_MAX. Asserts:
long_entity_count() returns int64 total (the int64-safe surface)
decsState.allArchetypes[0].size matches (the widened field
carries the value
without wrap)
The entity_count() panic side stays documented (try/recover banned
for panic UX testing per the soft-fail rule). The CI no-op pass is
preserved via the inline huge_enabled() gate.
This is the test that proves the actual win of phase 8c: without
PR-C, this scenario would silently corrupt arch.size or panic in the
length/resize cascade before reaching 2.2 G entities. With PR-C,
storage round-trips end-to-end.
Test status: tests/decs/ 245/245 pass (CI no-op skip works without
the env var). Sanity-sweep tests/language/ 1003/1003,
tests/long_array_table/ 45/45 confirm no regression in the broader
FixedArrayIterator-touching suite.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… INT_MAX The part 3 test design was wrong: [decs_template] splits each struct field into a SEPARATE component. A 10-int struct creates 10 components at 4-byte stride each (400 MB per c.data per component, never crosses INT_MAX). The probe ran 100 M create_entities iterations with 10 set calls per entity (~1 B set calls total) but the c.data cascade under test was never exercised. The probe also crashed (exit 5 deep in the SimNode_BlockNF block-invoke recursion -- likely OOM on per-component overhead at 11 components * 100 M entries). Rewrite uses ONE field of type float4[4] = 64-byte stride (the maximum per ComponentValue.data layout). 35 M entities * 64 B = 2.24 GB c.data per component, crossing INT_MAX (2.147 GB). One component = one c.data array, so the `length(c.data) + c.stride * count` arithmetic on master (pre-PR-C) would overflow int immediately on the first create_entities resize: 64 * 35_000_000 = 2_240_000_000 > INT_MAX. Memory: ~3 GB (2.24 GB c.data + 700 MB entityLookup). Runs in ~115 s on the local workstation. Verified locally with DASLANG_HUGE_HEAP_TESTS=1: --- PASS 'test_long_length_resize_cascade' (114.160343s) This is the test that proves part 3's c.data widening (`long_length(c.data) + int64(c.stride) * int64(count)`) actually delivers the win at the byte-storage layer. Without PR-C, this test would fail on master. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The dasfmt rule inserts a space between `>` and `.field` after a default<T> / type<T> closing angle bracket. The parser accepts both forms, so this is purely cosmetic but it tripped the pre-push formatter --verify gate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…contracts Part 5's formatter-inserted space (`default<Archetype> .size`) trips the lint compile path with "static assert condition is not constexpr or const" even though dastest accepts it. Switching to runtime `t |> equal` with the same typeinfo predicates: the typeinfo strings still resolve at compile-time and the comparison is effectively const-folded, just at test runtime. The contract is preserved -- a future revert of the widening flips this test red the same way. Also avoids the LINT002 unused-var warning by giving the type-witness-only local a leading underscore (`var _a : Archetype`). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…type.das
Three lint warnings in the file PR-C already touches (per the
mandatory-lint policy + no-preexisting-excuses rule). All pre-existing
on master.
LINT002: unused (k, v) in `for (k, v in keys(...), values(...))` -> _k, _v
(we only count entries, not use the keys/values)
PERF006: `for (i in range(4)) { ... eids |> push(eid) }` without
prior reserve -> add `eids |> reserve(4)` before the loop.
No behavioral change; tests/decs/ stays at 245/245.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three issues addressed in one commit: 1. CI failure on darwin/linux_arm AOT step: daslib/linq_fold.das:3087 — `emit_decs_count_archsize` emits `accName += archName.size` where archName.size is int64 (PR-C) and accName is the int accumulator backing `count() : int`. Cast at the += site (`int($i(archName).size)`); users who need int64-safe totals chain `long_count()` (which already emits int64 accumulator). Fixes the linq AOT batch that failed across the macOS/arm CI lanes. 2. Copilot C1 — off-by-one in entity allocation cap: daslib/decs.das:419 used `next_id >= 0xFFFF_FFFF` (prevents allocating id == UINT_MAX) while line 1043 used `>` (allows it). Extracted `MAX_ENTITY_ID = int64(0xFFFF_FFFF_l)` near INVALID_ENTITY_ID and made both per-entity and bulk paths consistent on `> MAX_ENTITY_ID`. EntityId.id : uint range is [0, UINT_MAX] inclusive; the panic guards the actual overflow boundary. 3. Copilot C2 — silent int truncation at block-invoke boundaries: daslib/decs.das:682 (for_eid_archetype) and :1050 (create_entities_from_cmp). With the cascade widened, lookup.index / eidx can exceed INT_MAX up to UINT_MAX, so `int(...)` casts silently truncate. Replaced the misleading "today's cap keeps it ≤ INT_MAX" comment with a panic guard before the cast (mirrors get_default_ro / get_optional pattern). Long-* block variants with int64 sig are the followup; until then the cap is loud. Test status: tests/decs/ 245/245, tests/linq/ 1163/1163 — confirmed locally with the new build. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… double accumulator) Both `emit_accumulator_lane` (array-side) and `emit_decs_accumulator` (decs-side) diverged from the canonical `linq.das:1545-1574` `average` implementation: - Accumulator was `accType` (often int → overflow risk), should be `double` - Counter was `int`, should be `uint64` - Empty source returned NaN (from `0.0lf / 0.0lf`), should return `0.0lf` via cnt==0 guard Aligned both sites. Macro-time `accType.baseType == Type.tDouble` cast-skip preserved so already-double chains avoid PERF020 redundant-cast trip on the hot path. Un-skips the deferred regression test `test_zip_average_empty_returns_zero` from PR #2742's review, flips `test_average_accumulator.average: empty → NaN` to expect `0.0lf`, and adds `test_unroll_average_empty_returns_zero` for the decs-side path. Both PR #2765 and PR #2766 Copilot reviewers flagged this 2-site divergence; deferring was intentional (kept resurrection PRs scoped to cherry-picking). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three issues addressed together (round 2 of CI failures + Copilot review): 1. CI failure on darwin/linux_arm AOT cpp compilation: AOT-generated cpp calls `builtin_make_temp_array_i64(...)` (emitted from daslib/decs.das `decs_array` via PR-C part 1) but `include/daScript/simulate/aot_builtin.h:99` only forward-declared the int variant. clang errors with "use of undeclared identifier 'builtin_make_temp_array_i64'". Added the int64 forward declaration alongside the existing one. The cpp definition exists in module_builtin_runtime.cpp:1684 already. 2. Copilot C3 — UINT_MAX cap off-by-one in create_entities_from_cmp: `id_base_long + int64(count) > MAX_ENTITY_ID` rejects the boundary case where the LAST allocated id == UINT_MAX (id_base == MAX_ENTITY_ID && count == 1). Highest id allocated is `id_base + count - 1`, not `id_base + count`. Changed comparison to `id_base_long + int64(count) - 1_l > MAX_ENTITY_ID`. 3. Copilot C4 — INT_MAX cap off-by-one for fill_blk (same pattern): `base + int64(count) > int64(INT_MAX)` rejects the boundary `base == INT_MAX && count == 1` where eidx = INT_MAX is a valid int cast. Changed to `base + int64(count) - 1_l > int64(INT_MAX)`. The per-entity new_entity_id cap (`next_id > MAX_ENTITY_ID`) is correct as-is — next_id IS the id being allocated, so the boundary `next_id == MAX_ENTITY_ID` is legitimately allowed by `>`. Test status: tests/decs/ 245/245 still pass locally. CI re-runs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…verage-empty-guard linq_fold: align average semantics with linq.das (empty-source guard, double accumulator)
…se8c-decs-archetype-size phase 8c: decs Archetype.size int64 + entityLookup cascade + FixedArrayIterator widening
Extends `plan_decs_unroll` to recognize trailing `take(N)` / `skip(N)` after the where/select chain. New `extract_decs_ranges` peels them into `DecsRangeInfo`; counter inits hoist above `for_each_archetype` so they span archetypes; per-element guards wrap `perElement` BEFORE chain so ranges apply to the post-`where_` stream (mirrors array-side `wrap_with_condition(wrap_with_ranges(...))`). When `takeExpr != null` the outer call switches to `for_each_archetype_find` with a `: bool` lambda so the take-cap stop propagates across archetypes — the inner `return true` exits both the for-loop and the lambda in one shot. All 4 non-bare-count emit paths threaded: `emit_decs_accumulator`, `emit_decs_early_exit`, `emit_decs_min_max_by`, `emit_decs_to_array`. Bare `count` via arch.size shortcut still bails on any chain ops including ranges. Tests: 11 new in `tests/linq/test_linq_from_decs.das` — take/skip/skip+take parity, where+take, select+take+sum, take+first, take+to_array, take(-N) short-circuit, skip-beyond-end, plus AST-shape gates confirming take routes to `for_each_archetype_find` and skip-only stays on plain `for_each_archetype`. Verification: - format clean, lint clean - interp: 41 + 11 new = 52/52 in test_linq_from_decs.das; 777/777 across all 11 linq test files - AOT: 1239/1239 via `bin/test_aot -use-aot dastest/dastest.das -- --use-aot --test tests/linq` Bench numbers (take_count / skip_take / take_count_filtered / take_sum_aggregate) all collapse to 0 ns/op — splice fires (AST gates confirm) but the compiler folds the tight chain when `accept(rows)` doesn't break the constant path. Numeric wins blocked on the plan.md Task 1 anti-DCE sweep. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…splice fires) Original claim was wrong. AST dump shows the splice IS firing and actively building the full 1000-element result array (for_each_archetype_find + decs_takec counter + push_clone). The 0 ns/op number is the bench framework's body_time / n_iters reporting hitting its measurement floor — same as the m3f array splice (also 0 ns/op, identical shape). Old m4 baseline 36 ns/op → new sub-1 ns/op ≈ 36× actual win, just below bench resolution. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ins+take + take-limit hoist Three correctness fixes flagged in Copilot review of PR #2770: 1. **Early-exit boolean confusion when take present (review #1).** When `takeExpr != null`, `any`/`all`/`contains` now route their answer through explicit `decs_found` state instead of `for_each_archetype_find`'s return value. Take-cap exits also emit `return true` (to stop the archetype walk), so the find result was ambiguous: `take(3).any(_>100)` on a 7-element source where no element matches would return true (the take-cap hit, not a match). Same trap for `contains` (false positive) and `all` (false negative — take-cap looks like "counterexample found"). Fix: in the takeExpr-present branch, set explicit `decs_found = true` on the actual match path; tail returns `decs_found` (or `!decs_found` for `all`); the find call's result is used only as iteration control. 2. **Take limit re-evaluated per element (review #2).** `wrap_inner_for_with_decs_ranges` embedded `clone_expression(rangeInfo.takeExpr)` directly in the per-element guard, so the take expression evaluated on every iteration. `take(src, total:int)` semantics evaluate `total` once at call time. Fix: hoist take limit into a `let decs_takel = $e(takeExpr)` in `decs_range_prelude` (alongside the existing skip hoist); per-element guard compares against the hoisted local. 3. **Missing coverage (review #3).** 7 new behavioral tests + 1 hoist regression test in tests/linq/test_linq_from_decs.das: - `take(0).any()` returns false on non-empty source - `take(3).any(_ > 100)` returns false when no prefix match (catches review-1 bug) - `take(3).any(_ == 1)` returns true when match in prefix - `take(3).all(_ >= 0)` returns true when prefix passes (catches review-1 bug) - `take(3).all(_ <= 1)` returns false when prefix has counterexample - `take(5).contains(2)` returns true on hit - `take(2).contains(100)` returns false on miss (catches review-1 bug) - `take(side_effect_fn())` evaluates the take expression exactly once (catches review-2 bug) Verification: - format clean, lint clean - interp: 60/60 in test_linq_from_decs.das (was 52, +8 new) - AOT: 1247/1247 via bin/test_aot (was 1239, +8) - ast_dump on target_unroll_take_all_fail_fold confirms explicit-state shape: `var decs_found = false; ... if (!pred) { decs_found = true; return true } ... return !decs_found` with the for_each_archetype_find result discarded Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the last int-only gaps in the daslib/builtin.das array surface
that prevent code from reaching insertion positions past INT_MAX. The
underlying C++ runtime (`__builtin_array_push` / `_push_zero` and
`__builtin_array_resize_i64`) has been int64-capable since the Phase 2+3
landings; only the daslang wrappers needed widening.
Widened (int -> int | int64 + static_if (typeinfo is_int(at)) dispatch,
so the int64 branch never pays an int64(at) ExprCall cost):
* push(arr, value, at) -- copy + move variants (lines 113, 126)
* emplace(arr, var value, at) -- line 224
* push_clone(arr, value, at) -- copy + move variants (lines 274, 287)
* resize_and_init(arr, newSize) -- + 2-arg initValue variant (lines 53, 69)
For resize_and_init, the static_if also picks the matching backing call
(__builtin_array_resize vs _i64), length() vs long_length(), range() vs
range64() -- the int variant stays on the int-flavor SimNode path
unchanged.
Tests:
* tests/long_array_table/test_int64_overloads.das -- 5 new
overload-resolution unit tests (push/emplace/push_clone at int64,
resize_and_init int64 + initValue). Fails on master with "no matching
overload"; passes here.
* tests/long_array_table/test_huge_array_push_emplace_clone.das --
extended with 3 huge insertion-path probes (push/emplace/push_clone
at position > INT_MAX). Memory-gated on DASLANG_HUGE_HEAP_TESTS=1;
verified locally on a 2.2 GB uint8 array (6 tests, 2.6s).
* tests/long_array_table/test_huge_array_erase.das -- NEW. 2 probes
for the already-widened erase(at:int64) / erase(at,count:int64)
paths past INT_MAX. Same gate; verified locally (2 tests, 0.8s).
Side-effects:
* doc/source/reference/language/generic_programming.rst -- adds
typeinfo is_int / is_int64 to the trait list (carry-over from
PR #2764 deferral).
* daslib/builtin.das -- swept all 31 pre-existing lint warnings
(STYLE024/025/026/005) per the bundling pattern. 6 of the clone /
clone_to_move variants carry an explicit `// nolint:STYLE025` with
the reason -- `var clone_dest : TT -#` is conditionally unsafe (the
is_unsafe_when_uninitialized type set), and the unsafe block scope
cannot be narrower than the var-decl.
Regression locally: 1785 tests pass across
long_array_table (58) + decs (245) + linq (1228) + daslib (92) +
algorithm (162). `dasfmt --verify` clean on full tree (17470 files).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ecs-unroll-slice5a linq_fold: plan_decs_unroll Slice 5a — take/skip ranges on decs
…andmade
Two CI failures from the initial PR-builtin commit:
1. tests/language/lock_array.das fails on darwin26/darwin15/linux_arm and
wasm with `error[31000]: address of reference requires unsafe` at
daslib/builtin.das lock_data. The STYLE025 sweep narrowed
`unsafe { invoke(blk, reinterpret<TT?#> addr(a[0]), len) }` to
`invoke(blk, unsafe(reinterpret<TT?#> addr(a[0])), len)`, but `addr()`
needs its own unsafe context too -- the lint's STYLE025 leaf detection
doesn't recognize addr() as an unsafe op, so the rule fires on what is
actually a 2-unsafe-leaf block (justified). Reverted both lock_data
variants (var + non-var arrays) back to block form with `// nolint:
STYLE025` and the reason. The empty-array null branch keeps the
narrowed `unsafe(reinterpret<...> null)` since no addr is involved.
Caught locally by `dastest -- --test tests/language`. Pre-push only
ran tests/long_array_table + tests/decs + tests/linq + tests/daslib +
tests/algorithm and missed lock_array which lives under tests/language
-- expanded local pre-push test scope going forward.
2. `Check for // stub in handmade docs` CI step found a new stub at
doc/source/stdlib/handmade/function-builtin-resize_and_init-0xba1d8e
a89988acec.rst. The int|int64 widening changed the canonical signature
hash so das2rst.das emitted a fresh stub. Per the documentation_rst
skill, daslib uses handmade-flow for per-symbol docs -- filled the
stub with the same 1-line description as the prior int-only hashes
(default-init resize). The 2-arg `initValue` variant continued to
resolve to its existing handmade file (unchanged hash).
Verified locally:
* lint clean on daslib/builtin.das (0 warnings)
* tests/language -- 1003/1003 pass (was 1 error on master + this PR)
* tests/long_array_table -- 58/58 pass
* das2rst.das -- no stubs remaining
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…y skipped The pre-push hook runs `daslang utils/lint/main.das -- <changed> --quiet` on every changed `.das` file. `utils/lint/main.das:88` hard-codes `daslib/builtin.das` to the `is_skip_file` set (lint can't compile it standalone; it's parsed as the implicit-prelude module). Before this fix: if the only changed `.das` is `daslib/builtin.das`, `scan_das_files` skips it, `files` ends empty, lint exits 1 with "Error: no .das files found". That's a false negative -- the user DID nothing wrong; the skip-list filtered out the only input. After this fix: `scan_das_files` tracks `var skipped : int&`. If `files` is empty at the end AND `skipped > 0`, lint exits 0 silently. If `files` is empty AND `skipped == 0` (genuine "no .das at the path"), the original error path stays. Caught while pushing part 2/N of PR #2771 -- the only changed `.das` was `daslib/builtin.das` and pre-push refused to push despite local verification. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Slice 5b extends DecsRangeInfo (added in #2770) with predicate-driven ranges (skipWhileCond/takeWhileCond). extract_decs_ranges now walks the suffix in canonical order skip→skip_while→take_while→take, bails when a select appears in the prefix (mirrors array-side seenSelect bail at linq_fold.das:1615/1623). Predicates peel against the source tuple tupName, so the post-where stream is visible but selects are forbidden. wrap_inner_for_with_decs_ranges + decs_range_prelude gain skippingName (one-way flag, hoisted at invoke scope so it persists across archetypes). Per-element guard order matches array-side wrap_with_ranges: take-cap → skip-counter → skip_while flag → take_while break → takeC++. All 4 emit fns (accumulator, early_exit, min_max_by, to_array) extend the outer dispatch to switch to for_each_archetype_find when takeExpr != null OR takeWhileCond != null (both trigger inner `return true`). useExplicitState in emit_decs_early_exit extends correspondingly so any/all/contains + take_while still distinguish "real match" from "take_while-stop" via explicit foundName state. Bench: take_while_match m4 55 → 8 ns/op (6.9× win over eager bridge). Coverage: 21 new tests (60 → 81 in test_linq_from_decs.das) — including AST shape gates (take_while → _find; skip_while-only → for_each_archetype), explicit-state regression guards (take_while + any/all/contains), and edge cases (always-true/always-false predicates, skip+take_while, skip_while+take, where+take_while). 1268/1268 linq interp + 1647/1647 AOT (linq+decs+aot). Pre-existing STYLE015 in emit_decs_count_archsize condensed inline. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ltin-int-int64-at phase 8d: builtin.das push/emplace/push_clone/resize_and_init int|int64
…ally exercise the splice Copilot caught that the take_while+_select+terminator tests (any/all/contains/ sum/first/to_array variants) were falling through to the tier-2 eager bridge, NOT exercising the new explicit-state routing in emit_decs_early_exit. Root cause: canonical chain order forbids _select after a range op (extract_decs_ranges bails at the non-range op in suffix walk), and the array side enforces the same rule (linq_fold.das:1623). Fix: - any/all rewritten to predicate over the source tuple directly: `_take_while(_.val<3)._any(_.val==2)` instead of `_take_while(_.val<3)._select(_.val)._any(_==2)`. Now splices through emit_decs_early_exit's useExplicitState path (verified via ast_dump). - sum/first/to_array: sum-after-take_while is unsplicable on either array or decs side (need _select to project, but select-after-while bails). Dropped sum variant; replaced first/to_array to operate on source tuples (test field access on the returned tuple). Added min_by variant — exercises emit_decs_min_max_by under take_while routing through for_each_archetype_find. - contains: source-tuple equality not defined; dropped (any/all already exercise the same useExplicitState branch). - New `test_unroll_take_while_any_splice_shape` AST gate asserts: to_sequence count == 0 (splice fires, not tier-2), for_each_archetype_find count == 1 (take_while-stop early-exit dispatch), decs_found token count == 3 (explicit-state routing emits var+set+return). Coverage: 60 → 80 (was 81 pre-rewrite, -1 for dropped contains, +2 for min_by/splice_shape, net +20). 1267 linq interp + 1512 AOT+decs green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…boost)
Closes the int-only hotspots across three daslib helper modules.
== array_boost.das ==
* array_helper (private, both var + const variants): `_::length(arr)` ->
`_::long_length(arr)` + `_builtin_make_temp_array_i64`. This is the
path the public `temp_array(arr)` delegates to, so passing a > INT_MAX
element source now Just Works instead of panicking on length().
* `temp_array(data, lenA, a)` (both var + const): consolidated the two
int + two int64 overloads to two `int | int64` signatures with
`static_if (typeinfo is_int(lenA))` dispatch. Mirrors the PR-builtin
pattern.
* `array_view(bytes, offset, length, blk)` (both const + var-bytes):
consolidated to one `int | int64` signature per byte-variant with
static_if dispatch. The int branch's signed-overflow guard
`(offset + length) > length(bytes)` -> `uint(offset) + uint(length) >
uint(_::length(bytes))` (avoids signed overflow UB on adversarial
inputs even within INT_MAX-bound arrays). The int64 branch lifts the
pre-existing uint64-arithmetic bounds check.
== json_boost.das ==
* `from_JV` generic-array deserialization at line 475: `reserve(arr |>
length)` -> `reserve(arr |> long_length)`. Past INT_MAX, the int
return silently truncated the reservation and corrupted capacity.
* `JV(value)` array builder at line 628: same `reserve(length)` ->
`reserve(long_length)`. The `is_dim` static_elif branch keeps
`reserve(typeinfo dim(value))` since fixed-size arrays are bounded
by their dim() and `long_length` is array<T>-only.
* Two PERF020 lint hits at lines 63 + 75 in null_coalescing/with_string
(template instantiations where TT == double / TT == int64 make the
`TT(...)` cast a no-op) -- annotated `// nolint:PERF020 generic` per
the convention. Sweep cost while we were here.
== strings_boost.das ==
* `jaccard(a, b : table<string>)` HIGH-severity overflow at line 219:
`length(a) + length(b) - intersect` int math wrapped on huge sets.
Widened `var intersect = 0_l` (int64) + `long_length(a) +
long_length(b)`. Float ratio output unchanged.
* `levenshtein_distance` / `_fast` and `replace_multiple` left as-is:
each guarded at the top by `let len = length(s)` which panics with
a clear message before any wider corruption. O(N) / O(N^2) on > INT_MAX
chars isn't a real use case; the panic gate is correct behavior.
== Tests ==
* `tests/long_array_table/test_int64_overloads.das` extended with
`test_temp_array_data_lenA_int64` + `test_array_view_int64` (small-N
overload-resolution + correctness).
* `tests/long_array_table/test_huge_temp_array.das` (NEW) -- memory-gated
probe over a 2.2 GB uint8 source: `temp_array(arr)` and
`temp_array(data, HUGE_N, type<uint8>)` both verify round-trip read
at index 0 and index `HUGE_N - 1`.
* `tests/long_array_table/test_huge_array_view.das` (NEW) -- memory-gated
probe: `array_view` at offset > INT_MAX, const + mutable view variants.
Regression locally: 3211 tests pass across long_array_table (64) +
decs (245) + linq (1247) + daslib (92) + strings (361) + json (266) +
language (1003). All four huge probes verified with
DASLANG_HUGE_HEAP_TESTS=1 on a 2.2 GB array (4 tests, 1.6s).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tutorial 33_algorithm.das exercises `temp_array(int[5])` via algorithm.reverse and algorithm.min_element. PR-G switched array_helper to long_length + _builtin_make_temp_array_i64 unconditionally; long_length only accepts array<T>/table<K;V>, so fixed-array callers got "no matching functions" at tutorial dry-run on darwin15 extended_checks. Split with static_if (typeinfo is_array(arr)): - array<T> path: long_length + _builtin_make_temp_array_i64 (huge-array safe) - fixed-array path: length + _builtin_make_temp_array (size known at compile time, always int-bounded) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ecs-unroll-slice5b linq_fold: plan_decs_unroll Slice 5b — take_while/skip_while on decs
…lib-boost-int-int64 phase 8e: daslib boost widenings (array_boost / json_boost / strings_boost)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 : )