Repository navigation
goto: a labelled block resolves only its own labels; a computed goto checks its target - #4475
Conversation
1802ca7 to
bbffec7
Compare
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🟡 Changes recommended
The change still has unresolved moderate issues in the computed-goto simulation around incorrect label collection across closures and stale label-root/known-label state across multiple initializer simulations, plus a minor test-style inconsistency noted in the new test.
3 open issues
What changed in this PR
This pull request corrects goto semantics across interpreter/JIT/AOT by tightening labelled-block label ownership (blocks only resolve their own labels and otherwise propagate the jump outward), making computed goto validate its target (throwing invalid label index when invalid), and adjusting AOT emission/block folding so goto does not produce invalid C++ control flow around declarations. It also adds regression tests and minor refactors/documentation updates.
Changes:
- Fix labelled block/loop jump handling so only “owned” labels are resolved locally; outward jumps and block-end labels behave correctly in the interpreter.
- Add computed-
gototarget validation using the function/expression’s declared label set, preserved across code relocation. - Update AOT emission and AST folding to keep/move declarations into C++-legal positions when bypassed by unconditional
goto. - Add regression tests for the reported cases; refactor repeated node-array copy logic.
Key review notes to address (from final comments):
- Moderate:
FunctionLabelCollectorshould not treat labels inside nested block literals/closures (ExprMakeBlock) as “known” labels for the outer function; also remove the unnecessaryconst_castonlabelRoot. - Moderate:
SimulateVisitor::simulateExpressionmust resetlabelRoot(and the cached known-label set) per entrypoint call to avoid validating later initializer subtrees against the first subtree’s labels. - Nits: Minor grammar fixes in docs and minor test-style/comment-style consistency tweaks in the new tests.
This pull request requires changes.
| File | Summary |
|---|---|
tests/language/test_goto_to_a_label_at_block_end.das |
Adds regression coverage for jumping to a label at block end and related outward-jump behavior. |
tests/language/test_goto_past_declaration.das |
Adds regression coverage for -aot correctness when goto bypasses local declarations/unreachable regions. |
tests/language/test_goto_out_of_a_loop_body.das |
Adds regression coverage ensuring loop bodies don’t incorrectly swallow outward gotos. |
tests/language/test_goto_out_of_a_labelled_block.das |
Adds regression coverage for outward gotos from labelled non-loop blocks; includes comment-style nit to adjust. |
tests/language/test_computed_goto_in_while.das |
Adds regression coverage for computed goto validity/invalid-target throwing; includes test-idiom nit to align assertion ordering. |
src/simulate/simulate.cpp |
Updates labelled-block evaluation to resolve only owned labels and propagate jumps outward; includes node-copy refactor usage. |
src/simulate/simulate_visit.cpp |
Ensures computed-goto metadata survives relocation via SimNode_Goto::copyNode. |
src/builtin/REVIEW.md |
Documentation wording updates; includes minor grammar nit. |
src/ast/REVIEW.md |
Documentation clarifications around cache/versioning and related rules. |
src/ast/ast_simulate.cpp |
Collects “known labels” and wires them into computed-goto simulation; has two moderate correctness issues to fix (closure traversal + labelRoot reset/caching). |
src/ast/ast_block_folding.cpp |
Adjusts folding to retain declarations after unconditional gotos so AOT has required declarations available. |
include/daScript/simulate/simulate.h |
Adds copyNodeArray helper and updates labelled-loop handling to use owned-label lookup. |
include/daScript/simulate/simulate_nodes.h |
Extends SimNode_Goto to validate computed targets against known labels and support relocation-safe copying. |
include/daScript/simulate/REVIEW.md |
Documentation wording updates; includes minor grammar nit. |
include/daScript/simulate/ARCHITECTURE.md |
Records the computed-goto validation as a sanctioned hot-path addition; includes minor grammar nit. |
include/daScript/ast/REVIEW.md |
Documentation clarifications around AST/JIT review guidance. |
include/daScript/ast/ast_serializer.h |
Bumps serialization/versioning to invalidate stale caches after AST/folding behavior changes. |
daslib/REVIEW.md |
Documentation clarifications for compile-time visitor/routing scope. |
daslib/aot_cpp.das |
Updates AOT variable/declaration handling for labelled blocks so generated C++ remains valid with goto. |
Review effort: Balanced
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🔵 Needs a closer look
A critical open issue remains in src/ast/ast_simulate.cpp where sv_collectKnownLabels() incorrectly records labels from nested closure blocks, potentially letting computed goto target an inner-closure label and bypass the intended runtime safety check.
3 open issues
Review effort: Balanced
bbffec7 to
c100efc
Compare
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🟡 Changes recommended
Approval is blocked because there are still unresolved nit findings affecting clarity and repo style conventions, including an unnecessary const_cast in ast_simulate.cpp and a misplaced top-level //! comment in a language test.
1 open issue
3 resolved since last review
Review effort: Balanced
c100efc to
69005d1
Compare
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🟡 Changes recommended
There are unresolved findings, most notably that the public-header copyNodeArray uses an unconstrained memcpy that can be misused with non-trivially-copyable types, along with a smaller const-correctness concern in sv_collectKnownLabels due to an unnecessary const_cast.
1 open issue
1 resolved since last review
Review effort: Balanced
A block literal in a global initializer sits outside any function, so the coverage pass reached its block instrumentation with a null (or the previous function's) `func` and threw. The pass now skips global variables the way it skips template structures. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
69005d1 to
bdd2577
Compare
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🔵 Needs a closer look
A remaining review finding notes an unnecessary const_cast on a const Expression pointer in src/ast/ast_simulate.cpp that should be removed to preserve const-correctness and avoid implying AST mutation.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
bdd2577 to
5abeea9
Compare
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🟡 Changes recommended
Approval is blocked because a moderate active finding reports potential signed-overflow undefined behavior in src/ast/ast_simulate.cpp when computing maxLabel + 1 in int32_t, requiring a type-safe cast/clamp fix.
1 open issue
Review effort: Balanced
A labelled block or loop body padded its label table with -1 for every number it does not hold and jumped there anyway: SIGSEGV from a loop body, 'jump to label N failed' from a plain block, and a label standing last in a block indexed one past its body and failed the same way. The block now takes a jump only to a label it holds, finishes when that label ends the block, and otherwise leaves the flag set for the enclosing block. Fixes #4451 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The innermost labelled block used to throw for a label index outside its table; now that a block hands an unowned jump outward, the goto node itself carries the set of labels the function declares and throws 'invalid label index' for any other value, so a bad computed target never falls silently out of the function. The test also pins the computed-goto-into-a-while shape, which answers right on this master. Fixes #4466 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
C++ refuses a goto that jumps past an initialized declaration. The emitter now hoists every local of a block holding labels through its moved-variable path, the way it already does for a block with a finally section, so the declaration precedes every goto and label of the block and the site becomes an assignment. Block folding keeps a declaration inside an unreachable region after an unconditional goto, so the uses after the label still see it. Fixes #4450 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The simulate checklist defines the hot path once in its opening, gives the ABI sweep its own rule with a layout change defined (data members, virtuals, a base) over every header it ships, lists the ledger entry's content as the work itself and splits the PR-measurement duty off it. The daslib checklist names the AOT emitter's visitors in its macro routing and leads its pair-record rule with the trigger. The src/builtin checklist takes the annotation-name duty: a bind's registered name, the name string a Managed*Annotation is constructed with included, bumps the JIT codegen version. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5abeea9 to
fd30030
Compare
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🟡 Changes recommended
The change still has open review findings—specifically a few identified grammar/wording issues in the ARCHITECTURE.md and REVIEW.md documents—that need to be addressed before approval.
3 open issues
1 resolved since last review
Review effort: Balanced


Behavior change: a computed
gotowhose target no label of the function declares now throwsinvalid label indexon every tier; agototo a label in an enclosing block, or to a label that ends a block, now reaches it in the interpreter.Why. Five of lookibed's goto reports. A labelled block or loop body resolved every jump from its own table, padded with
-1for the numbers it does not hold: SIGSEGV from a loop body (#4451),jump to label N failedfrom a plain block (#4453) and for a label standing last in a block (#4454). A computed goto's bad target fell silently out of the function.-aotwrote a C++gotopast an initialized local, which clang refuses (#4450).What changes.
ownedLabelread replaces the open-coded table checks in the block evals and the loop macro.relocateCode.copyNodebodies share onecopyNodeArrayhelper.Observable behavior.
goto label 1from a loop body to an outer label numbered below the body's labels: SIGSEGV -> runs.goto label 1from a labelled non-loop block to an outer label:jump to label 1 failed-> runs.jump to label 3 failed-> the block finishes.goto exprwith a target no label declares: garbage return ->invalid label index.goto exprnaming a label only a block literal inside the function declares: falls out of the function ->invalid label index; a block literal's owngoto exprstill lands on its own label.-aotof a goto pastvar g = n * 2:cannot jump from this goto statement to its label-> compiles.Where to look.
SimNode_BlockWithLabels::evalandDAS_PROCESS_LOOP_FLAGS_LABELEDininclude/daScript/simulate/simulate.h;SimNode_Gotoinsimulate_nodes.h;preVisitExprLetVariableindaslib/aot_cpp.das.Fixes #4451, fixes #4453, fixes #4454, fixes #4450, fixes #4466.
Validation, claims, ledger
Validation
preflight --only tests-aot, thetest_aotbuild and sweep overtests/): passed on the final code, 770 s; the only commit after it rewraps one comment. Every other gate and lane (lint, review-md, md-ascii, hash-refs, cpp-syntax, ast-verify, compile-sweep, tests-cpp, tests-interp, tests-jit, utils-tests) passed on the same tree.tests/language).ARCHITECTURE.md#sanctioned-hot-path-additions. Measured on a four-way computed-goto dispatch loop (goto (i & 3) + 100, 20M iterations, best of 5 in-process rounds, 5 alternating process runs per binary, M5 Max, interpreter): 5.32 ns -> 5.58 ns per dispatch, +4.8%; run-to-run cv 0.4% and 1.5%.SimNode_Gotogains two data members and acopyNodeoverride: a GitHub code search finds the type only in copies of daScript's own sources (DagorEngine's vendored tree and forks), nothing else constructs it.test_aot_subsetpassed withdaslib/fio.dasin its closure.gotocan target a label inside a nested block, which the front end refuses (can't find label, error 30819).utils/find-dupedaspkg install and the dasSQLITE/dasCrypto modules enabled; both are environment, not diff.Claims - stated, not tested
test_goto_out_of_a_labelled_block.dasruns underoptions debugger, the plain nodes take the same shapes intest_goto_to_a_label_at_block_end.das.Not done
gotointo awhilebody returns a wrong value when the target comes from a variable: the interpreter and-jitboth answer garbage,-no-optimizationanswers right #4466 (computed goto into a while body) no longer reproduces on master; the test pins the shape and the invalid-target check.gotoinside a block literal leaves the block instead of reaching its own label, on master independently of this PR; filed as A static goto inside a block literal leaves the block instead of reaching its label #4476.src/REVIEW.md's narrow-path file rules repeat what its gate checks, the rootREVIEW.mddescribes its scanner's routes in prose,include/daScript/ast/REVIEW.md's layout rule fuses two duties.#nightly - the diff touches
src/andinclude/, and the full AOT sweep is a nightly lane.