diff --git a/silverscript-lang/src/compiler/debug_value_types.rs b/silverscript-lang/src/compiler/debug_value_types.rs index c83b98b4..a0e91e72 100644 --- a/silverscript-lang/src/compiler/debug_value_types.rs +++ b/silverscript-lang/src/compiler/debug_value_types.rs @@ -100,6 +100,10 @@ pub(super) fn infer_debug_expr_value_type<'i>( ExprKind::Array(values) => { if values.iter().all(|value| matches!(value.kind, ExprKind::Byte(_))) { Ok(TypeRef { base: TypeBase::Byte, array_dims: vec![ArrayDim::Fixed(values.len())] }) + } else if values.iter().all(|value| matches!(value.kind, ExprKind::Int(_))) { + Ok(TypeRef { base: TypeBase::Int, array_dims: vec![ArrayDim::Fixed(values.len())] }) + } else if values.iter().all(|value| matches!(value.kind, ExprKind::Bool(_))) { + Ok(TypeRef { base: TypeBase::Bool, array_dims: vec![ArrayDim::Fixed(values.len())] }) } else { Ok(dynamic_bytes()) } @@ -255,4 +259,35 @@ mod tests { types.insert("buf".to_string(), "byte[]".to_string()); assert_eq!(infer(length, HashMap::new(), types), "int"); } + + #[test] + fn infers_int_and_bool_array_literal_value_types() { + // All-int literal array → int[N] + let int_arr = Expr::new( + ExprKind::Array(vec![Expr::int(0), Expr::int(1), Expr::int(2), Expr::int(3)]), + span::Span::default(), + ); + assert_eq!(infer(int_arr, HashMap::new(), HashMap::new()), "int[4]"); + + // All-bool literal array → bool[N] + let bool_arr = Expr::new( + ExprKind::Array(vec![Expr::bool(true), Expr::bool(false)]), + span::Span::default(), + ); + assert_eq!(infer(bool_arr, HashMap::new(), HashMap::new()), "bool[2]"); + + // Mixed array falls back to byte[] (heterogeneous → dynamic bytes) + let mixed_arr = Expr::new( + ExprKind::Array(vec![Expr::int(1), Expr::bool(true)]), + span::Span::default(), + ); + assert_eq!(infer(mixed_arr, HashMap::new(), HashMap::new()), "byte[]"); + + // All-byte array still byte[N] (regression guard) + let byte_arr = Expr::new( + ExprKind::Array(vec![Expr::byte(0x00), Expr::byte(0xff)]), + span::Span::default(), + ); + assert_eq!(infer(byte_arr, HashMap::new(), HashMap::new()), "byte[2]"); + } } diff --git a/silverscript-lang/tests/compiler_tests.rs b/silverscript-lang/tests/compiler_tests.rs index af4f59c5..a0662c47 100644 --- a/silverscript-lang/tests/compiler_tests.rs +++ b/silverscript-lang/tests/compiler_tests.rs @@ -11225,3 +11225,37 @@ 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 vos_int_array_literal_infers_correct_type() { + // Regression: int[4] array literal in validateOutputState body must infer as int[4], + // not fall back to byte[]. Previously would fail compile-time type mismatch. + let source = r#" + contract C(int[4] initWinners) { + int[4] winners = initWinners; + + entrypoint function main() { + validateOutputState(0, {winners: [0, 1, 2, 3]}); + } + } + "#; + + let input_compiled = compile_contract( + source, + &[vec![Expr::int(0), Expr::int(1), Expr::int(2), Expr::int(3)].into()], + CompileOptions::default(), + ) + .expect("compile succeeds"); + + let output_compiled = compile_contract( + source, + &[vec![Expr::int(4), Expr::int(5), Expr::int(6), Expr::int(7)].into()], + CompileOptions::default(), + ) + .expect("compile succeeds"); + + // Different ctor values must produce different scripts — proves the fix + // actually encodes each int[4] value through VOS splice rather than + // always emitting the same passthrough bytecode. + assert_ne!(input_compiled.script, output_compiled.script); +}