diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index dd63a254a18..530d3ca5400 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -35,6 +35,10 @@ pub enum RuntimeError { UnknownLoopBound { call_stack: CallStack }, #[error("Argument is not constant")] AssertConstantFailed { call_stack: CallStack }, + #[error("The static_assert message is not constant")] + StaticAssertDynamicMessage { call_stack: CallStack }, + #[error("Nested slices are not supported")] + StaticAssertFailed { call_stack: CallStack }, #[error("Nested slices are not supported")] NestedSlice { call_stack: CallStack }, #[error("Big Integer modulus do no match")] @@ -120,6 +124,8 @@ impl RuntimeError { | RuntimeError::UnInitialized { call_stack, .. } | RuntimeError::UnknownLoopBound { call_stack } | RuntimeError::AssertConstantFailed { call_stack } + | RuntimeError::StaticAssertFailed { call_stack } + | RuntimeError::StaticAssertDynamicMessage { call_stack } | RuntimeError::IntegerOutOfBounds { call_stack, .. } | RuntimeError::UnsupportedIntegerSize { call_stack, .. } | RuntimeError::NestedSlice { call_stack, .. } diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 71fb51bf750..0e0c3f1e631 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -66,7 +66,10 @@ pub(crate) fn optimize_into_acir( // Run mem2reg with the CFG separated into blocks .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization") - .try_run_pass(Ssa::evaluate_assert_constant, "After Assert Constant:")? + .try_run_pass( + Ssa::evaluate_static_assert_and_assert_constant, + "After `static_assert` and `assert_constant`:", + )? .try_run_pass(Ssa::unroll_loops_iteratively, "After Unrolling:")? .run_pass(Ssa::simplify_cfg, "After Simplifying:") .run_pass(Ssa::flatten_cfg, "After Flattening:") diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index f763ae52d50..994386f8197 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -497,9 +497,22 @@ impl DataFlowGraph { } } - /// True if the given ValueId refers to a constant value + /// True if the given ValueId refers to a (recursively) constant value pub(crate) fn is_constant(&self, argument: ValueId) -> bool { - !matches!(&self[self.resolve(argument)], Value::Instruction { .. } | Value::Param { .. }) + match &self[self.resolve(argument)] { + Value::Instruction { .. } | Value::Param { .. } => false, + Value::Array { array, .. } => array.iter().all(|element| self.is_constant(*element)), + _ => true, + } + } + + /// True that the input is a non-zero `Value::NumericConstant` + pub(crate) fn is_constant_true(&self, argument: ValueId) -> bool { + if let Some(constant) = self.get_numeric_constant(argument) { + !constant.is_zero() + } else { + false + } } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 8f881b86e47..f854e8e0693 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -52,6 +52,7 @@ pub(crate) enum Intrinsic { ArrayLen, AsSlice, AssertConstant, + StaticAssert, SlicePushBack, SlicePushFront, SlicePopBack, @@ -76,6 +77,7 @@ impl std::fmt::Display for Intrinsic { Intrinsic::ArrayLen => write!(f, "array_len"), Intrinsic::AsSlice => write!(f, "as_slice"), Intrinsic::AssertConstant => write!(f, "assert_constant"), + Intrinsic::StaticAssert => write!(f, "static_assert"), Intrinsic::SlicePushBack => write!(f, "slice_push_back"), Intrinsic::SlicePushFront => write!(f, "slice_push_front"), Intrinsic::SlicePopBack => write!(f, "slice_pop_back"), @@ -104,9 +106,10 @@ impl Intrinsic { /// If there are no side effects then the `Intrinsic` can be removed if the result is unused. pub(crate) fn has_side_effects(&self) -> bool { match self { - Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint | Intrinsic::AsWitness => { - true - } + Intrinsic::AssertConstant + | Intrinsic::StaticAssert + | Intrinsic::ApplyRangeConstraint + | Intrinsic::AsWitness => true, // These apply a constraint that the input must fit into a specified number of limbs. Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => true, @@ -142,6 +145,7 @@ impl Intrinsic { "array_len" => Some(Intrinsic::ArrayLen), "as_slice" => Some(Intrinsic::AsSlice), "assert_constant" => Some(Intrinsic::AssertConstant), + "static_assert" => Some(Intrinsic::StaticAssert), "apply_range_constraint" => Some(Intrinsic::ApplyRangeConstraint), "slice_push_back" => Some(Intrinsic::SlicePushBack), "slice_push_front" => Some(Intrinsic::SlicePushFront), diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index e4635c0f974..281ab7c3057 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -246,6 +246,21 @@ pub(super) fn simplify_call( SimplifyResult::None } } + Intrinsic::StaticAssert => { + if arguments.len() != 2 { + panic!("ICE: static_assert called with wrong number of arguments") + } + + if !dfg.is_constant(arguments[1]) { + return SimplifyResult::None; + } + + if dfg.is_constant_true(arguments[0]) { + SimplifyResult::Remove + } else { + SimplifyResult::None + } + } Intrinsic::ApplyRangeConstraint => { let value = arguments[0]; let max_bit_size = dfg.get_numeric_constant(arguments[1]); diff --git a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs index a3608f89612..f0c854373ec 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs @@ -22,7 +22,9 @@ impl Ssa { /// since we must go through every instruction to find all references to `assert_constant` /// while loop unrolling only touches blocks with loops in them. #[tracing::instrument(level = "trace", skip(self))] - pub(crate) fn evaluate_assert_constant(mut self) -> Result { + pub(crate) fn evaluate_static_assert_and_assert_constant( + mut self, + ) -> Result { for function in self.functions.values_mut() { for block in function.reachable_blocks() { // Unfortunately we can't just use instructions.retain(...) here since @@ -54,10 +56,13 @@ fn check_instruction( instruction: InstructionId, ) -> Result { let assert_constant_id = function.dfg.import_intrinsic(Intrinsic::AssertConstant); + let static_assert_id = function.dfg.import_intrinsic(Intrinsic::StaticAssert); match &function.dfg[instruction] { Instruction::Call { func, arguments } => { if *func == assert_constant_id { evaluate_assert_constant(function, instruction, arguments) + } else if *func == static_assert_id { + evaluate_static_assert(function, instruction, arguments) } else { Ok(true) } @@ -82,3 +87,31 @@ fn evaluate_assert_constant( Err(RuntimeError::AssertConstantFailed { call_stack }) } } + +/// Evaluate a call to `static_assert`, returning an error if the value is false +/// or not constant (see assert_constant). +/// +/// When it passes, Ok(false) is returned. This signifies a +/// success but also that the instruction need not be reinserted into the block being unrolled +/// since it has already been evaluated. +fn evaluate_static_assert( + function: &Function, + instruction: InstructionId, + arguments: &[ValueId], +) -> Result { + if arguments.len() != 2 { + panic!("ICE: static_assert called with wrong number of arguments") + } + + if !function.dfg.is_constant(arguments[1]) { + let call_stack = function.dfg.get_call_stack(instruction); + return Err(RuntimeError::StaticAssertDynamicMessage { call_stack }); + } + + if function.dfg.is_constant_true(arguments[0]) { + Ok(false) + } else { + let call_stack = function.dfg.get_call_stack(instruction); + Err(RuntimeError::StaticAssertFailed { call_stack }) + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index c9a6b7bf9c3..f9a3c9a55eb 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -150,6 +150,7 @@ impl Context { Intrinsic::ArrayLen | Intrinsic::AssertConstant + | Intrinsic::StaticAssert | Intrinsic::ApplyRangeConstraint | Intrinsic::StrAsBytes | Intrinsic::ToBits(_) diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index fd7a1a06fc8..242eea7d6f4 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -226,6 +226,7 @@ fn slice_capacity_change( // These cases don't affect slice capacities Intrinsic::AssertConstant + | Intrinsic::StaticAssert | Intrinsic::ApplyRangeConstraint | Intrinsic::ArrayLen | Intrinsic::StrAsBytes diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index ae973385182..87cc7990753 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -189,23 +189,6 @@ impl ExpressionKind { struct_type: None, })) } - - /// Returns true if the expression is a literal integer - pub fn is_integer(&self) -> bool { - self.as_integer().is_some() - } - - fn as_integer(&self) -> Option { - let literal = match self { - ExpressionKind::Literal(literal) => literal, - _ => return None, - }; - - match literal { - Literal::Integer(integer, _) => Some(*integer), - _ => None, - } - } } impl Recoverable for ExpressionKind { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index d589294d4fd..0ed043914f3 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -610,6 +610,7 @@ impl<'interner> Monomorphizer<'interner> { }) .transpose()? .map(Box::new); + Ok(ast::Expression::Constrain(Box::new(expr), location, assert_message)) } HirStatement::Assign(assign) => self.assign(assign), diff --git a/docs/docs/noir/concepts/assert.md b/docs/docs/noir/concepts/assert.md index bcff613a695..2132de42072 100644 --- a/docs/docs/noir/concepts/assert.md +++ b/docs/docs/noir/concepts/assert.md @@ -1,9 +1,9 @@ --- title: Assert Function description: - Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or - comparison expression that follows to be true, and what happens if the expression is false at - runtime. + Learn about the `assert` and `static_assert` functions in Noir, which can be used to explicitly + constrain the predicate or comparison expression that follows to be true, and what happens if + the expression is false at runtime or compile-time, respectively. keywords: [Noir programming language, assert statement, predicate expression, comparison expression] sidebar_position: 4 --- @@ -43,3 +43,36 @@ let s = myStruct { myField: y }; assert(s.myField == x, s); ``` +There is also a special `static_assert` function that behaves like `assert`, +but that runs at compile-time. + +```rust +fn main(xs: [Field; 3]) { + let x = 2 + 2; + let y = 4; + static_assert(x == y, "expected 2 + 2 to equal 4"); + + // This passes since the length of `xs` is known at compile-time + static_assert(xs.len() == 3, "expected the input to have 3 elements"); +} +``` + +This function fails when passed a dynamic (run-time) argument: + +```rust +fn main(x : Field, y : Field) { + // this fails because `x` is not known at compile-time + static_assert(x == 2, "expected x to be known at compile-time and equal to 2"); + + let mut example_slice = &[]; + if y == 4 { + example_slice = example_slice.push_back(0); + } + + // This fails because the length of `example_slice` is not known at + // compile-time + let error_message = "expected an empty slice, known at compile-time"; + static_assert(example_slice.len() == 0, error_message); +} +``` + diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 1a756f441ba..c5c184fa825 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -48,6 +48,11 @@ pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: // Useful for debugging for-loop bounds. #[builtin(assert_constant)] pub fn assert_constant(x: T) {} + +// Asserts that the given value is both true and known at compile-time +#[builtin(static_assert)] +pub fn static_assert(predicate: bool, message: str) {} + // from_field and as_field are private since they are not valid for every type. // `as` should be the default for users to cast between primitive types, and in the future // traits can be used to work with generic types. diff --git a/test_programs/compile_failure/assert_constant_dynamic_array/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_array/Nargo.toml new file mode 100644 index 00000000000..6171770b62b --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_array/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_array" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_array/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_array/src/main.nr new file mode 100644 index 00000000000..43da3ef0eaa --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_array/src/main.nr @@ -0,0 +1,5 @@ +fn main( + dynamic_one: Field, // == 1 +) { + assert_constant([dynamic_one]); +} diff --git a/test_programs/compile_failure/assert_constant_dynamic_plus/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_plus/Nargo.toml new file mode 100644 index 00000000000..e5583e53126 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_plus/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_plus" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_plus/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_plus/src/main.nr new file mode 100644 index 00000000000..f8a377092a2 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_plus/src/main.nr @@ -0,0 +1,5 @@ +fn main( + dynamic_one: Field, // == 1 +) { + assert_constant(dynamic_one + 1 == 3); +} diff --git a/test_programs/compile_failure/assert_constant_dynamic_slice/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_slice/Nargo.toml new file mode 100644 index 00000000000..d1d068a79b8 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_slice/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_slice/src/main.nr new file mode 100644 index 00000000000..f07002d7d4c --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_slice/src/main.nr @@ -0,0 +1,5 @@ +fn main( + dynamic_one: Field, // == 1 +) { + assert_constant(&[dynamic_one]); +} diff --git a/test_programs/compile_failure/assert_constant_dynamic_struct_array/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_struct_array/Nargo.toml new file mode 100644 index 00000000000..18781f4d57d --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_struct_array/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_struct_array" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_struct_array/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_struct_array/src/main.nr new file mode 100644 index 00000000000..b9f4dceafc0 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_struct_array/src/main.nr @@ -0,0 +1,12 @@ +struct Foo { + field: Field, + array: [Field; 3], + slice: [Field], +} + +fn main( + dynamic_one: Field, // == 1 +) { + let foo_dynamic_array = Foo { field: 0, array: [dynamic_one, 2, 3], slice: &[] }; + assert_constant(foo_dynamic_array); +} diff --git a/test_programs/compile_failure/assert_constant_dynamic_struct_field/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_struct_field/Nargo.toml new file mode 100644 index 00000000000..173ea44101a --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_struct_field/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_struct_field" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_struct_field/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_struct_field/src/main.nr new file mode 100644 index 00000000000..e7986c93b6b --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_struct_field/src/main.nr @@ -0,0 +1,12 @@ +struct Foo { + field: Field, + array: [Field; 3], + slice: [Field], +} + +fn main( + dynamic_one: Field, // == 1 +) { + let foo_dynamic = Foo { field: dynamic_one, array: [1, 2, 3], slice: &[] }; + assert_constant(foo_dynamic); +} diff --git a/test_programs/compile_failure/assert_constant_dynamic_struct_slice/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_struct_slice/Nargo.toml new file mode 100644 index 00000000000..426f4826a0b --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_struct_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_struct_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_struct_slice/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_struct_slice/src/main.nr new file mode 100644 index 00000000000..c775b563928 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_struct_slice/src/main.nr @@ -0,0 +1,12 @@ +struct Foo { + field: Field, + array: [Field; 3], + slice: [Field], +} + +fn main( + dynamic_one: Field, // == 1 +) { + let foo_dynamic_slice = Foo { field: 0, array: [1, 2, 3], slice: &[dynamic_one] }; + assert_constant(foo_dynamic_slice); +} diff --git a/test_programs/compile_failure/assert_constant_dynamic_tuple/Nargo.toml b/test_programs/compile_failure/assert_constant_dynamic_tuple/Nargo.toml new file mode 100644 index 00000000000..de7b2031300 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_tuple/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_dynamic_tuple" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_dynamic_tuple/src/main.nr b/test_programs/compile_failure/assert_constant_dynamic_tuple/src/main.nr new file mode 100644 index 00000000000..579a5a0991f --- /dev/null +++ b/test_programs/compile_failure/assert_constant_dynamic_tuple/src/main.nr @@ -0,0 +1,5 @@ +fn main( + dynamic_one: Field, // == 1 +) { + assert_constant((dynamic_one, 2)); +} diff --git a/test_programs/compile_failure/assert_constant_false/Nargo.toml b/test_programs/compile_failure/assert_constant_false/Nargo.toml new file mode 100644 index 00000000000..cb8d59f4293 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_false/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_false" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_constant_false/src/main.nr b/test_programs/compile_failure/assert_constant_false/src/main.nr new file mode 100644 index 00000000000..f4e98cfec37 --- /dev/null +++ b/test_programs/compile_failure/assert_constant_false/src/main.nr @@ -0,0 +1,3 @@ +fn main() { + std::static_assert(false, ""); +} diff --git a/test_programs/compile_failure/static_assert_dynamic_array_len/Nargo.toml b/test_programs/compile_failure/static_assert_dynamic_array_len/Nargo.toml new file mode 100644 index 00000000000..1c6c13ce225 --- /dev/null +++ b/test_programs/compile_failure/static_assert_dynamic_array_len/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "static_assert_dynamic_array_len" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/static_assert_dynamic_array_len/src/main.nr b/test_programs/compile_failure/static_assert_dynamic_array_len/src/main.nr new file mode 100644 index 00000000000..9e4665f8ad8 --- /dev/null +++ b/test_programs/compile_failure/static_assert_dynamic_array_len/src/main.nr @@ -0,0 +1,5 @@ +fn main( + dynamic_one: Field, // == 1 +) { + std::static_assert([1, 2, dynamic_one].len() == 4, ""); +} diff --git a/test_programs/compile_failure/static_assert_dynamic_slice/Nargo.toml b/test_programs/compile_failure/static_assert_dynamic_slice/Nargo.toml new file mode 100644 index 00000000000..45e39ae24a8 --- /dev/null +++ b/test_programs/compile_failure/static_assert_dynamic_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "static_assert_dynamic_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/static_assert_dynamic_slice/src/main.nr b/test_programs/compile_failure/static_assert_dynamic_slice/src/main.nr new file mode 100644 index 00000000000..6621b8a1fb3 --- /dev/null +++ b/test_programs/compile_failure/static_assert_dynamic_slice/src/main.nr @@ -0,0 +1,15 @@ +fn main( + dynamic_one: Field, // == 1 + dynamic_two: Field, // == 2 +) { + // length unknown at compile time + let mut dynamic_built_slice_pair = &[]; + if dynamic_one == 1 { + dynamic_built_slice_pair = dynamic_built_slice_pair.push_back(dynamic_one); + } + if dynamic_two == 2 { + dynamic_built_slice_pair = dynamic_built_slice_pair.push_back(dynamic_two); + } + + std::static_assert(dynamic_built_slice_pair.len() == 3, ""); +} diff --git a/test_programs/compile_failure/static_assert_plus/Nargo.toml b/test_programs/compile_failure/static_assert_plus/Nargo.toml new file mode 100644 index 00000000000..fdb90c0dc1c --- /dev/null +++ b/test_programs/compile_failure/static_assert_plus/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "static_assert_plus" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/static_assert_plus/src/main.nr b/test_programs/compile_failure/static_assert_plus/src/main.nr new file mode 100644 index 00000000000..2241743ddb0 --- /dev/null +++ b/test_programs/compile_failure/static_assert_plus/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + let x = 2; + let y = 3; + let xy = x + y; + + std::static_assert(xy == 6, "2 + 3 != 6"); +} diff --git a/test_programs/compile_success_empty/assert_constant/Nargo.toml b/test_programs/compile_success_empty/assert_constant/Nargo.toml new file mode 100644 index 00000000000..f18d4828fdf --- /dev/null +++ b/test_programs/compile_success_empty/assert_constant/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/assert_constant/src/main.nr b/test_programs/compile_success_empty/assert_constant/src/main.nr new file mode 100644 index 00000000000..6910a2d17b2 --- /dev/null +++ b/test_programs/compile_success_empty/assert_constant/src/main.nr @@ -0,0 +1,59 @@ +use std::static_assert; + +global GLOBAL_ONE = 1; +global GLOBAL_TWO = 2; +global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; + +// contents known at compile time +// length known at compile time +global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; + +struct Foo { + field: Field, + array: [Field; 3], + slice: [Field], +} + +fn main( + dynamic_one: Field, // == 1 + dynamic_two: Field // == 2 +) { + // contents unknown at compile time + // length known at compile time + let dynamic_array_pair = [dynamic_one, dynamic_two]; + let dynamic_slice_pair = &[dynamic_one, dynamic_two]; + + assert_constant(true); + assert_constant(false); + + assert_constant(2 == 2); + assert_constant(1 + 1 == 2); + + // assert_constant doesn't check for true + assert_constant(1 + 1 == 3); + + let local_one = 1; + let local_two = 2; + let local_three = local_one + local_two; + let local_array_pair = [local_one, local_two]; + let local_slice_pair = &[local_one, local_two]; + + assert_constant(local_one); + assert_constant(local_three); + assert_constant(local_array_pair); + assert_constant(local_slice_pair); + + assert_constant(GLOBAL_ONE); + assert_constant(GLOBAL_THREE); + assert_constant(GLOBAL_ARRAY_PAIR); + assert_constant(GLOBAL_SLICE_PAIR); + + assert_constant([1, 2, dynamic_one].len() == 4); + + static_assert(dynamic_array_pair.len() == 2, ""); + static_assert(dynamic_slice_pair.len() == 2, ""); + + let foo = Foo { field: 0, array: [1, 2, 3], slice: &[] }; + assert_constant(foo); +} diff --git a/test_programs/compile_success_empty/macros/src/main.nr b/test_programs/compile_success_empty/macros/src/main.nr index 1b00a084c61..587c2c4c077 100644 --- a/test_programs/compile_success_empty/macros/src/main.nr +++ b/test_programs/compile_success_empty/macros/src/main.nr @@ -2,7 +2,9 @@ comptime fn my_macro(x: Field, y: Field) -> Quoted { // Current version of macros in Noir are not hygienic // so we can quote a and b here and expect them to resolve // to the a and b in main at the callsite of my_macro. - quote { $x + $y + a + b } + quote { + $x + $y + a + b + } } fn main() { diff --git a/test_programs/compile_success_empty/static_assert/Nargo.toml b/test_programs/compile_success_empty/static_assert/Nargo.toml new file mode 100644 index 00000000000..5dabd7803e3 --- /dev/null +++ b/test_programs/compile_success_empty/static_assert/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "static_assert" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/static_assert/src/main.nr b/test_programs/compile_success_empty/static_assert/src/main.nr new file mode 100644 index 00000000000..e61d9388ceb --- /dev/null +++ b/test_programs/compile_success_empty/static_assert/src/main.nr @@ -0,0 +1,46 @@ +use std::static_assert; + +global GLOBAL_ONE = 1; +global GLOBAL_TWO = 2; +global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; + +// contents known at compile time +// length known at compile time +global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; + +struct Foo { + field: Field, + array: [Field; 3], + slice: [Field], +} + +fn main( + dynamic_one: Field, // == 1 + dynamic_two: Field // == 2 +) { + // contents unknown at compile time + // length known at compile time + let dynamic_array_pair = [dynamic_one, dynamic_two]; + let dynamic_slice_pair = &[dynamic_one, dynamic_two]; + + static_assert(true, ""); + + static_assert(1 + 1 == 2, ""); + + let x = 2; + let y = 3; + let xy = x + y; + static_assert(xy == 5, ""); + + static_assert(3 == GLOBAL_THREE, ""); + + static_assert(GLOBAL_ARRAY_PAIR.len() == 2, ""); + static_assert(GLOBAL_SLICE_PAIR.len() == 2, ""); + + static_assert(dynamic_array_pair.len() == 2, ""); + static_assert(dynamic_slice_pair.len() == 2, ""); + + assert_constant([1, 2, dynamic_one].len() == 4); + static_assert([1, 2, dynamic_one].len() == 3, ""); +}