Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions silverscript-lang/src/compiler/debug_value_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down Expand Up @@ -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]");
}
}
34 changes: 34 additions & 0 deletions silverscript-lang/tests/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}