Running the file below with bin/daslang (Release, macOS arm64, master as of 2026-09-01) throws EXCEPTION: can't delete locked array at daslib/builtin.das:1499 at the end of main, where the scoped rows is deleted. Two observations that make it look like a lock-count leak in a fused at permutation rather than a logic error:
- Replacing the two
let lines with a direct call eq("fam_one", rows[1][0]) passes; so does string interpolation "{rows[1][0]}". Reading into a let local fails.
- On the extended-checks linux lane (Release, gcc; run 33573527004) the comprehension form
[for (k in range(1, n - 1)); cells[k]] over a scoped array failed the same way inside dastest/tests/test_review_gate.das, while it passed on macOS - the failing shape differs by host.
Debug builds bypass the fused permutations (the 5-tier indexing note in CLAUDE.md), which fits. The gates in PR 3922 avoid the shape - iterate the outer array, single-index the element, clone what is kept - pending this.
options gen2
require strings
require daslib/strings_boost
def markdown_table_rows(text : string; section : string) : array<array<string>> {
var out : array<array<string>>
var in_section = false
var in_table = false
for (raw in split(text, "\n")) {
let line = strip(raw)
if (line |> starts_with("## ")) {
break if (in_table)
in_section = line == "## {section}"
continue
}
continue if (!in_section)
if (!(line |> starts_with("|"))) {
break if (in_table)
continue
}
in_table = true
continue if (line |> starts_with("|---") || line |> starts_with("| ---"))
// value iteration with a counter, never index access: indexing a scoped array from a
// range loop or a comprehension leaks its lock on some hosts, and the next scoped
// delete fails with "can't delete locked array" (interpreter issue, repro on file)
var inscope raw_cells <- split(line, "|")
let n_cells = length(raw_cells)
continue if (n_cells < 3)
var row : array<string>
row |> reserve(n_cells - 2)
var idx = 0
for (cell in raw_cells) { // nolint:STYLE027 - the comprehension form is the lock leak
if (idx > 0 && idx < n_cells - 1) {
row |> push(strip(replace(cell, "`", "")))
}
idx++
}
out |> emplace(row)
}
return <- out
}
def eq(a : string; b : string) : bool {
return a == b
}
[export]
def main {
var inscope rows <- markdown_table_rows("## INTERP\n\n| Benchmark | A (m1) | B (m2) |\n|---|---:|---:|\n| `fam_one` | 1.5 | - |\n| `fam_two` | 2.0 | 3.5 |\n", "INTERP")
let a = rows[1][0]
let b = rows[1][2]
print("local: {eq("fam_one", a)} {eq("-", b)}\n")
}
Running the file below with
bin/daslang(Release, macOS arm64, master as of 2026-09-01) throwsEXCEPTION: can't delete locked arrayatdaslib/builtin.das:1499at the end ofmain, where the scopedrowsis deleted. Two observations that make it look like a lock-count leak in a fusedatpermutation rather than a logic error:letlines with a direct calleq("fam_one", rows[1][0])passes; so does string interpolation"{rows[1][0]}". Reading into aletlocal fails.[for (k in range(1, n - 1)); cells[k]]over a scoped array failed the same way insidedastest/tests/test_review_gate.das, while it passed on macOS - the failing shape differs by host.Debug builds bypass the fused permutations (the 5-tier indexing note in CLAUDE.md), which fits. The gates in PR 3922 avoid the shape - iterate the outer array, single-index the element, clone what is kept - pending this.