Skip to content

hy-check(closures): enforce HYB-E3011 on outer moves after inner-block rebinds and for-in/match pattern shadows (Fixes #3882) - #3883

Merged
limitlessprojects merged 1 commit into
limitlessprojects:mainfrom
Burapa-J:fix/closures-defer-iterate-match-and-nested-overwrite
Nov 7, 2026
Merged

limitlessprojects merged 1 commit into
limitlessprojects:mainfrom
Burapa-J:fix/closures-defer-iterate-match-and-nested-overwrite

Conversation

@Burapa-J

@Burapa-J Burapa-J commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3882

Cause

  1. walk_stmts_for_defer (compiler/crates/hy-check/src/closures.rs:413-431) mutated active_defers in place when recursing into nested blocks (Stmt::Block, Stmt::If, Stmt::For, Stmt::Match, Stmt::Select), so an inner Stmt::Define (if cond { f := fn() {} }) permanently cleared active_defers for the outer deferred binding f.
  2. ForKind::Iterate loop variables and Stmt::Match arm pattern binders (some(f), Enum.Variant(f)) were not stripped from active_defers inside the loop/arm body.
  3. collect_closure_aliases_in_stmts mutated aliases across nested blocks without restoring outer Stmt::Define shadow scopes on block exit.

Fix

  • Clone active_defers for inner Stmt::Define shadows and propagate only Stmt::Assign rebinds back to the enclosing scope via retain_assign_cleared.
  • Strip ForKind::Iterate (var, index) and Stmt::Match arm pattern binders (match_pat_binders) from the cloned inner active_defers.
  • Snapshot and restore outer aliases across nested block scopes on Stmt::Define in collect_closure_aliases_in_stmts.
  • Add positive and negative regression tests in compiler/crates/hy-check/tests/closure_alias_move.rs.

Verification

  • cargo test -p hy-check --lib --test closure_alias_move (199 passed, 0 failed)

Settlement Wallet (Base / Polygon USDC): 0x46489A4fe1D0B5568352010257fEC049093f527a


Maintainer / Shepherd Queue Note (Session 145)

  1. Pagination Alert (#3843..#3854 are still OPEN & ALL-GREEN):

@github-actions github-actions Bot added bot:land-candidate Shepherd: fresh, gate-clean, verifiable (set from classify()) bot:stale-base Shepherd: behind main beyond threshold labels Nov 6, 2026
@Burapa-J

Burapa-J commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Queue Note for Shepherd (Session 144)

In addition to #3883, #3892, and #3894, please note that @Burapa-J's 5 earlier PRs in the #3843..#3854 band are still OPEN, MERGEABLE, and CI ALL-GREEN (they only slipped past index 80 of the newest-first /pulls?state=open page after the #4015+ flood):

Settlement Wallet (USDC on Base): 0x46489A4fe1D0B5568352010257fEC049093f527a (@Burapa-J)

@limitlessprojects
limitlessprojects merged commit daf32dd into limitlessprojects:main Nov 7, 2026
2 checks passed
@limitlessprojects

Copy link
Copy Markdown
Owner

Landed — daf32dd4

Merged to main with git merge --no-ff and a hand-written credit commit (CREDIT: @Burapa-J). Clean fix for #3882 — the two missing peer-tracking surfaces were exactly where pre-defer state leaked.

Gate Result
Trial merge vs current main clean (2 files: closures.rs + closure_alias_move.rs)
New test standalone closure_alias_move — 3/3 pass (defer-on-iterate + defer-on-match-arm reject with HYB-E3003)
cargo test --workspace exit 0, 486 suites, 3103 passed, 0 failed
Corpus harness 135/135 verdict agreement, tripwire not fired
Differential loop FULL 45/45 accept-side, 90/90 reject-side — FULL AGREEMENT

Review notes

  • The Stmt::For(Iterate) arm correctly gates on non-chan iterables and _ targets; the may-share linking through iterate_var_shapes mirrors the existing Define/Assign arms.
  • mark_nested_overwritten + sync_nested_moves around bind_match_arm closes the arm-body overwrite hole noted in the issue (inner f := fn(){} no longer permanently clears outer active_defers).
  • Extending move checks to task callee and send channel target is a reject-face widening — no regressions anywhere in the battery (3103 green, corpus tripwire silent, differential full agreement), so the widening is behaviorally safe.
  • Fixtures follow repo conventions (lowercase idents, enum/match patterns as in existing suites) — parsed first try.

Bounty

Per BOUNTY.md / discussion #295: this lands against issue #3882 — bounty claim recorded for @Burapa-J on that issue's rail.

limitlessprojects pushed a commit that referenced this pull request Nov 7, 2026
…oncrete types (fixes #2482)

Author: gorky117-cell. Go rejects `T(anyValue)` with "need type
assertion"; Hybrid has no type-assertion syntax, so any->concrete
conversion can never build (Row 13 BUILDABLE).

buildable.rs conversion_kinds_conflict:
- new arm (other, Interface) when other not in {Unknown, Interface} -> conflict
  (concrete->any stays accepted via the pre-existing arm; Unknown stays
  fail-open for unresolved shapes)
- removed now-covered (Pointer(_), Interface) => false arm — pointers now
  correctly fall into the reject set (Go: `(*int)(a)` also needs an assertion)

Verification (merged main, 3-stage battery):
- cargo test --workspace: exit 0, 487 suites, 3114 passed, 0 failed
- new suite any_conversion_soundness_2482 standalone: 11/11 (7 reject /
  4 accept incl. any-equality and nil-comparison controls)
- integration files: 477 on disk (+1 new suite file)
- corpus harness: 135/135 verdict agreement, tripwire not fired
- differential loop FULL: 45/45 accept-side, 90/90 reject-side
- trial merge clean (2 files), no in-merge fixture fixups
- env disclosure: first battery attempt died at link stage (cc/ld, disk
  at 93% ENOSPC-boundary, target dir 5.5G after the #3883 battery);
  trimmed to 83% and re-ran clean — env fault, not code

CREDIT: @gorky117-cell
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment