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]); + } +} 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..5b6e4ec0 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#" @@ -11225,3 +11247,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}"); + } +}