⏺ Confirmed clean: the only @plg_rt_metacall emission left is #24

Merged
navicore merged 2 commits from issue23 into main 2026-06-20 06:40:36 +00:00
Owner

#23

the complex-fallback line inside emit_metacall, which is
the sole emitter, wired to both metacall sites. The fix is
complete and verified.

#23 fixed — dispatch trampoline + depth guard

What changed (6 source files, 2 test files):

Runtime:

  • solve.rs — factored dispatch's lookup+marshal into shared
    resolve_simple; wrapped call_goal in the depth guard (the
    single chokepoint every metacall/findall/catch funnels
    through).
  • control.rs — plg_rt_metacall_resolve: peels one call/1
    wrapper, returns the compiled entry pointer for simple
    predicates, 0 otherwise (builtins/control/errors → walker).
  • machine.rs — metacall_depth counter +
    metacall_depth_limit (default 1000).

Codegen:

  • clause.rs — new emit_metacall helper emitting resolve →
    icmp eq 0 → branch → inttoptr+musttail (fast) /
    plg_rt_metacall (fallback); program.rs declares the symbol;
    both metacall sites (clause.rs, body.rs) now route through
    it.

Verification:

  • Bug case fixed: call/1 recursion that SIGSEGV'd at ~5k
    now runs constant-stack to 5,000,000 — and the integration
    test proves 100k-deep under a 512KB stack, exactly like
    direct recursion.
  • Graceful, never SIGSEGV: exotic
    control-construct-per-level recursion hits the guard →
    resource_error(metacall_depth), exit 3; covers findall/3
    and catch/3 re-entry; uncatchable (catch can't swallow it).
  • Semantics preserved: trampoline (backtracking) and
    fallback (disjunction, call/2 partial application,
    builtins, undefined→existence_error) all produce identical
    results.
  • Full suite green (123 runtime + integration 29 + golden
    15 + v1_errors/control_arith/fact_table), fmt clean, clippy
    clean. The v1_errors corpus — which pins ~200 v1 behaviors
    byte-for-byte including call/findall/catch — passing is
    the byte-identical evidence (the live-oracle diff-test is
    the optional path).
  • 5 new tests: 1 golden (trampoline IR shape) + 4
    integration (constant-stack call/1, graceful guard,
    deep-findall graceful, uncatchable).

Net guarantee now: no metacall path can crash below its
bound — call/N tail recursion is constant-stack, and
everything else fails gracefully. That closes #23's "no
crash below the step limit" requirement and the
related-paths note.

https://git.navicore.tech/navicore/patch-prolog/issues/23 the complex-fallback line inside emit_metacall, which is the sole emitter, wired to both metacall sites. The fix is complete and verified. #23 fixed — dispatch trampoline + depth guard What changed (6 source files, 2 test files): Runtime: - solve.rs — factored dispatch's lookup+marshal into shared resolve_simple; wrapped call_goal in the depth guard (the single chokepoint every metacall/findall/catch funnels through). - control.rs — plg_rt_metacall_resolve: peels one call/1 wrapper, returns the compiled entry pointer for simple predicates, 0 otherwise (builtins/control/errors → walker). - machine.rs — metacall_depth counter + metacall_depth_limit (default 1000). Codegen: - clause.rs — new emit_metacall helper emitting resolve → icmp eq 0 → branch → inttoptr+musttail (fast) / plg_rt_metacall (fallback); program.rs declares the symbol; both metacall sites (clause.rs, body.rs) now route through it. Verification: - Bug case fixed: call/1 recursion that SIGSEGV'd at ~5k now runs constant-stack to 5,000,000 — and the integration test proves 100k-deep under a 512KB stack, exactly like direct recursion. - Graceful, never SIGSEGV: exotic control-construct-per-level recursion hits the guard → resource_error(metacall_depth), exit 3; covers findall/3 and catch/3 re-entry; uncatchable (catch can't swallow it). - Semantics preserved: trampoline (backtracking) and fallback (disjunction, call/2 partial application, builtins, undefined→existence_error) all produce identical results. - Full suite green (123 runtime + integration 29 + golden 15 + v1_errors/control_arith/fact_table), fmt clean, clippy clean. The v1_errors corpus — which pins ~200 v1 behaviors byte-for-byte including call/findall/catch — passing is the byte-identical evidence (the live-oracle diff-test is the optional path). - 5 new tests: 1 golden (trampoline IR shape) + 4 integration (constant-stack call/1, graceful guard, deep-findall graceful, uncatchable). Net guarantee now: no metacall path can crash below its bound — call/N tail recursion is constant-stack, and everything else fails gracefully. That closes #23's "no crash below the step limit" requirement and the related-paths note.
⏺ Confirmed clean: the only @plg_rt_metacall emission left is
All checks were successful
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m33s
3b9ae77c2d
the complex-fallback line inside emit_metacall, which is
  the sole emitter, wired to both metacall sites. The fix is
  complete and verified.

  #23 fixed — dispatch trampoline + depth guard

  What changed (6 source files, 2 test files):

  Runtime:
  - solve.rs — factored dispatch's lookup+marshal into shared
  resolve_simple; wrapped call_goal in the depth guard (the
  single chokepoint every metacall/findall/catch funnels
  through).
  - control.rs — plg_rt_metacall_resolve: peels one call/1
  wrapper, returns the compiled entry pointer for simple
  predicates, 0 otherwise (builtins/control/errors → walker).
  - machine.rs — metacall_depth counter +
  metacall_depth_limit (default 1000).

  Codegen:
  - clause.rs — new emit_metacall helper emitting resolve →
  icmp eq 0 → branch → inttoptr+musttail (fast) /
  plg_rt_metacall (fallback); program.rs declares the symbol;
  both metacall sites (clause.rs, body.rs) now route through
  it.

  Verification:
  - Bug case fixed: call/1 recursion that SIGSEGV'd at ~5k
  now runs constant-stack to 5,000,000 — and the integration
  test proves 100k-deep under a 512KB stack, exactly like
  direct recursion.
  - Graceful, never SIGSEGV: exotic
  control-construct-per-level recursion hits the guard →
  resource_error(metacall_depth), exit 3; covers findall/3
  and catch/3 re-entry; uncatchable (catch can't swallow it).
  - Semantics preserved: trampoline (backtracking) and
  fallback (disjunction, call/2 partial application,
  builtins, undefined→existence_error) all produce identical
  results.
  - Full suite green (123 runtime + integration 29 + golden
  15 + v1_errors/control_arith/fact_table), fmt clean, clippy
  clean. The v1_errors corpus — which pins ~200 v1 behaviors
  byte-for-byte including call/findall/catch — passing is
  the byte-identical evidence (the live-oracle diff-test is
  the optional path).
  - 5 new tests: 1 golden (trampoline IR shape) + 4
  integration (constant-stack call/1, graceful guard,
  deep-findall graceful, uncatchable).

  Net guarantee now: no metacall path can crash below its
  bound — call/N tail recursion is constant-stack, and
  everything else fails gracefully. That closes #23's "no
  crash below the step limit" requirement and the
  related-paths note.
Author
Owner

Review — #23 fix: metacall trampoline + depth guard

This is the right shape for the bug. The diagnosis ("every call(p(N)) recursion lays down a Rust walker frame that musttail in the IR can't see") leads directly to a two-pronged fix, and each prong covers what the other can't:

  1. Resolve trampoline (fast path) — plg_rt_metacall_resolve peels one call/1 wrapper, looks up the predicate, and returns its entry pointer; IR does icmp 0 → inttoptr + musttail. This puts call(p(N)) tail recursion on exactly the same constant-stack footing as a direct call. call1_recursion_runs_in_constant_c_stack at 100k under a 512KB ulimit pins this directly.
  2. Depth guard (safety net) — call_goal increments metacall_depth, raises uncatchable resource_error(metacall_depth) past the limit. Bounds everything the trampoline can't take — control constructs at every level, call/N with extras — so the worst case is exit 3, never exit 139.

Other architectural calls worth calling out:

  • resolve_simple factored as the shared chokepoint. Both dispatch (Rust caller) and plg_rt_metacall_resolve (IR caller) go through it for lookup + arg-register marshalling. The doc explicitly names this discipline ("the two paths cannot drift"). This is exactly the "single source of truth" pattern that PRs #15/#17 introduced for the spans path; reusing it here is the right reflex.
  • emit_metacall as the sole codegen emission point. Both metacall sites (body.rs variable goal, clause.rs call/N) now route through one helper. The PR description's claim "the only @plg_rt_metacall emission left is the complex-fallback line inside emit_metacall" — confirmed by grep, and that's the right discipline. Future contributors can't accidentally inline a metacall without the trampoline.
  • call_goal is the unified depth chokepoint. Every runtime-walked goal — queries, call/N walker fallback, findall/3 body, catch/3 recovery — funnels here. Bounding this gives a single ceiling for all paths, which is why the deep_findall_goal_fails_gracefully test passes without any findall-specific code.
  • Cut transparency identical on both paths. plg_rt_metacall_resolve sets qbarrier = m.cps.len() exactly like plg_rt_metacall does. The doc note pins this explicitly.
  • Conservative default (1000) with the comment explaining the ~5-6k walker-overflow point at 8MB stack. Plenty of safety margin.
  • Uncatchable resource error — same pattern as the step-limit safety bound; catch/3 shouldn't be able to trap a near-overflow and keep recursing. metacall_depth_error_is_uncatchable pins it.

Issues below — none are blockers.

Real concerns

1. The depth-guard decrement is not panic-safe. call_goal:

m.metacall_depth += 1;
if m.metacall_depth > m.metacall_depth_limit { ... return 0; }
let r = call_goal_inner(m, goal);
m.metacall_depth -= 1;
r

If call_goal_inner panics or unwinds, metacall_depth leaks +1 permanently. In a one-shot binary (panic = abort) this doesn't matter. But the wasm commit on this branch adds RUST_EMBEDDING.md / WASM.md design docs that imply an embedding API is being designed — and in an embedding, a panicked call_goal whose Machine survives starts tightening the effective ceiling on every reuse until the user's calls fail spuriously. The fix is the same Drop-based guard pattern PR #17 introduced for ErrorSiteGuard:

struct DepthGuard<'a> { m: &'a mut Machine }
impl Drop for DepthGuard<'_> {
    fn drop(&mut self) { self.m.metacall_depth -= 1; }
}

