From b37a7d2e4d4554a8e54b3c388e738cd1f6bdad00 Mon Sep 17 00:00:00 2001 From: OSKR Date: Wed, 22 Jul 2026 14:24:51 -0400 Subject: [PATCH 1/3] compiler: add bool[N] ctor binding via Expr::bool_array and From> The Expr API has helpers for int, bool, byte, bytes, string, but no bool_array helper nor From> impl. This blocks Rust test code from constructing bool[N] ctor params for silverscript contracts. - Add Expr::bool_array(Vec) parallel to Expr::bytes(Vec) - Add From> for Expr - Unit tests pin both --- silverscript-lang/src/ast/mod.rs | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/silverscript-lang/src/ast/mod.rs b/silverscript-lang/src/ast/mod.rs index cb77a882..2574a060 100644 --- a/silverscript-lang/src/ast/mod.rs +++ b/silverscript-lang/src/ast/mod.rs @@ -548,6 +548,13 @@ impl<'i> Expr<'i> { Self::new(ExprKind::Array(value.into_iter().map(Expr::byte).collect()), Span::default()) } + pub fn bool_array(value: Vec) -> Self { + Self::new( + ExprKind::Array(value.into_iter().map(Expr::bool).collect()), + Span::default(), + ) + } + pub fn string(value: impl Into) -> Self { Self::new(ExprKind::String(value.into()), Span::default()) } @@ -579,6 +586,12 @@ impl<'i> From> for Expr<'i> { } } +impl<'i> From> for Expr<'i> { + fn from(value: Vec) -> Self { + Expr::bool_array(value) + } +} + impl<'i> From for Expr<'i> { fn from(value: String) -> Self { Expr::string(value) @@ -2591,3 +2604,29 @@ fn parse_identifier<'i>(pair: Pair<'i, Rule>) -> Result, Compiler Ok(Identifier { name: value, span }) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bool_array_helper_builds_expr_array() { + let expr = Expr::bool_array(vec![true, false, true]); + if let ExprKind::Array(items) = expr.kind { + assert_eq!(items.len(), 3); + if let ExprKind::Bool(b) = items[0].kind { + assert!(b); + } else { + panic!("expected Bool"); + } + } else { + panic!("expected Array"); + } + } + + #[test] + fn from_vec_bool_impl_works() { + let _expr: Expr = vec![true, false].into(); + let _expr2: Expr = Expr::from(vec![false, true, false]); + } +} From 5305cf2e0ab91c7afe1d0e5909adbb68583e2d81 Mon Sep 17 00:00:00 2001 From: OSKR Date: Wed, 22 Jul 2026 14:24:54 -0400 Subject: [PATCH 2/3] tests: add bool[N] VOS regression fixture for ctor binding Integration test uses bool[2] param initW and field ref initW in validateOutputState. Uses field ref (not literal) to stay independent from PR #1's literal inference fix. --- silverscript-lang/tests/compiler_tests.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/silverscript-lang/tests/compiler_tests.rs b/silverscript-lang/tests/compiler_tests.rs index af4f59c5..c396db8b 100644 --- a/silverscript-lang/tests/compiler_tests.rs +++ b/silverscript-lang/tests/compiler_tests.rs @@ -11225,3 +11225,19 @@ fn blake3_with_key_requires_a_fixed_32_byte_key() { let err = compile_contract(numeric_data, &[], CompileOptions::default()).expect_err("numeric Blake3 data should be rejected"); assert!(err.to_string().contains("argument 'data' expects byte[], got int"), "unexpected error: {err}"); } + +#[test] +fn compiles_validate_output_state_with_bool_array_literal() { + let source = r#" + contract C(bool[2] initW) { + bool[2] w = initW; + entrypoint function main() { + validateOutputState(0, { w: initW }); + } + } + "#; + let result = compile_contract(source, &[vec![true, false].into()], CompileOptions::default()); + if let Err(ref e) = result { + panic!("compile failed: {e}"); + } +} From 085d347298b31fe89e4e671424b85d9e28e09723 Mon Sep 17 00:00:00 2001 From: OSKR Date: Wed, 22 Jul 2026 14:31:39 -0400 Subject: [PATCH 3/3] compiler: preserve bool[N] in validateOutputState state encoding fixed_type_size in compile.rs handles arrays for byte[] and int[] but not bool[], causing fixed_state_payload_len to return None for bool[N] fields. This propagates to compile_encoded_state_object and compile_encoded_flat_state_fields, rejecting bool[N] in validateOutputState/validateNextState. Fix by adding is_bool() branch: bool is 1 byte per element, same layout as byte[N]. Adds: - Unit test fixed_type_size_accepts_bool_array in compile.rs - Integration test compiles_validate_output_state_with_bool_array_field in compiler_tests.rs --- silverscript-lang/src/compiler/compile.rs | 19 +++++++++++++++++++ silverscript-lang/tests/compiler_tests.rs | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/silverscript-lang/src/compiler/compile.rs b/silverscript-lang/src/compiler/compile.rs index 41de54c3..f767bce0 100644 --- a/silverscript-lang/src/compiler/compile.rs +++ b/silverscript-lang/src/compiler/compile.rs @@ -516,6 +516,9 @@ fn fixed_type_size(type_ref: &TypeRef) -> Option { if elem_type.is_int() { return Some((size * 8) as i64); } + if elem_type.is_bool() { + return Some(size as i64); + } } return None; } @@ -3839,6 +3842,22 @@ mod tests { use super::{Op0, OpPushData1, OpPushData2, StackBindings, data_prefix, eval_const_int}; + #[test] + fn fixed_type_size_accepts_bool_array() { + use crate::ast::TypeRef; + use crate::ast::TypeBase; + use crate::ast::ArrayDim; + // bool[2] → Some(2) (1 byte per element) + let bool2 = TypeRef { base: TypeBase::Bool, array_dims: vec![ArrayDim::Fixed(2)] }; + assert_eq!(super::fixed_type_size(&bool2), Some(2)); + // bool[4] → Some(4) + let bool4 = TypeRef { base: TypeBase::Bool, array_dims: vec![ArrayDim::Fixed(4)] }; + assert_eq!(super::fixed_type_size(&bool4), Some(4)); + // scalar bool → Some(1) + let bool_scalar = TypeRef { base: TypeBase::Bool, array_dims: vec![] }; + assert_eq!(super::fixed_type_size(&bool_scalar), Some(1)); + } + #[test] fn data_prefix_encodes_small_pushes() { assert_eq!(data_prefix(0), vec![Op0]); diff --git a/silverscript-lang/tests/compiler_tests.rs b/silverscript-lang/tests/compiler_tests.rs index af4f59c5..ebeb4ae8 100644 --- a/silverscript-lang/tests/compiler_tests.rs +++ b/silverscript-lang/tests/compiler_tests.rs @@ -5467,6 +5467,28 @@ fn compiles_validate_output_state_to_expected_script() { assert_eq!(compiled.script, expected); } +#[test] +fn compiles_validate_output_state_with_bool_array_field() { + let source = r#" + contract C(bool[2] initW) { + bool[2] w = initW; + + entrypoint function main() { + validateOutputState(0, { w: w }); + } + } + "#; + + let compiled = compile_contract( + source, + &[vec![Expr::bool(true), Expr::bool(false)].into()], + CompileOptions::default(), + ) + .expect("bool[2] field ref in validateOutputState should compile"); + // Verify script is non-empty (compilation produced output) + assert!(!compiled.script.is_empty(), "compiled script must not be empty"); +} + #[test] fn runs_validate_output_state() { let source = r#"