Skip to content

check(channel_liveness): a function body owns its capability names (fixes #4462) - #4463

Open
jorge123255 wants to merge 1 commit into
limitlessprojects:mainfrom
jorge123255:fix-4462-capability-fn-scope
Open

jorge123255 wants to merge 1 commit into
limitlessprojects:mainfrom
jorge123255:fix-4462-capability-fn-scope

Conversation

@jorge123255

Copy link
Copy Markdown

Fixes #4462.

The bug

Capabilities were identified by their bare name across the whole file: cap_to_pipe/pipes_all are file-wide, pop_scope promotes a body's moved_caps into the file-level scope, and is_moved scans the stack. Nothing reset at a function boundary, so a function inherited the moves (and closed/drained names, and pipe indices) of every function walked before it.

That rejected a valid program whenever two functions both call their capability tx — the name the checker's own fix-it prints:

$ hyb check repro.hy
error[HYB-E3001]: use of moved linear capability 'tx'   --> :10:5
error[HYB-E3001]: use of moved linear capability 'tx'   --> :11:5

Declaration order alone decided the verdict: helper-above-main passed, main-above-helper failed, and renaming the parameter fixed it — both orders are legal programs.

It is not only noisy: the move also satisfies the terminal invariant, so a later function reusing a moved name loses its HYB-E4004/E4005 check.

The fix

check_fn saves cap_to_pipe, pipes_all and the file-level moved_caps, clears them for the body, and restores them after. A body starts from a clean world; closures stay inside one body, so the promotion they rely on still works (a test pins that the closure case keeps rejecting).

Six tests, three fail on b1627e8 and three are controls that already passed:

  • rejected order now accepted; renamed-parameter control;
  • closure move still poisons the enclosing body;
  • unclosed second function still rejected E4004 (guard against over-relaxing);
  • closed second function not flagged;
  • constant-shape case.

cargo test --workspace green, check_corpus_census green (139, unchanged).

No corpus fixture: hy.pipe still has no emit path (hyb build fails with package hy is not in std on any capability program), so an ACCEPT entry cannot build yet — worth its own issue if this surface is meant to ship.

Co-authored-by: OpenBot openbot@openbot.dev

…ixes limitlessprojects#4462)

`ChannelLivenessChecker` keys capabilities by the bare name (`cap_to_pipe`,
`pipes_all`) and `pop_scope` promotes a body's `moved_caps` into the
file-level scope, while `is_moved` scans the whole stack. So the file-level
state outlived the function that produced it: a `move tx` in one function made
every LATER function's own `tx` look moved.

The trigger is declaration order. Helper-before-main is accepted; main-before-
helper is rejected with two `HYB-E3001` — and `tx` is the name the checker's
own fix-it text prints, so the rejected spelling is the natural one:

    fn main() {
        tx, rx := hy.pipe[int]()
        task push_three(move tx)
        for v in rx { fmt.println("{v}") }
    }

    fn push_three(tx hy.send_cap[int]) {   // same name
        tx.send(i); tx.close()             // rejected: "use of moved linear
    }                                      //  capability 'tx'"

Renaming the parameter made the same program pass, which is what pointed at
identity-by-name. Both orders are legal programs; only the walk order differed.

`check_fn` now saves and restores the three pieces of capability state
(`cap_to_pipe`, `pipes_all`, the file-level `moved_caps`) around the body, so a
body starts from a clean world. Closures stay inside one function and their
promotion still works — a move performed in a closure body still reaches the
enclosing function.

Six tests: the rejected order now accepts, the renamed control, the closure
promotion that must survive, an unclosed SECOND function still rejected
(E4004), a closed second function not flagged, and the constant-shape case.
Three of them fail on b1627e8 (the acceptance, the closed-second, the
constant), the three controls already passed.

No corpus fixture: `hy.pipe` has no emit support yet, so a capability program
does not reach `go build` and cannot be an ACCEPT corpus entry.

Co-authored-by: OpenBot <openbot@openbot.dev>
@github-actions github-actions Bot added the bot:land-candidate Shepherd: fresh, gate-clean, verifiable (set from classify()) label Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:land-candidate Shepherd: fresh, gate-clean, verifiable (set from classify())

Projects

None yet

1 participant