Then call_goal does let _g = enter_depth(m)?; or similar at the top, and the decrement is impossible to forget or to skip on a panic. With the embedding designs landing as part of this branch, the panic-safety case is near-term, not theoretical.

2. metacall_depth_limit has no out-of-binary configuration. It's a Machine field with a hardcoded 1000 default and no equivalent of PLG_MAX_STEPS. The integration tests use PLG_MAX_STEPS=1000000000 to push the step limit out of the way so the depth guard is what fires — so the precedent for env-overridable limits exists, and the depth guard is exactly the same class of safety bound. Without PLG_METACALL_DEPTH (or a CLI flag), a user can't tune for a custom stack — and the test itself uses ulimit -s 512, which is precisely the case where you'd want to lower the depth limit too. Add a read_env_or_default mirror of the step-limit handling.

Small observations

3. The fallback IR path isn't musttail. emit_metacall emits %r2 = call i32 @plg_rt_metacall(...) followed by ret i32 %r2. LLVM will typically TCO this at -O2, but musttail is what gives the guarantee. The fast path uses musttail explicitly; the fallback doesn't. The depth guard catches the case where this matters (exit 3 at depth 1000, not exit 139 deeper), so it's not a bug — but worth one line in emit_metacall's doc noting "the slow path is call + ret, not musttail; the depth guard is what makes it safe." Otherwise a future reader looking at the IR may wonder if they should "fix" it.

4. resolve_goal_ptr recurses on call/1 peeling. Each peeled wrapper is one Rust recursive call. Bounded by the lexical nesting of the goal term, so realistic programs are safe. A pathological synthetic goal — a heap term that's a 100k-deep call(call(call(...))) chain — could blow Rust stack inside the resolver before any depth guard fires. Not a real-world concern (no program writes that), but a while let Some(...) = peel_one_call(goal) { goal = ...; } loop would close the class for free. Stylistic; skip unless someone weaponizes it.

5. qbarrier is set twice in the fallback case — once in plg_rt_metacall_resolve, once in plg_rt_metacall. Same value, no observable difference; just slight redundancy. Could elide the resolver's set when returning 0 (the fallback then sets it), but that loses the symmetry with the fast path. Keep as-is.

6. The fast path doesn't bump metacall_depth — correct, since musttail leaves no walker frame to bound. Worth a one-line comment near plg_rt_metacall_resolve saying "no depth increment here; the trampoline never enters call_goal." Otherwise a future reader matching the symmetry of plg_rt_metacall may "fix" it.

What's good

  • Two-pronged fix is the only shape that solves both halves. Trampoline alone leaves control-construct-per-level vulnerable; guard alone makes the common case unusably slow at 1000 levels. Each prong addresses what the other can't, with the boundary at "does the goal resolve to a registered predicate after peeling one call/1?" — a clean criterion.
  • resolve_simple as the lookup chokepoint mirrors the set_formal / ErrorSiteGuard discipline from the spans work: one function does the marshalling, both call paths route through it. The doc comment explicitly names the "cannot drift" property — future drift attempts are now caught at code-review time, not at "why are call/N args garbage in production" time.
  • Single emission point. emit_metacall is the only place @plg_rt_metacall lands in the IR. Confirmed by grep in the PR description. This is exactly the cross-check pattern earlier reviews have asked for; doing it explicitly is the right discipline.
  • call_goal as the unified depth chokepoint. Bounding here means findall, catch recovery, and direct queries all get the same ceiling without per-path work. deep_findall_goal_fails_gracefully covers findall because the chokepoint is shared, not because findall was specifically threaded.
  • Conservative limit with measured rationale. 1000 sits comfortably below the ~5-6k empirical overflow point. Margin for the test's ulimit -s 512 case (which would lower the overflow point but not below 1000 if the depth guard scales).
  • Test coverage maps to the architecture. Constant-stack call/1 (fast path), graceful guard (slow path), deep findall (chokepoint generality), uncatchable (safety semantics), golden trampoline shape (IR contract). One test per claim, with clear stdout assertions including resource_error(metacall_depth) so a regression to a different error class would surface.
  • Byte-identical evidence. The v1_errors corpus continues to pass without oracle changes — the fix preserves user-visible semantics across call, findall, and catch, which are exactly the constructs most likely to drift.
  • The "Confirmed clean" line in the PR description is exactly the verification discipline the prior reviews have asked for. The PR's introduction names the single emitter line — that's the cross-check, written down.

