From b3c4c6bc07662ad3e42c9686392bced136c86e40 Mon Sep 17 00:00:00 2001 From: SuperPiIot <323746769+SuperPiIot@users.noreply.github.com> Date: Fri, 13 Nov 2026 21:19:46 -0300 Subject: [PATCH] fix(hy-check): reject non-channel receive operands in declaration array-length TypeExprs (fixes #3880) --- compiler/crates/hy-check/src/emit_guard.rs | 4 +- compiler/crates/hy-check/src/recv.rs | 2 +- .../hy-check/tests/recv_operand_soundness.rs | 43 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/compiler/crates/hy-check/src/emit_guard.rs b/compiler/crates/hy-check/src/emit_guard.rs index e31214fe90..4b7e4a942c 100644 --- a/compiler/crates/hy-check/src/emit_guard.rs +++ b/compiler/crates/hy-check/src/emit_guard.rs @@ -1310,7 +1310,9 @@ impl<'a> Checker<'a> { // TypeRef, so running the builtin-argument check here covers struct // fields, aliases, local annotations, literals and conversions. let empty_st = crate::model::FnState::default(); - self.check_expr_builtin_args(st.unwrap_or(&empty_st), len); + let st_ref = st.unwrap_or(&empty_st); + self.check_expr_recv_operands(st_ref, len); + self.check_expr_builtin_args(st_ref, len); let is_wrapped_ident_int_conv = matches!( unwrap_parens(len), Expr::Call { callee, args, .. } diff --git a/compiler/crates/hy-check/src/recv.rs b/compiler/crates/hy-check/src/recv.rs index 58e7125090..370eff30d1 100644 --- a/compiler/crates/hy-check/src/recv.rs +++ b/compiler/crates/hy-check/src/recv.rs @@ -199,7 +199,7 @@ impl<'a> Checker<'a> { /// Recurse an expression tree, classifying every `Expr::Receive` /// operand found at any depth. FnLit bodies are the A5 perimeter — /// not walked here (the closure pass enters them). - fn check_expr_recv_operands(&mut self, st: &FnState, expr: &Expr) { + pub(crate) fn check_expr_recv_operands(&mut self, st: &FnState, expr: &Expr) { match expr { Expr::Receive { chan, span } => { self.check_recv_operand(st, chan, *span); diff --git a/compiler/crates/hy-check/tests/recv_operand_soundness.rs b/compiler/crates/hy-check/tests/recv_operand_soundness.rs index 9c7bf2b33b..955dcf7099 100644 --- a/compiler/crates/hy-check/tests/recv_operand_soundness.rs +++ b/compiler/crates/hy-check/tests/recv_operand_soundness.rs @@ -196,3 +196,46 @@ fn drain(opt option[chan chan<- int]) { ); assert_has(&r_select, "HYB-E3008"); } + +#[test] +fn decl_type_expr_array_length_non_channel_receive_rejected_3880() { + // Issue #3880: struct field, type alias, enum payload, and fn signature + // array-length TypeExprs must reject receive from non-channel with HYB-E2008. + let r_struct = check( + r#"package main + +struct bad_struct { + a [<-5]int +} +"#, + ); + assert_has(&r_struct, "HYB-E2008"); + + let r_alias = check( + r#"package main + +type bad_alias = [<-"abc"]int +"#, + ); + assert_has(&r_alias, "HYB-E2008"); + + let r_fn_param = check( + r#"package main + +fn run(a [<-5]int) { + _ = a +} +"#, + ); + assert_has(&r_fn_param, "HYB-E2008"); + + let r_fn_ret = check( + r#"package main + +fn run() [<-5]int { + return [0] +} +"#, + ); + assert_has(&r_fn_ret, "HYB-E2008"); +}