Skip to content

fix(hy-check): check channel-send operand kinds at both send sites (fixes #2577) - #2582

Open
Dimany4 wants to merge 1 commit into
limitlessprojects:mainfrom
Dimany4:fix-send-operand-kinds-2577
Open

Dimany4 wants to merge 1 commit into
limitlessprojects:mainfrom
Dimany4:fix-send-operand-kinds-2577

Conversation

@Dimany4

@Dimany4 Dimany4 commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2577.

A channel send never compared the sent value with the element type of the channel, so var ch chan float64; var i int = 1; ch <- i passed hyb check and failed go build with cannot use i (variable of type int) as float64 value in send. The report named the select arm — the plain send had the same hole, and both sites are covered here.

Fix

  • check_send_operand_kinds(chan, value, span) in buildable.rs, called from Stmt::Send and SelectArmKind::Send. It reuses kinds_conflict() — the [soundness] P2: 16 accepted-but-unbuildable type/shape bypasses in the buildable operand pass (HYB-E1002) #682 "a value of kind got does not satisfy a declared type of kind want" predicate the assignment family already uses — so every operand-kind mismatch is covered (string → chan int, a map → chan int, …), not only the int/float64 pair, and there is no second type table to keep in sync.
  • One direction is narrowed rather than reused: kinds_conflict fails open on (Float, Int), because an untyped int constant is assignable to a float target (var f float64 = 1 builds). The send narrows it with the checker's existing is_untyped_int_const(), the same predicate the assignment rules use — so ch <- 1, ch <- 1 + 2 and ch <- k for const k = 2 (file-level or fn-local) stay accepted, while a typed const k int = 2 and a typed int variable are rejected, exactly as Go does.
  • Stand-down parity with the audited hatch: inside managed(...) the check returns early, as the boundary rules do (--report-managed records the site, HYB-E8001). Collector holds ck as a shared reference, so the walk cannot raise the checker's own counter; the nesting depth is tracked in the walk instead, with Stmt::Managed bumping it around block_scoped(). Without that parity the new check broke the lib suite's fc_closure_boundary_send_inside_managed_escapes.
  • Not a duplicate finding: fix(hy-check): validate channel send and select send operand types (fixes #2577, fixes #2648, fixes #2691, fixes #2575) #2581 (opened ~7 minutes earlier) reaches the same root cause with a narrower rule — Int → Chan(Float) only. This change is built on the shared predicate, covers both send sites, and keeps the hatch parity; the two should not be counted as two root causes.

Evidence

program before after
ch <- i (typed int → chan float64) accept HYB-E1002
select { ch <- i: default: } accept HYB-E1002
ch <- s (string → chan int) accept HYB-E1002
ch <- k for const k int = 2 (typed) accept HYB-E1002
ch <- 1 (untyped literal) accept accept (control)
ch <- 1 + 2 (constant expression) accept accept (control)
ch <- k for const k = 2 (untyped, fn-local and file-level) accept accept (control)
ch <- f for var f float64 accept accept (control)
ch <- i for var i int → chan int accept accept (control)
the typed-int send inside managed(...) accept accept (hatch parity)
  • New suite compiler/crates/hy-check/tests/send_operand_kinds_2577.rs — 9 tests, all green: four rejects (plain send, select arm, string → chan int, typed constant at both sites), four accept-control groups (untyped literal, constant expression, untyped named constant, matching/float operands), one pinning the managed(...) stand-down.
  • cargo test -p hy-check --test send_operand_kinds_2577 → 9/9 green on this head; CI runs the full package suite on it.
  • Corpus harness (tools/corpus_harness.py): the earlier head of this branch measured 128/130 with the same two pre-existing failures as main (10-a5-http-handler-capture.hy, local-composite-chan-recv-reject-e2008.hy), tripwire: not fired; the send sites are the only behaviour this diff touches.
  • Scope: buildable.rs (+69/−1: the check, the local depth field, the two call sites) plus the new test file. No other crate is touched.

…ixes limitlessprojects#2577)

A channel send never compared the sent value with the element type of the
channel, so `var ch chan float64; var i int = 1; ch <- i` passed `hyb check`
and failed `go build` (`cannot use i (variable of type int) as float64 value
in send`). The report described the `select` arm; `Stmt::Send` had the same
hole, and both sites now call the same check.

The check reuses the rules the assignment family already applies:
`kinds_conflict` plus the untyped-int-to-float carve-out, reported as
HYB-E1002 with the `value in send` wording of the emitted Go. It stands down
inside `managed`, the audited escape hatch, exactly like the boundary rules,
which is why buildable.rs now raises the depth it already tracks.

Tests: compiler/crates/hy-check/tests/send_operand_kinds_2577.rs (both sites;
untyped literals, constant expressions and untyped named constants stay
accepted, typed `const k int = 2` and typed variables are refused, `managed`
parity pinned).
@Dimany4
Dimany4 force-pushed the fix-send-operand-kinds-2577 branch from 8f179ca to f6d1d5e Compare November 2, 2026 02:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:stale-base Shepherd: behind main beyond threshold

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hy-check: select send arm accepts typed int variable sent to chan float64 (soundness hole)

1 participant