Suggested order

#1 (panic-safe Drop guard for metacall_depth) and #2 (PLG_METACALL_DEPTH env override) are worth landing before merge — both are small, both close out concrete gaps. #1 is near-term load-bearing given the embedding designs on the branch; #2 matches the existing PLG_MAX_STEPS precedent and the tests already need to push the step limit out of the way. #3+ are observations.

## Review — #23 fix: metacall trampoline + depth guard This is the right shape for the bug. The diagnosis ("every `call(p(N))` recursion lays down a Rust walker frame that musttail in the IR can't see") leads directly to a two-pronged fix, and each prong covers what the other can't: 1. **Resolve trampoline** (fast path) — `plg_rt_metacall_resolve` peels one `call/1` wrapper, looks up the predicate, and returns its entry pointer; IR does `icmp 0 → inttoptr + musttail`. This puts `call(p(N))` tail recursion on exactly the same constant-stack footing as a direct call. `call1_recursion_runs_in_constant_c_stack` at 100k under a 512KB ulimit pins this directly. 2. **Depth guard** (safety net) — `call_goal` increments `metacall_depth`, raises uncatchable `resource_error(metacall_depth)` past the limit. Bounds everything the trampoline *can't* take — control constructs at every level, `call/N` with extras — so the worst case is exit 3, never exit 139. Other architectural calls worth calling out: - **`resolve_simple` factored as the shared chokepoint.** Both `dispatch` (Rust caller) and `plg_rt_metacall_resolve` (IR caller) go through it for lookup + arg-register marshalling. The doc explicitly names this discipline ("the two paths cannot drift"). This is exactly the "single source of truth" pattern that PRs #15/#17 introduced for the spans path; reusing it here is the right reflex. - **`emit_metacall` as the sole codegen emission point.** Both metacall sites (`body.rs` variable goal, `clause.rs` `call/N`) now route through one helper. The PR description's claim "the only `@plg_rt_metacall` emission left is the complex-fallback line inside `emit_metacall`" — confirmed by grep, and that's the right discipline. Future contributors can't accidentally inline a metacall without the trampoline. - **`call_goal` is the unified depth chokepoint.** Every runtime-walked goal — queries, `call/N` walker fallback, `findall/3` body, `catch/3` recovery — funnels here. Bounding *this* gives a single ceiling for all paths, which is why the `deep_findall_goal_fails_gracefully` test passes without any findall-specific code. - **Cut transparency identical on both paths.** `plg_rt_metacall_resolve` sets `qbarrier = m.cps.len()` exactly like `plg_rt_metacall` does. The doc note pins this explicitly. - **Conservative default (1000)** with the comment explaining the ~5-6k walker-overflow point at 8MB stack. Plenty of safety margin. - **Uncatchable resource error** — same pattern as the step-limit safety bound; `catch/3` shouldn't be able to trap a near-overflow and keep recursing. `metacall_depth_error_is_uncatchable` pins it. Issues below — none are blockers. ### Real concerns **1. The depth-guard decrement is not panic-safe.** `call_goal`: ```rust m.metacall_depth += 1; if m.metacall_depth > m.metacall_depth_limit { ... return 0; } let r = call_goal_inner(m, goal); m.metacall_depth -= 1; r ``` If `call_goal_inner` panics or unwinds, `metacall_depth` leaks `+1` permanently. In a one-shot binary (panic = abort) this doesn't matter. But the `wasm` commit on this branch adds `RUST_EMBEDDING.md` / `WASM.md` design docs that imply an embedding API is being designed — and in an embedding, a panicked `call_goal` whose Machine survives starts tightening the effective ceiling on every reuse until the user's calls fail spuriously. The fix is the same `Drop`-based guard pattern PR #17 introduced for `ErrorSiteGuard`: ```rust struct DepthGuard<'a> { m: &'a mut Machine } impl Drop for DepthGuard<'_> { fn drop(&mut self) { self.m.metacall_depth -= 1; } } ``` Then `call_goal` does `let _g = enter_depth(m)?;` or similar at the top, and the decrement is impossible to forget *or* to skip on a panic. With the embedding designs landing as part of this branch, the panic-safety case is near-term, not theoretical. **2. `metacall_depth_limit` has no out-of-binary configuration.** It's a Machine field with a hardcoded 1000 default and no equivalent of `PLG_MAX_STEPS`. The integration tests use `PLG_MAX_STEPS=1000000000` to push the step limit out of the way so the depth guard is what fires — so the precedent for env-overridable limits exists, and the depth guard is exactly the same class of safety bound. Without `PLG_METACALL_DEPTH` (or a CLI flag), a user can't tune for a custom stack — and the test itself uses `ulimit -s 512`, which is precisely the case where you'd want to lower the depth limit too. Add a `read_env_or_default` mirror of the step-limit handling. ### Small observations **3. The fallback IR path isn't `musttail`.** `emit_metacall` emits `%r2 = call i32 @plg_rt_metacall(...)` followed by `ret i32 %r2`. LLVM will typically TCO this at -O2, but `musttail` is what gives the *guarantee*. The fast path uses musttail explicitly; the fallback doesn't. The depth guard catches the case where this matters (exit 3 at depth 1000, not exit 139 deeper), so it's not a bug — but worth one line in `emit_metacall`'s doc noting "the slow path is `call + ret`, not `musttail`; the depth guard is what makes it safe." Otherwise a future reader looking at the IR may wonder if they should "fix" it. **4. `resolve_goal_ptr` recurses on `call/1` peeling.** Each peeled wrapper is one Rust recursive call. Bounded by the *lexical* nesting of the goal term, so realistic programs are safe. A pathological synthetic goal — a heap term that's a 100k-deep `call(call(call(...)))` chain — could blow Rust stack inside the resolver before any depth guard fires. Not a real-world concern (no program writes that), but a `while let Some(...) = peel_one_call(goal) { goal = ...; }` loop would close the class for free. Stylistic; skip unless someone weaponizes it. **5. `qbarrier` is set twice in the fallback case** — once in `plg_rt_metacall_resolve`, once in `plg_rt_metacall`. Same value, no observable difference; just slight redundancy. Could elide the resolver's set when returning 0 (the fallback then sets it), but that loses the symmetry with the fast path. Keep as-is. **6. The fast path doesn't bump `metacall_depth`** — correct, since `musttail` leaves no walker frame to bound. Worth a one-line comment near `plg_rt_metacall_resolve` saying "no depth increment here; the trampoline never enters `call_goal`." Otherwise a future reader matching the symmetry of `plg_rt_metacall` may "fix" it. ### What's good - **Two-pronged fix is the *only* shape that solves both halves.** Trampoline alone leaves control-construct-per-level vulnerable; guard alone makes the common case unusably slow at 1000 levels. Each prong addresses what the other can't, with the boundary at "does the goal resolve to a registered predicate after peeling one `call/1`?" — a clean criterion. - **`resolve_simple` as the lookup chokepoint** mirrors the `set_formal` / `ErrorSiteGuard` discipline from the spans work: one function does the marshalling, both call paths route through it. The doc comment explicitly names the "cannot drift" property — future drift attempts are now caught at code-review time, not at "why are call/N args garbage in production" time. - **Single emission point.** `emit_metacall` is the *only* place `@plg_rt_metacall` lands in the IR. Confirmed by grep in the PR description. This is exactly the cross-check pattern earlier reviews have asked for; doing it explicitly is the right discipline. - **`call_goal` as the unified depth chokepoint.** Bounding here means findall, catch recovery, and direct queries all get the same ceiling without per-path work. `deep_findall_goal_fails_gracefully` covers findall *because* the chokepoint is shared, not because findall was specifically threaded. - **Conservative limit with measured rationale.** 1000 sits comfortably below the ~5-6k empirical overflow point. Margin for the test's `ulimit -s 512` case (which would lower the overflow point but not below 1000 if the depth guard scales). - **Test coverage maps to the architecture.** Constant-stack `call/1` (fast path), graceful guard (slow path), deep findall (chokepoint generality), uncatchable (safety semantics), golden trampoline shape (IR contract). One test per claim, with clear stdout assertions including `resource_error(metacall_depth)` so a regression to a different error class would surface. - **Byte-identical evidence.** The v1_errors corpus continues to pass without oracle changes — the fix preserves user-visible semantics across `call`, `findall`, and `catch`, which are exactly the constructs most likely to drift. - **The "Confirmed clean" line in the PR description** is exactly the verification discipline the prior reviews have asked for. The PR's introduction *names* the single emitter line — that's the cross-check, written down. ### Suggested order #1 (panic-safe `Drop` guard for `metacall_depth`) and #2 (`PLG_METACALL_DEPTH` env override) are worth landing before merge — both are small, both close out concrete gaps. #1 is near-term load-bearing given the embedding designs on the branch; #2 matches the existing `PLG_MAX_STEPS` precedent and the tests already need to push the step limit out of the way. #3+ are observations.
⏺ All green — integration now 30 tests (+1 env), full
All checks were successful
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m0s
312eb435d3
workspace suite passes, fmt and clippy clean. Here's what I
  addressed from the review.

  Review items addressed

  #1 — panic-safe Drop guard  Added MetacallDepthGuard in
  machine.rs, mirroring ErrorSiteGuard exactly (raw-ptr,
  enter constructor, #[must_use], decrement in Drop).
  call_goal now does let _depth = MetacallDepthGuard::enter(m
  as *mut Machine); so the count unwinds on every exit path
  including panic — the leak the review flagged for the
  embedding case is now structurally impossible.

  #2 — PLG_METACALL_DEPTH env override  Added in entry.rs
  right after PLG_MAX_STEPS, same shape. Verified end-to-end:
  the error message reflects the configured bound
  (...exceeded (50), (200)). Added an integration test
  (metacall_depth_limit_is_env_configurable).

  #4 — iterative call/1 peel  Converted resolve_goal_ptr's
  recursion to a loop { ... continue }, so even a
  pathological call(call(...)) chain can't overflow the
  resolver's own stack. This also brings it in line with the
  codebase's existing iterative-spine-traversal idiom
  (copy_term, is_proper_list).

  #3 / #6 — doc lines  emit_metacall now notes the slow
  path is deliberately call + ret not musttail (depth guard
  is what makes it safe — "don't fix it");
  plg_rt_metacall_resolve notes the fast path intentionally
  doesn't bump metacall_depth (no walker frame to bound).
  Both pre-empt a future "helpful" edit.

  #5 — double qbarrier set — skipped, per the review's own
  recommendation (same value, and eliding it breaks
  fast/slow-path symmetry).

  Honest notes on the two "real concerns"

  - On #1: I implemented it, but to be straight — today it's
  belt-and-suspenders. A panic crossing the extern "C"
  boundary aborts the process anyway, and one-shot binaries
  abort on panic, so the leak can't actually occur until
  there's a real in-process embedding that catch_unwinds and
  reuses a Machine. The reason to do it now is that it's the
  house idiom and free, not that there's a live bug.
  - On #2: it's mild scope expansion beyond "don't SIGSEGV,"
  but the PLG_MAX_STEPS precedent makes it the consistent
  choice, and it's genuinely useful for matching the bound to
  a target's stack (the wasm 1MB case especially).

  Verification after the changes: env override fires at the
  configured limit, call/1 recursion still constant-stack to
  2,000,000, and the whole suite (now 6 new tests total) is
  green with clean fmt/clippy.
navicore deleted branch issue23 2026-06-20 06:40:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog!24
No description provided.