From a523278c404256c63731f6cff646c5c2a382e2d5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 17 Jan 2024 16:55:22 +0000 Subject: [PATCH 01/64] initial updates to make assert_message not a string literal --- .../src/brillig/brillig_gen/brillig_block.rs | 3 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 3 +- .../src/ssa/function_builder/mod.rs | 2 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 10 ++-- .../src/ssa/opt/fill_internal_slices.rs | 2 +- .../src/ssa/ssa_gen/context.rs | 14 ++++-- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 24 +++++++-- compiler/noirc_frontend/src/ast/statement.rs | 2 +- .../src/hir/resolution/resolver.rs | 6 ++- .../noirc_frontend/src/hir/type_check/stmt.rs | 7 ++- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- .../src/monomorphization/ast.rs | 2 +- .../src/monomorphization/mod.rs | 3 +- compiler/noirc_frontend/src/parser/parser.rs | 50 ++++++++++--------- .../execution_success/debug_logs/src/main.nr | 11 ++++ tooling/nargo/src/ops/foreign_calls.rs | 2 + 16 files changed, 98 insertions(+), 45 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index db005d9d438..7ac93da9691 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -265,7 +265,8 @@ impl<'block> BrilligBlock<'block> { condition, ); - self.brillig_context.constrain_instruction(condition, assert_message.clone()); + // TODO: add back assert message + self.brillig_context.constrain_instruction(condition, None); self.brillig_context.deallocate_register(condition); } Instruction::Allocate => { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index e65e71b045d..12b4e265aac 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -469,7 +469,8 @@ impl Context { for (lhs, rhs) in get_var_equality_assertions(lhs, rhs, &mut read_dynamic_array_index)? { - self.acir_context.assert_eq_var(lhs, rhs, assert_message.clone())?; + // TODO: add back assert message + self.acir_context.assert_eq_var(lhs, rhs, None)?; } } Instruction::Cast(value_id, _) => { diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 852848afb81..39bd8eb4303 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -250,7 +250,7 @@ impl FunctionBuilder { &mut self, lhs: ValueId, rhs: ValueId, - assert_message: Option, + assert_message: Option, ) { self.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message), None); } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 457fe41de93..bfda86ede25 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -151,7 +151,7 @@ pub(crate) enum Instruction { Truncate { value: ValueId, bit_size: u32, max_bit_size: u32 }, /// Constrains two values to be equal to one another. - Constrain(ValueId, ValueId, Option), + Constrain(ValueId, ValueId, Option), /// Range constrain `value` to `max_bit_size` RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, @@ -435,7 +435,7 @@ impl Instruction { } } Instruction::Constrain(lhs, rhs, msg) => { - let constraints = decompose_constrain(*lhs, *rhs, msg.clone(), dfg); + let constraints = decompose_constrain(*lhs, *rhs, *msg, dfg); if constraints.is_empty() { Remove } else { @@ -600,7 +600,7 @@ fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> Sim fn decompose_constrain( lhs: ValueId, rhs: ValueId, - msg: Option, + msg: Option, dfg: &mut DataFlowGraph, ) -> Vec { let lhs = dfg.resolve(lhs); @@ -657,7 +657,7 @@ fn decompose_constrain( let one = dfg.make_constant(one, Type::bool()); [ - decompose_constrain(lhs, one, msg.clone(), dfg), + decompose_constrain(lhs, one, msg, dfg), decompose_constrain(rhs, one, msg, dfg), ] .concat() @@ -685,7 +685,7 @@ fn decompose_constrain( let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); [ - decompose_constrain(lhs, zero, msg.clone(), dfg), + decompose_constrain(lhs, zero, msg, dfg), decompose_constrain(rhs, zero, msg, dfg), ] .concat() diff --git a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs index 5ee8e42fe3a..a0da32bd690 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs @@ -641,7 +641,7 @@ mod tests { // Every slice access checks against the dynamic slice length let slice_access_check = builder.insert_binary(main_v0, BinaryOp::Lt, two); let one = builder.field_constant(1_u128); - builder.insert_constrain(slice_access_check, one, Some("Index out of bounds".to_owned())); + builder.insert_constrain(slice_access_check, one, None); let field_element_type = Rc::new(vec![Type::field()]); let inner_slice_contents_type = Type::Slice(field_element_type); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index f1a2154d3a8..b15bb152876 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -431,10 +431,12 @@ impl<'a> FunctionContext<'a> { Type::unsigned(bit_size), ); let sign = self.builder.insert_binary(rhs, BinaryOp::Lt, half_width); + // TODO: bring back assert message + // Some("attempt to bit-shift with overflow".to_owned()) self.builder.set_location(location).insert_constrain( sign, one, - Some("attempt to bit-shift with overflow".to_string()), + None, ); } @@ -442,10 +444,12 @@ impl<'a> FunctionContext<'a> { .builder .numeric_constant(FieldElement::from(bit_size as i128), Type::unsigned(bit_size)); let overflow = self.builder.insert_binary(rhs, BinaryOp::Lt, max); + // TODO: bring back assert message + // Some("attempt to bit-shift with overflow".to_owned()) self.builder.set_location(location).insert_constrain( overflow, one, - Some("attempt to bit-shift with overflow".to_owned()), + None, ); self.builder.insert_truncate(result, bit_size, bit_size + 1) } @@ -498,8 +502,9 @@ impl<'a> FunctionContext<'a> { let sign_diff = self.builder.insert_binary(result_sign, BinaryOp::Eq, lhs_sign); let sign_diff_with_predicate = self.builder.insert_binary(sign_diff, BinaryOp::Mul, same_sign); + // TODO: bring back assert message let overflow_check = - Instruction::Constrain(sign_diff_with_predicate, same_sign, Some(message)); + Instruction::Constrain(sign_diff_with_predicate, same_sign, None); self.builder.set_location(location).insert_instruction(overflow_check, None); } BinaryOpKind::Multiply => { @@ -525,10 +530,11 @@ impl<'a> FunctionContext<'a> { self.builder.insert_binary(half_width, BinaryOp::Add, not_same_sign_field); let product_overflow_check = self.builder.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); + // TODO: bring back assert message self.builder.set_location(location).insert_constrain( product_overflow_check, one, - Some(message), + None, ); } _ => unreachable!("operator {} should not overflow", operator), diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index eb35ba9a65b..237bfe15708 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -141,7 +141,13 @@ impl<'a> FunctionContext<'a> { Expression::Call(call) => self.codegen_call(call), Expression::Let(let_expr) => self.codegen_let(let_expr), Expression::Constrain(expr, location, assert_message) => { - self.codegen_constrain(expr, *location, assert_message.clone()) + // let assert_message = assert_message.map(|expr| self.codegen_expression(expr.as_ref())?)?; + // let assert_message = if let Some(assert_message) = assert_message { + // Some(self.codegen_expression(assert_message.as_ref())?.into_leaf()) + // } else { + // None + // }; + self.codegen_constrain(expr, *location, assert_message) } Expression::Assign(assign) => self.codegen_assign(assign), Expression::Semi(semi) => self.codegen_semi(semi), @@ -438,10 +444,15 @@ impl<'a> FunctionContext<'a> { let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); let true_const = self.builder.numeric_constant(true, Type::bool()); + + // let x = self.builder.import_foreign_function("format"); + // let y = self.codegen_literal(&ast::Literal::Str("Index out of bounds".to_owned())); + let message = self.codegen_string("Index out of bounds").into_leaf().eval(self); + self.builder.insert_constrain( is_offset_out_of_bounds, true_const, - Some("Index out of bounds".to_owned()), + Some(message), ); } @@ -665,10 +676,17 @@ impl<'a> FunctionContext<'a> { &mut self, expr: &Expression, location: Location, - assert_message: Option, + assert_message: &Option>, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); + let assert_message = if let Some(assert_message) = assert_message { + Some(self.codegen_non_tuple_expression(assert_message.as_ref())?) + } else { + None + }; + // let assert_message_expr = assert_message.map(|expr| ) + // self.codegen_non_tuple_expression(expr) self.builder.set_location(location).insert_constrain(expr, true_literal, assert_message); Ok(Self::unit_value()) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 73b1f68778d..a2c29a8c52e 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -416,7 +416,7 @@ pub enum LValue { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct ConstrainStatement(pub Expression, pub Option, pub ConstrainKind); +pub struct ConstrainStatement(pub Expression, pub Option, pub ConstrainKind); #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum ConstrainKind { diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index bb7acf1037b..c1b76352ec5 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1110,7 +1110,11 @@ impl<'a> Resolver<'a> { } StatementKind::Constrain(constrain_stmt) => { let expr_id = self.resolve_expression(constrain_stmt.0); - let assert_message = constrain_stmt.1; + let assert_message = constrain_stmt.1.map(|expr| { + // dbg!(expr.clone()); + let resolved_expr = self.resolve_expression(expr); + resolved_expr + }); HirStatement::Constrain(HirConstrainStatement(expr_id, self.file, assert_message)) } StatementKind::Expression(expr) => { diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index c4e72f48c50..54f344bc496 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -304,7 +304,12 @@ impl<'interner> TypeChecker<'interner> { fn check_constrain_stmt(&mut self, stmt: HirConstrainStatement) { let expr_type = self.check_expression(&stmt.0); let expr_span = self.interner.expr_span(&stmt.0); - + if let Some(msg) = stmt.2 { + let hir_msg = self.interner.expression(&msg); + dbg!(hir_msg); + let checked_msg = self.check_expression(&msg); + dbg!(checked_msg.clone()); + } self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), expected_typ: Type::Bool.to_string(), diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 34c9302c251..4d946690151 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -55,7 +55,7 @@ pub struct HirAssignStatement { /// originates from. This is used later in the SSA pass to issue /// an error if a constrain is found to be always false. #[derive(Debug, Clone)] -pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); +pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); #[derive(Debug, Clone, Hash)] pub enum HirPattern { diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 42a618e7d77..5172eabde46 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -31,7 +31,7 @@ pub enum Expression { ExtractTupleField(Box, usize), Call(Call), Let(Let), - Constrain(Box, Location, Option), + Constrain(Box, Location, Option>), Assign(Assign), Semi(Box), } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index ac11e00ad20..ef9f2d85b13 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -479,7 +479,8 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Constrain(constrain) => { let expr = self.expr(constrain.0); let location = self.interner.expr_location(&constrain.0); - ast::Expression::Constrain(Box::new(expr), location, constrain.2) + let message_expr = constrain.2.map(|expr_id| Box::new(self.expr(expr_id))); + ast::Expression::Constrain(Box::new(expr), location, message_expr) } HirStatement::Assign(assign) => self.assign(assign), HirStatement::For(for_loop) => { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index cdfdc570949..0327268c593 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -845,19 +845,20 @@ where .labelled(ParsingRuleLabel::Statement) .validate(|expressions, span, emit| { let condition = expressions.get(0).unwrap_or(&Expression::error(span)).clone(); - let mut message_str = None; - - if let Some(message) = expressions.get(1) { - if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { - message_str = Some(message.clone()); - } else { - emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); - } - } - + // let mut message_str = None; + // if let Some(message) = expressions.get(1) { + // if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { + // message_str = Some(message.clone()); + // } + // else { + // dbg!(message.kind.clone()); + // emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); + // } + // } + let message = expressions.get(1).map(|expr| expr.clone()); StatementKind::Constrain(ConstrainStatement( condition, - message_str, + message, ConstrainKind::Assert, )) }) @@ -881,18 +882,19 @@ where })), span, ); - let mut message_str = None; - - if let Some(message) = exprs.get(2) { - if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { - message_str = Some(message.clone()); - } else { - emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); - } - } + // let mut message_str = None; + // if let Some(message) = exprs.get(2) { + // if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { + // message_str = Some(message.clone()); + // } else { + // emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); + // } + // } + + let message = exprs.get(1).map(|expr| expr.clone()); StatementKind::Constrain(ConstrainStatement( predicate, - message_str, + message, ConstrainKind::AssertEq, )) }) @@ -2103,7 +2105,8 @@ mod test { match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() { StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - assert_eq!(message, Some("assertion message".to_owned())); + // TODO: update this test + // assert_eq!(message, Some("assertion message".to_owned())); } _ => unreachable!(), } @@ -2127,7 +2130,8 @@ mod test { .unwrap() { StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - assert_eq!(message, Some("assertion message".to_owned())); + // TODO: update this test + // assert_eq!(message, Some("assertion message".to_owned())); } _ => unreachable!(), } diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index 6accdf725d9..b133457c7ec 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -3,6 +3,13 @@ use dep::std; fn main(x: Field, y: pub Field) { let string = "i: {i}, j: {j}"; std::println(string); + let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; + let fmt_fmt_str = f"fmtstr: {fmt_str}, i: {x}"; + std::println(fmt_fmt_str); + // assert_with_generic(x, string); + // This will just give None + // assert(x == y, string); + // A `fmtstr` lets you easily perform string interpolation. let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; let fmt_str = string_identity(fmt_str); @@ -42,6 +49,10 @@ fn main(x: Field, y: pub Field) { regression_2906(); } +// fn assert_with_generic(x: Field, message: str) { +// assert(x == 5, ""ge""); +// } + fn string_identity(string: fmtstr<14, (Field, Field)>) -> fmtstr<14, (Field, Field)> { string } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index cbe40c92b4e..81399af8554 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -140,6 +140,8 @@ impl DefaultForeignCallExecutor { .1 .try_into()?; print!("{display_values}{}", if skip_newline { "" } else { "\n" }); + + // let return_this = format!("{display_values}{}" if skip_newline { "" } else { "\n" }); Ok(()) } } From 84ec1bc8a6e910a2fd45a526f74d70fd240a2781 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 21:59:50 +0000 Subject: [PATCH 02/64] working resolution of assert messages and runtime --- Cargo.lock | 9 ++ Cargo.toml | 3 + acvm-repo/acir/src/circuit/mod.rs | 4 + acvm-repo/acvm/src/pwg/mod.rs | 59 +++++++++- acvm-repo/brillig/src/foreign_call.rs | 2 +- acvm-repo/brillig/src/value.rs | 6 ++ compiler/noirc_driver/Cargo.toml | 1 + compiler/noirc_driver/src/lib.rs | 2 +- .../brillig/brillig_gen/brillig_directive.rs | 22 +++- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 39 ++++--- .../ssa/acir_gen/acir_ir/generated_acir.rs | 4 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 4 +- .../src/ssa/function_builder/mod.rs | 2 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 10 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 2 +- .../src/ssa/opt/flatten_cfg.rs | 2 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 23 +--- .../src/hir/def_collector/dc_crate.rs | 28 +++++ .../src/hir/def_collector/dc_mod.rs | 4 +- .../noirc_frontend/src/hir/def_map/mod.rs | 3 +- .../src/hir/resolution/resolver.rs | 7 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 7 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- compiler/noirc_frontend/src/lib.rs | 2 +- .../src/monomorphization/ast.rs | 2 +- .../src/monomorphization/mod.rs | 8 +- compiler/noirc_frontend/src/parser/parser.rs | 16 ++- compiler/noirc_printable_type/src/lib.rs | 3 + noirc_macros/Cargo.toml | 14 +++ noirc_macros/src/lib.rs | 78 ++++++++++++++ .../execution_success/debug_logs/src/main.nr | 68 ++++++------ tooling/debugger/src/context.rs | 1 + tooling/nargo/src/ops/execute.rs | 102 ++++++++++++------ tooling/nargo/src/ops/foreign_calls.rs | 86 +++++++++++---- 34 files changed, 469 insertions(+), 156 deletions(-) create mode 100644 noirc_macros/Cargo.toml create mode 100644 noirc_macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1088bb63004..bb2cea9331f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2927,6 +2927,7 @@ dependencies = [ "noirc_errors", "noirc_evaluator", "noirc_frontend", + "noirc_macros", "rust-embed", "serde", "tracing", @@ -2986,6 +2987,14 @@ dependencies = [ "tracing", ] +[[package]] +name = "noirc_macros" +version = "0.22.0" +dependencies = [ + "iter-extended", + "noirc_frontend", +] + [[package]] name = "noirc_printable_type" version = "0.22.0" diff --git a/Cargo.toml b/Cargo.toml index 05139934c62..d017b450c61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [workspace] members = [ + # Macros crates for metaprogramming "aztec_macros", + "noirc_macros", + # Compiler crates "compiler/noirc_evaluator", "compiler/noirc_frontend", "compiler/noirc_errors", diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index b248b30b1d9..8053fd640cb 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -38,6 +38,10 @@ pub struct Circuit { // Note: This should be a BTreeMap, but serde-reflect is creating invalid // c++ code at the moment when it is, due to OpcodeLocation needing a comparison // implementation which is never generated. + // + // TODO: These are only used for constrains that are generating during compilation. + // TODO: We should move towards having all the checks being evaluated in the same manner + // TODO: as runtime assert messages specified by the user. pub assert_messages: Vec<(OpcodeLocation, String)>, } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 41b96572658..281a026d836 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use acir::{ - brillig::ForeignCallResult, + brillig::{ForeignCallResult, Value}, circuit::{opcodes::BlockId, Opcode, OpcodeLocation}, native_types::{Expression, Witness, WitnessMap}, BlackBoxFunc, FieldElement, @@ -203,6 +203,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { /// Sets the VM status to [ACVMStatus::Failure] using the provided `error`. /// Returns the new status. fn fail(&mut self, error: OpcodeResolutionError) -> ACVMStatus { + self.instruction_pointer += 1; self.status(ACVMStatus::Failure(error)) } @@ -224,13 +225,20 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { /// Resolves a foreign call's [result][acir::brillig_vm::ForeignCallResult] using a result calculated outside of the ACVM. /// /// The ACVM can then be restarted to solve the remaining Brillig VM process as well as the remaining ACIR opcodes. - pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult) { + pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ACVMForeignCallResult) { if !matches!(self.status, ACVMStatus::RequiresForeignCall(_)) { panic!("ACVM is not expecting a foreign call response as no call was made"); } let brillig_solver = self.brillig_solver.as_mut().expect("No active Brillig solver"); - brillig_solver.resolve_pending_foreign_call(foreign_call_result); + match foreign_call_result { + ACVMForeignCallResult::BrilligOutput(foreign_call_result) => { + brillig_solver.resolve_pending_foreign_call(foreign_call_result); + } + ACVMForeignCallResult::ResolvedAssertMessage(_) => { + brillig_solver.resolve_pending_foreign_call(ForeignCallResult::default()); + } + } // Now that the foreign call has been resolved then we can resume execution. self.status(ACVMStatus::InProgress); @@ -253,7 +261,9 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { let opcode = &self.opcodes[self.instruction_pointer]; let resolution = match opcode { - Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr), + Opcode::AssertZero(expr) => { + ExpressionSolver::solve(&mut self.witness_map, expr) + } Opcode::BlackBoxFuncCall(bb_func) => { blackbox::solve(self.backend, &mut self.witness_map, bb_func) } @@ -445,3 +455,44 @@ fn any_witness_from_expression(expr: &Expression) -> Option { Some(expr.linear_combinations[0].1) } } + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ACVMForeignCallResult { + BrilligOutput(ForeignCallResult), + ResolvedAssertMessage(String) +} + +impl ACVMForeignCallResult { + pub fn get_assert_message(self) -> Option { + match self { + Self::ResolvedAssertMessage(msg) => Some(msg), + _ => None, + } + } + + pub fn get_brillig_output(self) -> Option { + match self { + Self::BrilligOutput(foreign_call_result) => Some(foreign_call_result), + _ => None, + } + } +} + +impl From for ACVMForeignCallResult { + fn from(value: ForeignCallResult) -> Self { + Self::BrilligOutput(value) + } +} + +impl From for ACVMForeignCallResult { + fn from(value: String) -> Self { + Self::ResolvedAssertMessage(value) + } +} + +impl From for ACVMForeignCallResult { + fn from(value: Value) -> Self { + let foreign_call_result: ForeignCallResult = value.into(); + foreign_call_result.into() + } +} diff --git a/acvm-repo/brillig/src/foreign_call.rs b/acvm-repo/brillig/src/foreign_call.rs index 1359d7d604d..3f124a9a0a7 100644 --- a/acvm-repo/brillig/src/foreign_call.rs +++ b/acvm-repo/brillig/src/foreign_call.rs @@ -37,7 +37,7 @@ impl ForeignCallParam { } /// Represents the full output of a [foreign call][crate::Opcode::ForeignCall]. -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Default)] pub struct ForeignCallResult { /// Resolved output values of the foreign call. pub values: Vec, diff --git a/acvm-repo/brillig/src/value.rs b/acvm-repo/brillig/src/value.rs index 73a7d897eb7..3b71dd98839 100644 --- a/acvm-repo/brillig/src/value.rs +++ b/acvm-repo/brillig/src/value.rs @@ -48,6 +48,12 @@ impl From for Value { } } +impl From for Value { + fn from(value: u8) -> Self { + Value { inner: FieldElement::from(value as u128) } + } +} + impl From for Value { fn from(value: u128) -> Self { Value { inner: FieldElement::from(value) } diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index eb9650e8aec..d9b240101d8 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -25,3 +25,4 @@ rust-embed.workspace = true tracing.workspace = true aztec_macros = { path = "../../aztec_macros" } +noirc_macros = { path = "../../noirc_macros" } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index db69d41c704..672cc8ac41b 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -175,7 +175,7 @@ pub fn check_crate( let macros: Vec<&dyn MacroProcessor> = if disable_macros { vec![] } else { - vec![&aztec_macros::AztecMacro as &dyn MacroProcessor] + vec![&aztec_macros::AztecMacro as &dyn MacroProcessor, &noirc_macros::AssertMessageMacro as &dyn MacroProcessor] }; let mut errors = vec![]; diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index a07865073ff..e61410ac74c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -2,7 +2,7 @@ use acvm::acir::brillig::{ BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value, }; -use crate::brillig::brillig_ir::artifact::GeneratedBrillig; +use crate::{brillig::brillig_ir::artifact::GeneratedBrillig, ssa::ir::value::ValueId}; /// Generates brillig bytecode which computes the inverse of its input if not null, and zero else. pub(crate) fn directive_invert() -> GeneratedBrillig { @@ -87,3 +87,23 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { locations: Default::default(), } } + +pub(crate) fn directive_assert_message(inputs: &[ValueId]) -> GeneratedBrillig { + // let mut inputs = Vec::new(); + // for i in 0..num_inputs { + // inputs.push(RegisterOrMemory()) + // } + // let inputs = + GeneratedBrillig { + byte_code: vec![ + BrilligOpcode::ForeignCall { + function: "resolve_assert_message".to_owned(), + destinations: vec![], + inputs: vec![], + }, + BrilligOpcode::Stop, + ], + assert_messages: Default::default(), + locations: Default::default(), + } +} diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index cf7c6151110..9ef4e780577 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -6,7 +6,7 @@ use crate::ssa::acir_gen::{AcirDynamicArray, AcirValue}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; -use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; +use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs, Brillig}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; @@ -1321,21 +1321,7 @@ impl AcirContext { outputs: Vec, attempt_execution: bool, ) -> Result, InternalError> { - let b_inputs = try_vecmap(inputs, |i| match i { - AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), - AcirValue::Array(vars) => { - let mut var_expressions: Vec = Vec::new(); - for var in vars { - self.brillig_array_input(&mut var_expressions, var)?; - } - Ok(BrilligInputs::Array(var_expressions)) - } - AcirValue::DynamicArray(_) => { - let mut var_expressions = Vec::new(); - self.brillig_array_input(&mut var_expressions, i)?; - Ok(BrilligInputs::Array(var_expressions)) - } - })?; + let b_inputs = self.brillig_inputs(inputs)?; // Optimistically try executing the brillig now, if we can complete execution they just return the results. // This is a temporary measure pending SSA optimizations being applied to Brillig which would remove constant-input opcodes (See #2066) @@ -1371,6 +1357,27 @@ impl AcirContext { Ok(outputs_var) } + pub(crate) fn brillig_inputs( + &mut self, + inputs: Vec + ) -> Result, InternalError> { + try_vecmap(inputs, |i| match i { + AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), + AcirValue::Array(vars) => { + let mut var_expressions: Vec = Vec::new(); + for var in vars { + self.brillig_array_input(&mut var_expressions, var)?; + } + Ok(BrilligInputs::Array(var_expressions)) + } + AcirValue::DynamicArray(_) => { + let mut var_expressions = Vec::new(); + self.brillig_array_input(&mut var_expressions, i)?; + Ok(BrilligInputs::Array(var_expressions)) + } + }) + } + fn brillig_array_input( &mut self, var_expressions: &mut Vec, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index efc64c5286e..dc3639a256d 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -55,6 +55,8 @@ pub(crate) struct GeneratedAcir { pub(crate) call_stack: CallStack, /// Correspondence between an opcode index and the error message associated with it. + /// The error message is stored as a vector of witnesses which will only be evaluated + /// during execution and an assertion has been found to be unsatisfiable. pub(crate) assert_messages: BTreeMap, pub(crate) warnings: Vec, @@ -523,7 +525,7 @@ impl GeneratedAcir { for (brillig_index, message) in generated_brillig.assert_messages { self.assert_messages.insert( OpcodeLocation::Brillig { acir_index: self.opcodes.len() - 1, brillig_index }, - message, + message.clone(), ); } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 61566b79178..7d7d438ea3a 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -20,6 +20,7 @@ use super::{ }, ssa_gen::Ssa, }; +use crate::brillig::brillig_gen::brillig_directive::directive_assert_message; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; @@ -28,6 +29,7 @@ pub(crate) use acir_ir::generated_acir::GeneratedAcir; use acvm::acir::native_types::Witness; use acvm::acir::BlackBoxFunc; +use acvm::brillig_vm::brillig::{RegisterOrMemory, HeapArray}; use acvm::{ acir::{circuit::opcodes::BlockId, native_types::Expression}, FieldElement, @@ -470,7 +472,7 @@ impl Context { get_var_equality_assertions(lhs, rhs, &mut read_dynamic_array_index)? { // TODO: add back assert message - self.acir_context.assert_eq_var(lhs, rhs, None)?; + self.acir_context.assert_eq_var(lhs, rhs, assert_message.clone())?; } } Instruction::Cast(value_id, _) => { diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 39bd8eb4303..852848afb81 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -250,7 +250,7 @@ impl FunctionBuilder { &mut self, lhs: ValueId, rhs: ValueId, - assert_message: Option, + assert_message: Option, ) { self.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message), None); } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index bfda86ede25..457fe41de93 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -151,7 +151,7 @@ pub(crate) enum Instruction { Truncate { value: ValueId, bit_size: u32, max_bit_size: u32 }, /// Constrains two values to be equal to one another. - Constrain(ValueId, ValueId, Option), + Constrain(ValueId, ValueId, Option), /// Range constrain `value` to `max_bit_size` RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, @@ -435,7 +435,7 @@ impl Instruction { } } Instruction::Constrain(lhs, rhs, msg) => { - let constraints = decompose_constrain(*lhs, *rhs, *msg, dfg); + let constraints = decompose_constrain(*lhs, *rhs, msg.clone(), dfg); if constraints.is_empty() { Remove } else { @@ -600,7 +600,7 @@ fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> Sim fn decompose_constrain( lhs: ValueId, rhs: ValueId, - msg: Option, + msg: Option, dfg: &mut DataFlowGraph, ) -> Vec { let lhs = dfg.resolve(lhs); @@ -657,7 +657,7 @@ fn decompose_constrain( let one = dfg.make_constant(one, Type::bool()); [ - decompose_constrain(lhs, one, msg, dfg), + decompose_constrain(lhs, one, msg.clone(), dfg), decompose_constrain(rhs, one, msg, dfg), ] .concat() @@ -685,7 +685,7 @@ fn decompose_constrain( let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); [ - decompose_constrain(lhs, zero, msg, dfg), + decompose_constrain(lhs, zero, msg.clone(), dfg), decompose_constrain(rhs, zero, msg, dfg), ] .concat() diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 2899b987c1d..c87905e455b 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -146,7 +146,7 @@ pub(crate) fn display_instruction( writeln!(f, "truncate {value} to {bit_size} bits, max_bit_size: {max_bit_size}",) } Instruction::Constrain(lhs, rhs, message) => match message { - Some(message) => writeln!(f, "constrain {} == {} '{message}'", show(*lhs), show(*rhs)), + Some(message) => writeln!(f, "constrain {} == {} '{message:?}'", show(*lhs), show(*rhs)), None => writeln!(f, "constrain {} == {}", show(*lhs), show(*rhs)), }, Instruction::Call { func, arguments } => { diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 6bdf2ab1c0a..04677ab93ef 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -660,7 +660,7 @@ impl<'f> Context<'f> { Instruction::binary(BinaryOp::Mul, rhs, casted_condition), call_stack, ); - + dbg!(message.clone()); Instruction::Constrain(lhs, rhs, message) } Instruction::Store { address, value } => { diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 237bfe15708..48c3b39a1df 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -104,6 +104,7 @@ pub(crate) fn generate_ssa(program: Program) -> Result { while let Some((src_function_id, dest_id)) = context.pop_next_function_in_queue() { let function = &context.program[src_function_id]; function_context.new_function(dest_id, function); + // dbg!(function.name.clone()); function_context.codegen_function_body(&function.body)?; } // we save the data bus inside the dfg @@ -140,14 +141,8 @@ impl<'a> FunctionContext<'a> { } Expression::Call(call) => self.codegen_call(call), Expression::Let(let_expr) => self.codegen_let(let_expr), - Expression::Constrain(expr, location, assert_message) => { - // let assert_message = assert_message.map(|expr| self.codegen_expression(expr.as_ref())?)?; - // let assert_message = if let Some(assert_message) = assert_message { - // Some(self.codegen_expression(assert_message.as_ref())?.into_leaf()) - // } else { - // None - // }; - self.codegen_constrain(expr, *location, assert_message) + Expression::Constrain(expr, location) => { + self.codegen_constrain(expr, *location) } Expression::Assign(assign) => self.codegen_assign(assign), Expression::Semi(semi) => self.codegen_semi(semi), @@ -452,7 +447,7 @@ impl<'a> FunctionContext<'a> { self.builder.insert_constrain( is_offset_out_of_bounds, true_const, - Some(message), + Some("Index out of bounds".to_owned()), ); } @@ -676,18 +671,10 @@ impl<'a> FunctionContext<'a> { &mut self, expr: &Expression, location: Location, - assert_message: &Option>, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); - let assert_message = if let Some(assert_message) = assert_message { - Some(self.codegen_non_tuple_expression(assert_message.as_ref())?) - } else { - None - }; - // let assert_message_expr = assert_message.map(|expr| ) - // self.codegen_non_tuple_expression(expr) - self.builder.set_location(location).insert_constrain(expr, true_literal, assert_message); + self.builder.set_location(location).insert_constrain(expr, true_literal, None); Ok(Self::unit_value()) } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index c768ea96f8f..686cbee73e4 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -3,6 +3,8 @@ use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; +use crate::hir_def::expr::HirExpression; +use crate::hir_def::stmt::HirStatement; use crate::hir::resolution::import::{resolve_import, ImportDirective}; use crate::hir::resolution::resolver::Resolver; @@ -14,6 +16,7 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; +use crate::hir_def::stmt::HirConstrainStatement; use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; @@ -361,6 +364,31 @@ impl DefCollector { for macro_processor in macro_processors { macro_processor.process_typed_ast(&crate_id, context); } + + // TODO: started process for inserting call to `resolve_assert_message` on typed ast + // maybe it is better to do it on the untyped ast as we do not need type resolution + // for func_ids in file_func_ids.iter() { + // let function_body = context.def_interner.function(&func_ids.1); + // let function_body_id = function_body.as_expr(); + // match context.def_interner.expression(function_body_id) { + // HirExpression::Block(block_expr) => { + // let statements = block_expr.statements(); + // for stmt in statements.iter() { + // match context.def_interner.statement(stmt) { + // HirStatement::Constrain(constrain_stmt) => { + // if let Some(assert_msg_expr) = constrain_stmt.2 { + // let hir_expr = context.def_interner.expression(function_body_id); + // dbg!(hir_expr.clone()); + // } + // } + // _ => {} + // } + // } + // } + // _ => {} + // } + // } + errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); // Type check all of the functions in the crate diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 2e6eb3992ff..2612abfbe5f 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -10,7 +10,7 @@ use crate::{ node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, FunctionDefinition, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, - NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, + NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, UnresolvedGenerics, }; use super::{ @@ -220,7 +220,7 @@ impl<'a> ModCollector<'a> { let name = function.name_ident().clone(); let func_id = context.def_interner.push_empty_fn(); - + // First create dummy function in the DefInterner // So that we can get a FuncId let location = Location::new(function.span(), self.file_id); diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index d60ceffa9af..03cd307ebf7 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -1,3 +1,4 @@ +use crate::{Statement, StatementKind, Expression, ExpressionKind, Ident, Path, PathKind}; use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; @@ -7,7 +8,7 @@ use crate::parser::{parse_program, ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; use arena::{Arena, Index}; use fm::{FileId, FileManager}; -use noirc_errors::Location; +use noirc_errors::{Location, Span}; use std::collections::BTreeMap; mod module_def; pub use module_def::*; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 43081e21d7e..86b3b82b4da 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1103,12 +1103,7 @@ impl<'a> Resolver<'a> { } StatementKind::Constrain(constrain_stmt) => { let expr_id = self.resolve_expression(constrain_stmt.0); - let assert_message = constrain_stmt.1.map(|expr| { - // dbg!(expr.clone()); - let resolved_expr = self.resolve_expression(expr); - resolved_expr - }); - HirStatement::Constrain(HirConstrainStatement(expr_id, self.file, assert_message)) + HirStatement::Constrain(HirConstrainStatement(expr_id, self.file)) } StatementKind::Expression(expr) => { HirStatement::Expression(self.resolve_expression(expr)) diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 54f344bc496..c4e72f48c50 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -304,12 +304,7 @@ impl<'interner> TypeChecker<'interner> { fn check_constrain_stmt(&mut self, stmt: HirConstrainStatement) { let expr_type = self.check_expression(&stmt.0); let expr_span = self.interner.expr_span(&stmt.0); - if let Some(msg) = stmt.2 { - let hir_msg = self.interner.expression(&msg); - dbg!(hir_msg); - let checked_msg = self.check_expression(&msg); - dbg!(checked_msg.clone()); - } + self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), expected_typ: Type::Bool.to_string(), diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 4d946690151..f76e0e37a6c 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -55,7 +55,7 @@ pub struct HirAssignStatement { /// originates from. This is used later in the SSA pass to issue /// an error if a constrain is found to be always false. #[derive(Debug, Clone)] -pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); +pub struct HirConstrainStatement(pub ExprId, pub FileId); #[derive(Debug, Clone, Hash)] pub enum HirPattern { diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 9582b80dcba..6ba3d746a98 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -48,7 +48,7 @@ pub mod macros_api { pub use crate::hir_def::expr::{HirExpression, HirLiteral}; pub use crate::hir_def::stmt::HirStatement; pub use crate::node_interner::{NodeInterner, StructId}; - pub use crate::parser::SortedModule; + pub use crate::parser::{SortedModule, parse_program}; pub use crate::token::SecondaryAttribute; pub use crate::hir::def_map::ModuleDefId; diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 5172eabde46..782393ec4b2 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -31,7 +31,7 @@ pub enum Expression { ExtractTupleField(Box, usize), Call(Call), Let(Let), - Constrain(Box, Location, Option>), + Constrain(Box, Location), Assign(Assign), Semi(Box), } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index fb0f411f7ce..273bb1df42a 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -479,8 +479,7 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Constrain(constrain) => { let expr = self.expr(constrain.0); let location = self.interner.expr_location(&constrain.0); - let message_expr = constrain.2.map(|expr_id| Box::new(self.expr(expr_id))); - ast::Expression::Constrain(Box::new(expr), location, message_expr) + ast::Expression::Constrain(Box::new(expr), location) } HirStatement::Assign(assign) => self.assign(assign), HirStatement::For(for_loop) => { @@ -930,6 +929,9 @@ impl<'interner> Monomorphizer<'interner> { // The first argument to the `print` oracle is a bool, indicating a newline to be inserted at the end of the input // The second argument is expected to always be an ident self.append_printable_type_info(&hir_arguments[1], &mut arguments); + } else if name.as_str() == "assert_message" { + // The first argument to the `assert_message` oracle is the expression passed as a mesage to an `assert` or `assert_eq` statement + self.append_printable_type_info(&hir_arguments[0], &mut arguments); } } } @@ -1025,7 +1027,7 @@ impl<'interner> Monomorphizer<'interner> { // The caller needs information as to whether it is handling a format string or a single type arguments.push(ast::Expression::Literal(ast::Literal::Bool(is_fmt_str))); } - _ => unreachable!("logging expr {:?} is not supported", arguments[0]), + _ => unreachable!("logging expr {:?} is not supported", hir_argument), } } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 0327268c593..340015e7b9d 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -855,7 +855,19 @@ where // emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); // } // } - let message = expressions.get(1).map(|expr| expr.clone()); + let message = expressions.get(1).map(|expr| { + // let call_expr = Expression::call( + // Expression { kind: ExpressionKind::Variable(Path { + // segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], + // kind: PathKind::Dep, + // span: Span::default(), + // }), span }, + // vec![expr.clone()], + // span + // ); + // call_expr + expr.clone() + }); StatementKind::Constrain(ConstrainStatement( condition, message, @@ -891,7 +903,7 @@ where // } // } - let message = exprs.get(1).map(|expr| expr.clone()); + let message = exprs.get(2).map(|expr| expr.clone()); StatementKind::Constrain(ConstrainStatement( predicate, message, diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index 273e2d512ea..b42cf197d09 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -76,6 +76,9 @@ pub enum ForeignCallError { #[error("Failed calling external resolver. {0}")] ExternalResolverError(#[from] jsonrpc::Error), + + #[error("Assert message resolved after an unsatisified constrain. {0}")] + ResolvedAssertMessage(String) } impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { diff --git a/noirc_macros/Cargo.toml b/noirc_macros/Cargo.toml new file mode 100644 index 00000000000..699e6b01cae --- /dev/null +++ b/noirc_macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "noirc_macros" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +noirc_frontend.workspace = true +iter-extended.workspace = true \ No newline at end of file diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs new file mode 100644 index 00000000000..35191bb6993 --- /dev/null +++ b/noirc_macros/src/lib.rs @@ -0,0 +1,78 @@ +// use noirc_frontend::macros_api::{parse_program, SortedModule, CrateId + +use noirc_frontend::macros_api::parse_program; +use noirc_frontend::macros_api::{ + Expression, ExpressionKind, + HirContext, Ident, Path, PathKind, Span, + Statement, StatementKind, +}; +use noirc_frontend::macros_api::{CrateId, FileId}; +use noirc_frontend::macros_api::{MacroError, MacroProcessor}; +use noirc_frontend::macros_api::SortedModule; + + +pub struct AssertMessageMacro; + +impl MacroProcessor for AssertMessageMacro { + fn process_untyped_ast( + &self, + ast: SortedModule, + _crate_id: &CrateId, + _context: &HirContext, + ) -> Result { + transform(ast) + } + + // This macro does not need to process any information after name resolution + fn process_typed_ast(&self, _crate_id: &CrateId, _context: &mut HirContext) {} +} + +fn transform( + mut ast: SortedModule, +) -> Result { + let assert_message_oracles = "#[oracle(assert_message)] + unconstrained fn assert_message_oracle(_input: T) {} + unconstrained pub fn resolve_assert_message(input: T) { + assert_message_oracle(input); + }"; + // TODO: return parsing errors? + let (assert_msg_funcs_ast, _) = parse_program(assert_message_oracles); + let assert_msg_funcs_ast = assert_msg_funcs_ast.into_sorted(); + // TODO: first check whether we have any constrain exprs with messages in the first place + // other while we can skip this transform + for func in assert_msg_funcs_ast.functions { + ast.functions.push(func) + } + + for func in ast.functions.iter_mut() { + let mut calls_to_insert = Vec::new(); + for (i, stmt) in func.def.body.0.iter().enumerate() { + match stmt { + Statement { kind, span } => { + match kind { + StatementKind::Constrain(constrain_stmt) => { + if let Some(assert_msg_expr) = &constrain_stmt.1 { + let call_expr = Expression::call( + Expression { kind: ExpressionKind::Variable(Path { + segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], + kind: PathKind::Dep, + span: Span::default(), + }), span: Span::default() }, + vec![assert_msg_expr.clone()], + *span, + ); + calls_to_insert.push((i + calls_to_insert.len(), call_expr, *span)); + } + } + _ => {} + } + } + } + } + + for (i, call_expr, span) in calls_to_insert { + func.def.body.0.insert(i + 1, Statement { kind: StatementKind::Expression(call_expr), span }); + } + } + Ok(ast) +} \ No newline at end of file diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index b133457c7ec..72b2a9c8f4d 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -3,55 +3,61 @@ use dep::std; fn main(x: Field, y: pub Field) { let string = "i: {i}, j: {j}"; std::println(string); - let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; - let fmt_fmt_str = f"fmtstr: {fmt_str}, i: {x}"; - std::println(fmt_fmt_str); + + // TODO: fmtstr cannot be printed + // let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; + // let fmt_fmt_str = f"fmtstr: {fmt_str}, i: {x}"; + // std::println(fmt_fmt_str); // assert_with_generic(x, string); // This will just give None - // assert(x == y, string); + assert(x == y, string); // A `fmtstr` lets you easily perform string interpolation. - let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; - let fmt_str = string_identity(fmt_str); - std::println(fmt_str); + // let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; + + // TODO + // assert(x == y, fmt_str); + + // let fmt_str = string_identity(fmt_str); + // std::println(fmt_str); - let fmt_str_no_type = f"i: {x}, j: {y}"; - std::println(fmt_str_no_type); + // let fmt_str_no_type = f"i: {x}, j: {y}"; + // std::println(fmt_str_no_type); - let fmt_str_generic = string_with_generics(fmt_str_no_type); - std::println(fmt_str_generic); + // let fmt_str_generic = string_with_generics(fmt_str_no_type); + // std::println(fmt_str_generic); - let s = myStruct { y: x, x: y }; - std::println(s); + // let s = myStruct { y: x, x: y }; + // std::println(s); - std::println(f"randomstring{x}{x}"); + // std::println(f"randomstring{x}{x}"); - let fmt_str = string_with_partial_generics(f"i: {x}, s: {s}"); - std::println(fmt_str); + // let fmt_str = string_with_partial_generics(f"i: {x}, s: {s}"); + // std::println(fmt_str); - std::println(x); - std::println([x, y]); + // std::println(x); + // std::println([x, y]); - let foo = fooStruct { my_struct: s, foo: 15 }; - std::println(f"s: {s}, foo: {foo}"); + // let foo = fooStruct { my_struct: s, foo: 15 }; + // std::println(f"s: {s}, foo: {foo}"); - std::println(f"x: 0, y: 1"); + // std::println(f"x: 0, y: 1"); - let s_2 = myStruct { x: 20, y: 30 }; - std::println(f"s1: {s}, s2: {s_2}"); + // let s_2 = myStruct { x: 20, y: 30 }; + // std::println(f"s1: {s}, s2: {s_2}"); - let bar = fooStruct { my_struct: s_2, foo: 20 }; - std::println(f"foo1: {foo}, foo2: {bar}"); + // let bar = fooStruct { my_struct: s_2, foo: 20 }; + // std::println(f"foo1: {foo}, foo2: {bar}"); - let struct_string = if x != 5 { f"{foo}" } else { f"{bar}" }; - std::println(struct_string); + // let struct_string = if x != 5 { f"{foo}" } else { f"{bar}" }; + // std::println(struct_string); - regression_2906(); + // regression_2906(); } -// fn assert_with_generic(x: Field, message: str) { -// assert(x == 5, ""ge""); -// } +fn assert_with_generic(x: Field, message: str) { + assert(x == 5, message); +} fn string_identity(string: fmtstr<14, (Field, Field)>) -> fmtstr<14, (Field, Field)> { string diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 12b55708b15..e5c38806eb1 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -225,6 +225,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { + let foreign_call_result = foreign_call_result.get_brillig_output().expect("Debugger Error: Should only be attempting to execute foreign calls resolving back to Brillig"); solver.resolve_pending_foreign_call(foreign_call_result); self.brillig_solver = Some(solver); } else { diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 4fc7f7b599f..56ad6139066 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,6 +1,8 @@ +use acvm::acir::circuit::OpcodeLocation; use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use noirc_printable_type::ForeignCallError; use crate::errors::ExecutionError; use crate::NargoError; @@ -16,44 +18,80 @@ pub fn execute_circuit( ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); + let mut err: Option = None; loop { - let solver_status = acvm.solve(); - match solver_status { - ACVMStatus::Solved => break, - ACVMStatus::InProgress => { - unreachable!("Execution should not stop while in `InProgress` state.") - } - ACVMStatus::Failure(error) => { - let call_stack = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - Some(call_stack.clone()) - } - _ => None, - }; - - return Err(NargoError::ExecutionError(match call_stack { - Some(call_stack) => { - if let Some(assert_message) = circuit.get_assert_message( - *call_stack.last().expect("Call stacks should not be empty"), - ) { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else { - ExecutionError::SolvingError(error) + if let Some(error) = &err { + let solver_status = if acvm.instruction_pointer() < acvm.opcodes().len() { + acvm.solve_opcode() + } else { + return Err(resolve_comptime_assert_message(error, circuit)); + }; + match solver_status { + ACVMStatus::RequiresForeignCall(foreign_call) => { + let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; + + let assert_message = foreign_call_result.get_assert_message().expect("Only assert message resolution is supported for execution after an ACVM failure"); + let call_stack = resolve_call_stack(error); + + return Err(NargoError::ExecutionError(match call_stack { + Some(call_stack) => { + ExecutionError::AssertionFailed(assert_message, call_stack) } - } - None => ExecutionError::SolvingError(error), - })); + None => ExecutionError::SolvingError(error.clone()), + })); + } + _ => { + return Err(resolve_comptime_assert_message(error, circuit)) + } } - ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - acvm.resolve_pending_foreign_call(foreign_call_result); + } else { + let solver_status = acvm.solve(); + + match solver_status { + ACVMStatus::Solved => break, + ACVMStatus::InProgress => { + unreachable!("Execution should not stop while in `InProgress` state.") + } + ACVMStatus::Failure(error) => { + err = Some(error); + } + ACVMStatus::RequiresForeignCall(foreign_call) => { + let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; + acvm.resolve_pending_foreign_call(foreign_call_result); + } } - } + } } Ok(acvm.finalize()) } + +fn resolve_call_stack(error: &OpcodeResolutionError) -> Option> { + match error { + OpcodeResolutionError::UnsatisfiedConstrain { + opcode_location: ErrorLocation::Resolved(opcode_location), + } => Some(vec![*opcode_location]), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + Some(call_stack.clone()) + } + _ => None, + } +} + +fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circuit) -> NargoError { + let call_stack = resolve_call_stack(error); + + NargoError::ExecutionError(match call_stack { + Some(call_stack) => { + if let Some(assert_message) = circuit.get_assert_message( + *call_stack.last().expect("Call stacks should not be empty"), + ) { + ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) + } else { + ExecutionError::SolvingError(error.clone()) + } + } + None => ExecutionError::SolvingError(error.clone()), + }) +} diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 81399af8554..11aadf04983 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,6 +1,6 @@ use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, - pwg::ForeignCallWaitInfo, + pwg::{ForeignCallWaitInfo, ACVMForeignCallResult} }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; @@ -9,13 +9,33 @@ pub trait ForeignCallExecutor { fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, - ) -> Result; + ) -> Result; } +// #[derive(Debug, PartialEq, Eq, Clone)] +// pub(crate) enum CustomForeignCallResult { +// BrilligOutput(ForeignCallResult), +// ResolvedAssertMessage(String) +// } + +// impl From for CustomForeignCallResult { +// fn from(value: ForeignCallResult) -> Self { +// Self::BrilligOutput(value) +// } +// } + +// impl From for CustomForeignCallResult { +// fn from(value: String) -> Self { +// Self::ResolvedAssertMessage(value) +// } +// } + + /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. /// After resolution of a foreign call, nargo will restart execution of the ACVM pub(crate) enum ForeignCall { Print, + AssertMessage, CreateMock, SetMockParams, SetMockReturns, @@ -33,6 +53,7 @@ impl ForeignCall { pub(crate) fn name(&self) -> &'static str { match self { ForeignCall::Print => "print", + ForeignCall::AssertMessage => "assert_message", ForeignCall::CreateMock => "create_mock", ForeignCall::SetMockParams => "set_mock_params", ForeignCall::SetMockReturns => "set_mock_returns", @@ -44,6 +65,7 @@ impl ForeignCall { pub(crate) fn lookup(op_name: &str) -> Option { match op_name { "print" => Some(ForeignCall::Print), + "assert_message" => Some(ForeignCall::AssertMessage), "create_mock" => Some(ForeignCall::CreateMock), "set_mock_params" => Some(ForeignCall::SetMockParams), "set_mock_returns" => Some(ForeignCall::SetMockReturns), @@ -134,30 +156,55 @@ impl DefaultForeignCallExecutor { fn execute_print(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { let skip_newline = foreign_call_inputs[0].unwrap_value().is_zero(); - let display_values: PrintableValueDisplay = foreign_call_inputs + + let foreign_call_inputs = foreign_call_inputs .split_first() .ok_or(ForeignCallError::MissingForeignCallInputs)? - .1 - .try_into()?; - print!("{display_values}{}", if skip_newline { "" } else { "\n" }); + .1; + let display_string = Self::format_printable_value(foreign_call_inputs, skip_newline)?; + + print!("{display_string}"); - // let return_this = format!("{display_values}{}" if skip_newline { "" } else { "\n" }); Ok(()) } + + fn execute_assert_message(foreign_call_inputs: &[ForeignCallParam]) -> Result { + let display_string = Self::format_printable_value(foreign_call_inputs, true)?; + Ok(display_string.into()) + } + + fn format_printable_value(foreign_call_inputs: &[ForeignCallParam], skip_newline: bool) -> Result { + let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; + + let result = format!("{display_values}{}", if skip_newline { "" } else { "\n" }); + + Ok(result) + } } +// pub(crate) fn format_printable_value(foreign_call_inputs: &[ForeignCallParam], skip_newline: bool) -> Result { +// let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; + +// let result = format!("{display_values}{}", if skip_newline { "" } else { "\n" }); + +// Ok(result) +// } + impl ForeignCallExecutor for DefaultForeignCallExecutor { fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, - ) -> Result { + ) -> Result { let foreign_call_name = foreign_call.function.as_str(); match ForeignCall::lookup(foreign_call_name) { Some(ForeignCall::Print) => { if self.show_output { Self::execute_print(&foreign_call.inputs)?; } - Ok(ForeignCallResult { values: vec![] }) + Ok(ForeignCallResult { values: vec![] }.into()) + } + Some(ForeignCall::AssertMessage) => { + Self::execute_assert_message(&foreign_call.inputs) } Some(ForeignCall::CreateMock) => { let mock_oracle_name = Self::parse_string(&foreign_call.inputs[0]); @@ -166,7 +213,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { self.mocked_responses.push(MockedCall::new(id, mock_oracle_name)); self.last_mock_id += 1; - Ok(ForeignCallResult { values: vec![Value::from(id).into()] }) + Ok(ForeignCallResult { values: vec![Value::from(id).into()] }.into()) } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -174,7 +221,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .params = Some(params.to_vec()); - Ok(ForeignCallResult { values: vec![] }) + Ok(ForeignCallResult { values: vec![] }.into()) } Some(ForeignCall::SetMockReturns) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -182,7 +229,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .result = ForeignCallResult { values: params.to_vec() }; - Ok(ForeignCallResult { values: vec![] }) + Ok(ForeignCallResult { values: vec![] }.into()) } Some(ForeignCall::SetMockTimes) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -196,12 +243,12 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .times_left = Some(times); - Ok(ForeignCallResult { values: vec![] }) + Ok(ForeignCallResult { values: vec![] }.into()) } Some(ForeignCall::ClearMock) => { let (id, _) = Self::extract_mock_id(&foreign_call.inputs)?; self.mocked_responses.retain(|response| response.id != id); - Ok(ForeignCallResult { values: vec![] }) + Ok(ForeignCallResult { values: vec![] }.into()) } None => { let mock_response_position = self @@ -224,7 +271,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } } - Ok(ForeignCallResult { values: result }) + Ok(ForeignCallResult { values: result }.into()) } (None, Some(external_resolver)) => { let encoded_params: Vec<_> = @@ -237,7 +284,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { let parsed_response: ForeignCallResult = response.result()?; - Ok(parsed_response) + Ok(parsed_response.into()) } (None, None) => panic!("Unknown foreign call {}", foreign_call_name), } @@ -259,7 +306,7 @@ mod tests { use jsonrpc_http_server::{Server, ServerBuilder}; use serial_test::serial; - use crate::ops::{DefaultForeignCallExecutor, ForeignCallExecutor}; + use crate::ops::{DefaultForeignCallExecutor, ForeignCallExecutor, foreign_calls::ACVMForeignCallResult}; #[allow(unreachable_pub)] #[rpc] @@ -314,7 +361,7 @@ mod tests { }; let result = executor.execute(&foreign_call); - assert_eq!(result.unwrap(), ForeignCallResult { values: foreign_call.inputs }); + assert_eq!(result.unwrap(), ForeignCallResult { values: foreign_call.inputs }.into()); server.close(); } @@ -332,7 +379,8 @@ mod tests { }; let result = executor.execute(&foreign_call); - assert_eq!(result.unwrap(), Value::from(3_usize).into()); + let brillig_output: ForeignCallResult = Value::from(3_usize).into(); + assert_eq!(result.unwrap(), brillig_output.into()); server.close(); } From 0a084c45781a659bcca811f7918c70842fbf171a Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:01:02 +0000 Subject: [PATCH 03/64] remove old impl --- acvm-repo/brillig/src/value.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/acvm-repo/brillig/src/value.rs b/acvm-repo/brillig/src/value.rs index 3b71dd98839..73a7d897eb7 100644 --- a/acvm-repo/brillig/src/value.rs +++ b/acvm-repo/brillig/src/value.rs @@ -48,12 +48,6 @@ impl From for Value { } } -impl From for Value { - fn from(value: u8) -> Self { - Value { inner: FieldElement::from(value as u128) } - } -} - impl From for Value { fn from(value: u128) -> Self { Value { inner: FieldElement::from(value) } From c581ceefeba70d9a446104de61d7e9209eb58980 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:03:31 +0000 Subject: [PATCH 04/64] revert debugging changes for brillig inputs --- .../brillig/brillig_gen/brillig_directive.rs | 22 +---------- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 37 ++++++++----------- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index e61410ac74c..a07865073ff 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -2,7 +2,7 @@ use acvm::acir::brillig::{ BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value, }; -use crate::{brillig::brillig_ir::artifact::GeneratedBrillig, ssa::ir::value::ValueId}; +use crate::brillig::brillig_ir::artifact::GeneratedBrillig; /// Generates brillig bytecode which computes the inverse of its input if not null, and zero else. pub(crate) fn directive_invert() -> GeneratedBrillig { @@ -87,23 +87,3 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { locations: Default::default(), } } - -pub(crate) fn directive_assert_message(inputs: &[ValueId]) -> GeneratedBrillig { - // let mut inputs = Vec::new(); - // for i in 0..num_inputs { - // inputs.push(RegisterOrMemory()) - // } - // let inputs = - GeneratedBrillig { - byte_code: vec![ - BrilligOpcode::ForeignCall { - function: "resolve_assert_message".to_owned(), - destinations: vec![], - inputs: vec![], - }, - BrilligOpcode::Stop, - ], - assert_messages: Default::default(), - locations: Default::default(), - } -} diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 9ef4e780577..0aaa83ef7de 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1321,7 +1321,21 @@ impl AcirContext { outputs: Vec, attempt_execution: bool, ) -> Result, InternalError> { - let b_inputs = self.brillig_inputs(inputs)?; + let b_inputs = try_vecmap(inputs, |i| match i { + AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), + AcirValue::Array(vars) => { + let mut var_expressions: Vec = Vec::new(); + for var in vars { + self.brillig_array_input(&mut var_expressions, var)?; + } + Ok(BrilligInputs::Array(var_expressions)) + } + AcirValue::DynamicArray(_) => { + let mut var_expressions = Vec::new(); + self.brillig_array_input(&mut var_expressions, i)?; + Ok(BrilligInputs::Array(var_expressions)) + } + })?; // Optimistically try executing the brillig now, if we can complete execution they just return the results. // This is a temporary measure pending SSA optimizations being applied to Brillig which would remove constant-input opcodes (See #2066) @@ -1357,27 +1371,6 @@ impl AcirContext { Ok(outputs_var) } - pub(crate) fn brillig_inputs( - &mut self, - inputs: Vec - ) -> Result, InternalError> { - try_vecmap(inputs, |i| match i { - AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), - AcirValue::Array(vars) => { - let mut var_expressions: Vec = Vec::new(); - for var in vars { - self.brillig_array_input(&mut var_expressions, var)?; - } - Ok(BrilligInputs::Array(var_expressions)) - } - AcirValue::DynamicArray(_) => { - let mut var_expressions = Vec::new(); - self.brillig_array_input(&mut var_expressions, i)?; - Ok(BrilligInputs::Array(var_expressions)) - } - }) - } - fn brillig_array_input( &mut self, var_expressions: &mut Vec, From 73e48fbcf4f65e7d7afc93446bfd6bbd69c80a12 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:03:44 +0000 Subject: [PATCH 05/64] cargo fmt --- acvm-repo/acvm/src/pwg/mod.rs | 6 +-- compiler/noirc_driver/src/lib.rs | 5 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 2 +- .../ssa/acir_gen/acir_ir/generated_acir.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 2 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 4 +- .../src/ssa/ssa_gen/context.rs | 12 +---- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 4 +- .../src/hir/def_collector/dc_mod.rs | 2 +- .../noirc_frontend/src/hir/def_map/mod.rs | 2 +- compiler/noirc_frontend/src/lib.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 12 ++--- compiler/noirc_printable_type/src/lib.rs | 2 +- noirc_macros/src/lib.rs | 54 ++++++++++--------- tooling/nargo/src/ops/execute.rs | 17 +++--- tooling/nargo/src/ops/foreign_calls.rs | 34 ++++++------ 16 files changed, 75 insertions(+), 87 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 281a026d836..98d0c325d78 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -261,9 +261,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { let opcode = &self.opcodes[self.instruction_pointer]; let resolution = match opcode { - Opcode::AssertZero(expr) => { - ExpressionSolver::solve(&mut self.witness_map, expr) - } + Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr), Opcode::BlackBoxFuncCall(bb_func) => { blackbox::solve(self.backend, &mut self.witness_map, bb_func) } @@ -459,7 +457,7 @@ fn any_witness_from_expression(expr: &Expression) -> Option { #[derive(Debug, PartialEq, Eq, Clone)] pub enum ACVMForeignCallResult { BrilligOutput(ForeignCallResult), - ResolvedAssertMessage(String) + ResolvedAssertMessage(String), } impl ACVMForeignCallResult { diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 672cc8ac41b..23a3c739b80 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -175,7 +175,10 @@ pub fn check_crate( let macros: Vec<&dyn MacroProcessor> = if disable_macros { vec![] } else { - vec![&aztec_macros::AztecMacro as &dyn MacroProcessor, &noirc_macros::AssertMessageMacro as &dyn MacroProcessor] + vec![ + &aztec_macros::AztecMacro as &dyn MacroProcessor, + &noirc_macros::AssertMessageMacro as &dyn MacroProcessor, + ] }; let mut errors = vec![]; diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 0aaa83ef7de..4617254c8c4 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -6,7 +6,7 @@ use crate::ssa::acir_gen::{AcirDynamicArray, AcirValue}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; -use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs, Brillig}; +use acvm::acir::circuit::brillig::{Brillig, BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index dc3639a256d..d1565ca4304 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -55,7 +55,7 @@ pub(crate) struct GeneratedAcir { pub(crate) call_stack: CallStack, /// Correspondence between an opcode index and the error message associated with it. - /// The error message is stored as a vector of witnesses which will only be evaluated + /// The error message is stored as a vector of witnesses which will only be evaluated /// during execution and an assertion has been found to be unsatisfiable. pub(crate) assert_messages: BTreeMap, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 7d7d438ea3a..c7244d92a09 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -29,7 +29,7 @@ pub(crate) use acir_ir::generated_acir::GeneratedAcir; use acvm::acir::native_types::Witness; use acvm::acir::BlackBoxFunc; -use acvm::brillig_vm::brillig::{RegisterOrMemory, HeapArray}; +use acvm::brillig_vm::brillig::{HeapArray, RegisterOrMemory}; use acvm::{ acir::{circuit::opcodes::BlockId, native_types::Expression}, FieldElement, diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index c87905e455b..144cee30022 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -146,7 +146,9 @@ pub(crate) fn display_instruction( writeln!(f, "truncate {value} to {bit_size} bits, max_bit_size: {max_bit_size}",) } Instruction::Constrain(lhs, rhs, message) => match message { - Some(message) => writeln!(f, "constrain {} == {} '{message:?}'", show(*lhs), show(*rhs)), + Some(message) => { + writeln!(f, "constrain {} == {} '{message:?}'", show(*lhs), show(*rhs)) + } None => writeln!(f, "constrain {} == {}", show(*lhs), show(*rhs)), }, Instruction::Call { func, arguments } => { diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index b15bb152876..9e4293b1570 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -433,11 +433,7 @@ impl<'a> FunctionContext<'a> { let sign = self.builder.insert_binary(rhs, BinaryOp::Lt, half_width); // TODO: bring back assert message // Some("attempt to bit-shift with overflow".to_owned()) - self.builder.set_location(location).insert_constrain( - sign, - one, - None, - ); + self.builder.set_location(location).insert_constrain(sign, one, None); } let max = self @@ -446,11 +442,7 @@ impl<'a> FunctionContext<'a> { let overflow = self.builder.insert_binary(rhs, BinaryOp::Lt, max); // TODO: bring back assert message // Some("attempt to bit-shift with overflow".to_owned()) - self.builder.set_location(location).insert_constrain( - overflow, - one, - None, - ); + self.builder.set_location(location).insert_constrain(overflow, one, None); self.builder.insert_truncate(result, bit_size, bit_size + 1) } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 48c3b39a1df..aafc2538fcc 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -141,9 +141,7 @@ impl<'a> FunctionContext<'a> { } Expression::Call(call) => self.codegen_call(call), Expression::Let(let_expr) => self.codegen_let(let_expr), - Expression::Constrain(expr, location) => { - self.codegen_constrain(expr, *location) - } + Expression::Constrain(expr, location) => self.codegen_constrain(expr, *location), Expression::Assign(assign) => self.codegen_assign(assign), Expression::Semi(semi) => self.codegen_semi(semi), } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 2612abfbe5f..078b0028c21 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -220,7 +220,7 @@ impl<'a> ModCollector<'a> { let name = function.name_ident().clone(); let func_id = context.def_interner.push_empty_fn(); - + // First create dummy function in the DefInterner // So that we can get a FuncId let location = Location::new(function.span(), self.file_id); diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 03cd307ebf7..e5647b791f3 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -1,4 +1,3 @@ -use crate::{Statement, StatementKind, Expression, ExpressionKind, Ident, Path, PathKind}; use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; @@ -6,6 +5,7 @@ use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::{parse_program, ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; +use crate::{Expression, ExpressionKind, Ident, Path, PathKind, Statement, StatementKind}; use arena::{Arena, Index}; use fm::{FileId, FileManager}; use noirc_errors::{Location, Span}; diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 6ba3d746a98..d5ce902fdb6 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -48,7 +48,7 @@ pub mod macros_api { pub use crate::hir_def::expr::{HirExpression, HirLiteral}; pub use crate::hir_def::stmt::HirStatement; pub use crate::node_interner::{NodeInterner, StructId}; - pub use crate::parser::{SortedModule, parse_program}; + pub use crate::parser::{parse_program, SortedModule}; pub use crate::token::SecondaryAttribute; pub use crate::hir::def_map::ModuleDefId; diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 340015e7b9d..43d7ac5bb1f 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -849,7 +849,7 @@ where // if let Some(message) = expressions.get(1) { // if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { // message_str = Some(message.clone()); - // } + // } // else { // dbg!(message.kind.clone()); // emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); @@ -861,18 +861,14 @@ where // segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], // kind: PathKind::Dep, // span: Span::default(), - // }), span }, - // vec![expr.clone()], + // }), span }, + // vec![expr.clone()], // span // ); // call_expr expr.clone() }); - StatementKind::Constrain(ConstrainStatement( - condition, - message, - ConstrainKind::Assert, - )) + StatementKind::Constrain(ConstrainStatement(condition, message, ConstrainKind::Assert)) }) } diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index b42cf197d09..607c97894bf 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -78,7 +78,7 @@ pub enum ForeignCallError { ExternalResolverError(#[from] jsonrpc::Error), #[error("Assert message resolved after an unsatisified constrain. {0}")] - ResolvedAssertMessage(String) + ResolvedAssertMessage(String), } impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 35191bb6993..094737b7c36 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -1,16 +1,13 @@ // use noirc_frontend::macros_api::{parse_program, SortedModule, CrateId use noirc_frontend::macros_api::parse_program; +use noirc_frontend::macros_api::SortedModule; +use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{ - Expression, ExpressionKind, - HirContext, Ident, Path, PathKind, Span, - Statement, StatementKind, + Expression, ExpressionKind, HirContext, Ident, Path, PathKind, Span, Statement, StatementKind, }; -use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; -use noirc_frontend::macros_api::SortedModule; - pub struct AssertMessageMacro; impl MacroProcessor for AssertMessageMacro { @@ -27,9 +24,7 @@ impl MacroProcessor for AssertMessageMacro { fn process_typed_ast(&self, _crate_id: &CrateId, _context: &mut HirContext) {} } -fn transform( - mut ast: SortedModule, -) -> Result { +fn transform(mut ast: SortedModule) -> Result { let assert_message_oracles = "#[oracle(assert_message)] unconstrained fn assert_message_oracle(_input: T) {} unconstrained pub fn resolve_assert_message(input: T) { @@ -48,31 +43,38 @@ fn transform( let mut calls_to_insert = Vec::new(); for (i, stmt) in func.def.body.0.iter().enumerate() { match stmt { - Statement { kind, span } => { - match kind { - StatementKind::Constrain(constrain_stmt) => { - if let Some(assert_msg_expr) = &constrain_stmt.1 { - let call_expr = Expression::call( - Expression { kind: ExpressionKind::Variable(Path { - segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], + Statement { kind, span } => match kind { + StatementKind::Constrain(constrain_stmt) => { + if let Some(assert_msg_expr) = &constrain_stmt.1 { + let call_expr = Expression::call( + Expression { + kind: ExpressionKind::Variable(Path { + segments: vec![ + Ident::from("std"), + Ident::from("resolve_assert_message"), + ], kind: PathKind::Dep, span: Span::default(), - }), span: Span::default() }, - vec![assert_msg_expr.clone()], - *span, - ); - calls_to_insert.push((i + calls_to_insert.len(), call_expr, *span)); - } + }), + span: Span::default(), + }, + vec![assert_msg_expr.clone()], + *span, + ); + calls_to_insert.push((i + calls_to_insert.len(), call_expr, *span)); } - _ => {} } - } + _ => {} + }, } } for (i, call_expr, span) in calls_to_insert { - func.def.body.0.insert(i + 1, Statement { kind: StatementKind::Expression(call_expr), span }); + func.def + .body + .0 + .insert(i + 1, Statement { kind: StatementKind::Expression(call_expr), span }); } } Ok(ast) -} \ No newline at end of file +} diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 56ad6139066..85c9ce23c9b 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -20,7 +20,6 @@ pub fn execute_circuit( let mut err: Option = None; loop { - if let Some(error) = &err { let solver_status = if acvm.instruction_pointer() < acvm.opcodes().len() { acvm.solve_opcode() @@ -40,10 +39,8 @@ pub fn execute_circuit( } None => ExecutionError::SolvingError(error.clone()), })); - } - _ => { - return Err(resolve_comptime_assert_message(error, circuit)) } + _ => return Err(resolve_comptime_assert_message(error, circuit)), } } else { let solver_status = acvm.solve(); @@ -61,7 +58,7 @@ pub fn execute_circuit( acvm.resolve_pending_foreign_call(foreign_call_result); } } - } + } } Ok(acvm.finalize()) @@ -72,9 +69,7 @@ fn resolve_call_stack(error: &OpcodeResolutionError) -> Option Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - Some(call_stack.clone()) - } + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => Some(call_stack.clone()), _ => None, } } @@ -84,9 +79,9 @@ fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circ NargoError::ExecutionError(match call_stack { Some(call_stack) => { - if let Some(assert_message) = circuit.get_assert_message( - *call_stack.last().expect("Call stacks should not be empty"), - ) { + if let Some(assert_message) = circuit + .get_assert_message(*call_stack.last().expect("Call stacks should not be empty")) + { ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) } else { ExecutionError::SolvingError(error.clone()) diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 11aadf04983..35695de4767 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,6 +1,6 @@ use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, - pwg::{ForeignCallWaitInfo, ACVMForeignCallResult} + pwg::{ACVMForeignCallResult, ForeignCallWaitInfo}, }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; @@ -30,7 +30,6 @@ pub trait ForeignCallExecutor { // } // } - /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. /// After resolution of a foreign call, nargo will restart execution of the ACVM pub(crate) enum ForeignCall { @@ -157,10 +156,8 @@ impl DefaultForeignCallExecutor { fn execute_print(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { let skip_newline = foreign_call_inputs[0].unwrap_value().is_zero(); - let foreign_call_inputs = foreign_call_inputs - .split_first() - .ok_or(ForeignCallError::MissingForeignCallInputs)? - .1; + let foreign_call_inputs = + foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?.1; let display_string = Self::format_printable_value(foreign_call_inputs, skip_newline)?; print!("{display_string}"); @@ -168,17 +165,22 @@ impl DefaultForeignCallExecutor { Ok(()) } - fn execute_assert_message(foreign_call_inputs: &[ForeignCallParam]) -> Result { + fn execute_assert_message( + foreign_call_inputs: &[ForeignCallParam], + ) -> Result { let display_string = Self::format_printable_value(foreign_call_inputs, true)?; Ok(display_string.into()) } - fn format_printable_value(foreign_call_inputs: &[ForeignCallParam], skip_newline: bool) -> Result { + fn format_printable_value( + foreign_call_inputs: &[ForeignCallParam], + skip_newline: bool, + ) -> Result { let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; - + let result = format!("{display_values}{}", if skip_newline { "" } else { "\n" }); - - Ok(result) + + Ok(result) } } @@ -187,7 +189,7 @@ impl DefaultForeignCallExecutor { // let result = format!("{display_values}{}", if skip_newline { "" } else { "\n" }); -// Ok(result) +// Ok(result) // } impl ForeignCallExecutor for DefaultForeignCallExecutor { @@ -203,9 +205,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } Ok(ForeignCallResult { values: vec![] }.into()) } - Some(ForeignCall::AssertMessage) => { - Self::execute_assert_message(&foreign_call.inputs) - } + Some(ForeignCall::AssertMessage) => Self::execute_assert_message(&foreign_call.inputs), Some(ForeignCall::CreateMock) => { let mock_oracle_name = Self::parse_string(&foreign_call.inputs[0]); assert!(ForeignCall::lookup(&mock_oracle_name).is_none()); @@ -306,7 +306,9 @@ mod tests { use jsonrpc_http_server::{Server, ServerBuilder}; use serial_test::serial; - use crate::ops::{DefaultForeignCallExecutor, ForeignCallExecutor, foreign_calls::ACVMForeignCallResult}; + use crate::ops::{ + foreign_calls::ACVMForeignCallResult, DefaultForeignCallExecutor, ForeignCallExecutor, + }; #[allow(unreachable_pub)] #[rpc] From 0c463455016a7f3407b8d2eb17ad8a40a578866c Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:04:15 +0000 Subject: [PATCH 06/64] remove old ocmment --- .../noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index d1565ca4304..913b824a14c 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -55,8 +55,6 @@ pub(crate) struct GeneratedAcir { pub(crate) call_stack: CallStack, /// Correspondence between an opcode index and the error message associated with it. - /// The error message is stored as a vector of witnesses which will only be evaluated - /// during execution and an assertion has been found to be unsatisfiable. pub(crate) assert_messages: BTreeMap, pub(crate) warnings: Vec, From 1cf135d0eba0d6bbe8131bab1e013aa76d771852 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:12:51 +0000 Subject: [PATCH 07/64] cargo clippy and cleanup --- .../src/brillig/brillig_gen/brillig_block.rs | 3 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 3 -- .../src/ssa/ssa_gen/context.rs | 9 ++-- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 4 -- .../src/hir/def_collector/dc_crate.rs | 3 -- .../src/hir/def_collector/dc_mod.rs | 2 +- .../noirc_frontend/src/hir/def_map/mod.rs | 3 +- compiler/noirc_frontend/src/parser/parser.rs | 39 ++-------------- noirc_macros/src/lib.rs | 44 +++++++++---------- tooling/nargo/src/ops/execute.rs | 1 - 11 files changed, 31 insertions(+), 82 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 7ac93da9691..db005d9d438 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -265,8 +265,7 @@ impl<'block> BrilligBlock<'block> { condition, ); - // TODO: add back assert message - self.brillig_context.constrain_instruction(condition, None); + self.brillig_context.constrain_instruction(condition, assert_message.clone()); self.brillig_context.deallocate_register(condition); } Instruction::Allocate => { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 4617254c8c4..cf7c6151110 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -6,7 +6,7 @@ use crate::ssa::acir_gen::{AcirDynamicArray, AcirValue}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; -use acvm::acir::circuit::brillig::{Brillig, BrilligInputs, BrilligOutputs}; +use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index c7244d92a09..c0e3ed1ff66 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -20,7 +20,6 @@ use super::{ }, ssa_gen::Ssa, }; -use crate::brillig::brillig_gen::brillig_directive::directive_assert_message; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; @@ -29,7 +28,6 @@ pub(crate) use acir_ir::generated_acir::GeneratedAcir; use acvm::acir::native_types::Witness; use acvm::acir::BlackBoxFunc; -use acvm::brillig_vm::brillig::{HeapArray, RegisterOrMemory}; use acvm::{ acir::{circuit::opcodes::BlockId, native_types::Expression}, FieldElement, @@ -471,7 +469,6 @@ impl Context { for (lhs, rhs) in get_var_equality_assertions(lhs, rhs, &mut read_dynamic_array_index)? { - // TODO: add back assert message self.acir_context.assert_eq_var(lhs, rhs, assert_message.clone())?; } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 9e4293b1570..c1922ea6635 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -494,9 +494,8 @@ impl<'a> FunctionContext<'a> { let sign_diff = self.builder.insert_binary(result_sign, BinaryOp::Eq, lhs_sign); let sign_diff_with_predicate = self.builder.insert_binary(sign_diff, BinaryOp::Mul, same_sign); - // TODO: bring back assert message let overflow_check = - Instruction::Constrain(sign_diff_with_predicate, same_sign, None); + Instruction::Constrain(sign_diff_with_predicate, same_sign, Some(message)); self.builder.set_location(location).insert_instruction(overflow_check, None); } BinaryOpKind::Multiply => { @@ -506,11 +505,10 @@ impl<'a> FunctionContext<'a> { let rhs_abs = self.absolute_value_helper(rhs, rhs_sign, bit_size); let product_field = self.builder.insert_binary(lhs_abs, BinaryOp::Mul, rhs_abs); // It must not already overflow the bit_size - let message = "attempt to multiply with overflow".to_string(); self.builder.set_location(location).insert_range_check( product_field, bit_size, - Some(message.clone()), + Some("attempt to multiply with overflow".to_string()), ); let product = self.builder.insert_cast(product_field, Type::unsigned(bit_size)); @@ -522,11 +520,10 @@ impl<'a> FunctionContext<'a> { self.builder.insert_binary(half_width, BinaryOp::Add, not_same_sign_field); let product_overflow_check = self.builder.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); - // TODO: bring back assert message self.builder.set_location(location).insert_constrain( product_overflow_check, one, - None, + Some(message), ); } _ => unreachable!("operator {} should not overflow", operator), diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index aafc2538fcc..9fba708b9d9 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -438,10 +438,6 @@ impl<'a> FunctionContext<'a> { let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); let true_const = self.builder.numeric_constant(true, Type::bool()); - // let x = self.builder.import_foreign_function("format"); - // let y = self.codegen_literal(&ast::Literal::Str("Index out of bounds".to_owned())); - let message = self.codegen_string("Index out of bounds").into_leaf().eval(self); - self.builder.insert_constrain( is_offset_out_of_bounds, true_const, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 686cbee73e4..9d2179c78be 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -3,8 +3,6 @@ use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; -use crate::hir_def::expr::HirExpression; -use crate::hir_def::stmt::HirStatement; use crate::hir::resolution::import::{resolve_import, ImportDirective}; use crate::hir::resolution::resolver::Resolver; @@ -16,7 +14,6 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; -use crate::hir_def::stmt::HirConstrainStatement; use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 078b0028c21..2e6eb3992ff 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -10,7 +10,7 @@ use crate::{ node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, FunctionDefinition, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, - NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, UnresolvedGenerics, + NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, }; use super::{ diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index e5647b791f3..d60ceffa9af 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -5,10 +5,9 @@ use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::{parse_program, ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; -use crate::{Expression, ExpressionKind, Ident, Path, PathKind, Statement, StatementKind}; use arena::{Arena, Index}; use fm::{FileId, FileManager}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use std::collections::BTreeMap; mod module_def; pub use module_def::*; diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 43d7ac5bb1f..73060396aba 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -843,31 +843,9 @@ where ignore_then_commit(keyword(Keyword::Assert), parenthesized(argument_parser)) .labelled(ParsingRuleLabel::Statement) - .validate(|expressions, span, emit| { + .validate(|expressions, span, _| { let condition = expressions.get(0).unwrap_or(&Expression::error(span)).clone(); - // let mut message_str = None; - // if let Some(message) = expressions.get(1) { - // if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { - // message_str = Some(message.clone()); - // } - // else { - // dbg!(message.kind.clone()); - // emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); - // } - // } - let message = expressions.get(1).map(|expr| { - // let call_expr = Expression::call( - // Expression { kind: ExpressionKind::Variable(Path { - // segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], - // kind: PathKind::Dep, - // span: Span::default(), - // }), span }, - // vec![expr.clone()], - // span - // ); - // call_expr - expr.clone() - }); + let message = expressions.get(1).cloned(); StatementKind::Constrain(ConstrainStatement(condition, message, ConstrainKind::Assert)) }) } @@ -881,7 +859,7 @@ where ignore_then_commit(keyword(Keyword::AssertEq), parenthesized(argument_parser)) .labelled(ParsingRuleLabel::Statement) - .validate(|exprs: Vec, span, emit| { + .validate(|exprs: Vec, span, _| { let predicate = Expression::new( ExpressionKind::Infix(Box::new(InfixExpression { lhs: exprs.get(0).unwrap_or(&Expression::error(span)).clone(), @@ -890,16 +868,7 @@ where })), span, ); - // let mut message_str = None; - // if let Some(message) = exprs.get(2) { - // if let ExpressionKind::Literal(Literal::Str(message)) = &message.kind { - // message_str = Some(message.clone()); - // } else { - // emit(ParserError::with_reason(ParserErrorReason::AssertMessageNotString, span)); - // } - // } - - let message = exprs.get(2).map(|expr| expr.clone()); + let message = exprs.get(2).cloned(); StatementKind::Constrain(ConstrainStatement( predicate, message, diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 094737b7c36..4c671aa8713 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -42,30 +42,26 @@ fn transform(mut ast: SortedModule) -> Result match kind { - StatementKind::Constrain(constrain_stmt) => { - if let Some(assert_msg_expr) = &constrain_stmt.1 { - let call_expr = Expression::call( - Expression { - kind: ExpressionKind::Variable(Path { - segments: vec![ - Ident::from("std"), - Ident::from("resolve_assert_message"), - ], - kind: PathKind::Dep, - span: Span::default(), - }), - span: Span::default(), - }, - vec![assert_msg_expr.clone()], - *span, - ); - calls_to_insert.push((i + calls_to_insert.len(), call_expr, *span)); - } - } - _ => {} - }, + let Statement { kind, span } = stmt; + if let StatementKind::Constrain(constrain_stmt) = kind { + if let Some(assert_msg_expr) = &constrain_stmt.1 { + let call_expr = Expression::call( + Expression { + kind: ExpressionKind::Variable(Path { + segments: vec![ + Ident::from("std"), + Ident::from("resolve_assert_message"), + ], + kind: PathKind::Dep, + span: Span::default(), + }), + span: Span::default(), + }, + vec![assert_msg_expr.clone()], + *span, + ); + calls_to_insert.push((i + calls_to_insert.len(), call_expr, *span)); + } } } diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 85c9ce23c9b..5b7e4399f33 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -2,7 +2,6 @@ use acvm::acir::circuit::OpcodeLocation; use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; -use noirc_printable_type::ForeignCallError; use crate::errors::ExecutionError; use crate::NargoError; From 31232414900684398662a3ab259563eee9414337 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:30:47 +0000 Subject: [PATCH 08/64] cleanup old comments and initial debugging for brillig assert message --- .../brillig_assert/src/main.nr | 5 +++- tooling/nargo/src/ops/execute.rs | 6 ++++- tooling/nargo/src/ops/foreign_calls.rs | 26 ------------------- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/test_programs/execution_success/brillig_assert/src/main.nr b/test_programs/execution_success/brillig_assert/src/main.nr index 91e4cebd9d3..aeb07e68919 100644 --- a/test_programs/execution_success/brillig_assert/src/main.nr +++ b/test_programs/execution_success/brillig_assert/src/main.nr @@ -7,6 +7,9 @@ fn main(x: Field) { unconstrained fn conditional(x: bool) -> Field { assert(x, "x is false"); - assert_eq(x, true, "x is false"); + // TODO: this is not accurately printing the assert message + // got `Failed to solve brillig function, reason: explicit trap hit in brillig` + // expected `x is false` + assert_eq(x, false, "x is false"); 1 } diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 5b7e4399f33..cfb4e78b705 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -25,6 +25,7 @@ pub fn execute_circuit( } else { return Err(resolve_comptime_assert_message(error, circuit)); }; + dbg!(solver_status.clone()); match solver_status { ACVMStatus::RequiresForeignCall(foreign_call) => { let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; @@ -68,7 +69,10 @@ fn resolve_call_stack(error: &OpcodeResolutionError) -> Option Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => Some(call_stack.clone()), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + dbg!("got brillig func failed"); + Some(call_stack.clone()) + } _ => None, } } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 35695de4767..2b7ed7fb14c 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -12,24 +12,6 @@ pub trait ForeignCallExecutor { ) -> Result; } -// #[derive(Debug, PartialEq, Eq, Clone)] -// pub(crate) enum CustomForeignCallResult { -// BrilligOutput(ForeignCallResult), -// ResolvedAssertMessage(String) -// } - -// impl From for CustomForeignCallResult { -// fn from(value: ForeignCallResult) -> Self { -// Self::BrilligOutput(value) -// } -// } - -// impl From for CustomForeignCallResult { -// fn from(value: String) -> Self { -// Self::ResolvedAssertMessage(value) -// } -// } - /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. /// After resolution of a foreign call, nargo will restart execution of the ACVM pub(crate) enum ForeignCall { @@ -184,14 +166,6 @@ impl DefaultForeignCallExecutor { } } -// pub(crate) fn format_printable_value(foreign_call_inputs: &[ForeignCallParam], skip_newline: bool) -> Result { -// let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; - -// let result = format!("{display_values}{}", if skip_newline { "" } else { "\n" }); - -// Ok(result) -// } - impl ForeignCallExecutor for DefaultForeignCallExecutor { fn execute( &mut self, From 08d89224d0e44153c3e94aaed67c62bf66d8a9ec Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 19 Jan 2024 22:37:38 +0000 Subject: [PATCH 09/64] improved usage of From trait for ForeignCallResults --- acvm-repo/acvm/src/pwg/mod.rs | 16 +++++++++++++++- tooling/nargo/src/ops/foreign_calls.rs | 21 +++++++++------------ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 98d0c325d78..7a5e416e246 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use acir::{ - brillig::{ForeignCallResult, Value}, + brillig::{ForeignCallParam, ForeignCallResult, Value}, circuit::{opcodes::BlockId, Opcode, OpcodeLocation}, native_types::{Expression, Witness, WitnessMap}, BlackBoxFunc, FieldElement, @@ -494,3 +494,17 @@ impl From for ACVMForeignCallResult { foreign_call_result.into() } } + +impl From> for ACVMForeignCallResult { + fn from(values: Vec) -> Self { + let foreign_call_result: ForeignCallResult = values.into(); + foreign_call_result.into() + } +} + +impl From> for ACVMForeignCallResult { + fn from(values: Vec) -> Self { + let foreign_call_result: ForeignCallResult = values.into(); + foreign_call_result.into() + } +} diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 2b7ed7fb14c..31ffcbeeb13 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -177,7 +177,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { if self.show_output { Self::execute_print(&foreign_call.inputs)?; } - Ok(ForeignCallResult { values: vec![] }.into()) + Ok(ForeignCallResult::default().into()) } Some(ForeignCall::AssertMessage) => Self::execute_assert_message(&foreign_call.inputs), Some(ForeignCall::CreateMock) => { @@ -187,7 +187,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { self.mocked_responses.push(MockedCall::new(id, mock_oracle_name)); self.last_mock_id += 1; - Ok(ForeignCallResult { values: vec![Value::from(id).into()] }.into()) + Ok(vec![Value::from(id)].into()) } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -195,7 +195,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .params = Some(params.to_vec()); - Ok(ForeignCallResult { values: vec![] }.into()) + Ok(ForeignCallResult::default().into()) } Some(ForeignCall::SetMockReturns) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -203,7 +203,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .result = ForeignCallResult { values: params.to_vec() }; - Ok(ForeignCallResult { values: vec![] }.into()) + Ok(ForeignCallResult::default().into()) } Some(ForeignCall::SetMockTimes) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -217,12 +217,12 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .times_left = Some(times); - Ok(ForeignCallResult { values: vec![] }.into()) + Ok(ForeignCallResult::default().into()) } Some(ForeignCall::ClearMock) => { let (id, _) = Self::extract_mock_id(&foreign_call.inputs)?; self.mocked_responses.retain(|response| response.id != id); - Ok(ForeignCallResult { values: vec![] }.into()) + Ok(ForeignCallResult::default().into()) } None => { let mock_response_position = self @@ -245,7 +245,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } } - Ok(ForeignCallResult { values: result }.into()) + Ok(result.into()) } (None, Some(external_resolver)) => { let encoded_params: Vec<_> = @@ -280,9 +280,7 @@ mod tests { use jsonrpc_http_server::{Server, ServerBuilder}; use serial_test::serial; - use crate::ops::{ - foreign_calls::ACVMForeignCallResult, DefaultForeignCallExecutor, ForeignCallExecutor, - }; + use crate::ops::{DefaultForeignCallExecutor, ForeignCallExecutor}; #[allow(unreachable_pub)] #[rpc] @@ -355,8 +353,7 @@ mod tests { }; let result = executor.execute(&foreign_call); - let brillig_output: ForeignCallResult = Value::from(3_usize).into(); - assert_eq!(result.unwrap(), brillig_output.into()); + assert_eq!(result.unwrap(), Value::from(3_usize).into()); server.close(); } From 83605d89fb64dd6c91c2f095094952aa8e01d1b4 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 Jan 2024 19:00:09 +0000 Subject: [PATCH 10/64] start switching order of evaluation of resolve_assert_message as to not have to change the brillig VM too much --- acvm-repo/brillig_vm/src/lib.rs | 3 ++ noirc_macros/src/lib.rs | 21 ++++++-- tooling/nargo/src/ops/execute.rs | 70 ++++++++++++++++++++++---- tooling/nargo/src/ops/foreign_calls.rs | 7 +++ 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index df4c8135bce..4446b9a49bf 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -143,6 +143,9 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Indicating that the VM encountered a `Trap` Opcode /// or an invalid state. fn fail(&mut self, message: String) -> VMStatus { + dbg!(self.program_counter); + dbg!(&self.bytecode[self.program_counter]); + dbg!(&self.bytecode[self.program_counter - 1]); let mut error_stack: Vec<_> = self.call_stack.iter().map(|value| value.to_usize()).collect(); error_stack.push(self.program_counter); diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 4c671aa8713..c2b8bdb196d 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -25,7 +25,8 @@ impl MacroProcessor for AssertMessageMacro { } fn transform(mut ast: SortedModule) -> Result { - let assert_message_oracles = "#[oracle(assert_message)] + let assert_message_oracles = " + #[oracle(assert_message)] unconstrained fn assert_message_oracle(_input: T) {} unconstrained pub fn resolve_assert_message(input: T) { assert_message_oracle(input); @@ -41,6 +42,7 @@ fn transform(mut ast: SortedModule) -> Result Result( @@ -20,21 +20,44 @@ pub fn execute_circuit( let mut err: Option = None; loop { if let Some(error) = &err { - let solver_status = if acvm.instruction_pointer() < acvm.opcodes().len() { + // If there are two assertions in a row and the second one is false we could hit + // a failure status that will resolve a comptime assert message rather than a runtime assert + // message as we are expecting. + // If there is a Brillig assertion we are just going to process the next Brillig func rather than + // + dbg!("got err"); + // dbg!(&acvm.opcodes()[acvm.instruction_pointer()]); + // dbg!(&acvm.opcodes()[acvm.instruction_pointer() - 1]); + // dbg!(&acvm.opcodes()[acvm.instruction_pointer() - 2]); + + let call_stack = resolve_call_stack(error); + + // Consrtuct error + match call_stack.clone() { + Some((call_stack, is_brillig_fail)) => { + if is_brillig_fail { + dbg!("got brillig fail"); + let x = acvm.step_into_brillig_opcode(); + } + } + None => { + } + } + + let solver_status = if can_process_opcode_after_failure(&acvm) { acvm.solve_opcode() } else { return Err(resolve_comptime_assert_message(error, circuit)); }; - dbg!(solver_status.clone()); + // dbg!(solver_status.clone()); match solver_status { ACVMStatus::RequiresForeignCall(foreign_call) => { let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; let assert_message = foreign_call_result.get_assert_message().expect("Only assert message resolution is supported for execution after an ACVM failure"); - let call_stack = resolve_call_stack(error); return Err(NargoError::ExecutionError(match call_stack { - Some(call_stack) => { + Some((call_stack, is_brillig_fail)) => { ExecutionError::AssertionFailed(assert_message, call_stack) } None => ExecutionError::SolvingError(error.clone()), @@ -54,6 +77,7 @@ pub fn execute_circuit( err = Some(error); } ACVMStatus::RequiresForeignCall(foreign_call) => { + // dbg!(foreign_call.clone()); let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; acvm.resolve_pending_foreign_call(foreign_call_result); } @@ -64,24 +88,24 @@ pub fn execute_circuit( Ok(acvm.finalize()) } -fn resolve_call_stack(error: &OpcodeResolutionError) -> Option> { +fn resolve_call_stack(error: &OpcodeResolutionError) -> Option<(Vec, bool)> { match error { OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some(vec![*opcode_location]), + } => Some((vec![*opcode_location], false)), OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { dbg!("got brillig func failed"); - Some(call_stack.clone()) + Some((call_stack.clone(), true)) } _ => None, } } fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circuit) -> NargoError { - let call_stack = resolve_call_stack(error); + let call_stack= resolve_call_stack(error); NargoError::ExecutionError(match call_stack { - Some(call_stack) => { + Some((call_stack, _)) => { if let Some(assert_message) = circuit .get_assert_message(*call_stack.last().expect("Call stacks should not be empty")) { @@ -93,3 +117,27 @@ fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circ None => ExecutionError::SolvingError(error.clone()), }) } + +fn can_process_opcode_after_failure<'a, B: BlackBoxFunctionSolver>(acvm: &ACVM<'a, B>) -> bool { + if acvm.instruction_pointer() >= acvm.opcodes().len() { + return false; + } + if let Opcode::Brillig(brillig) = &acvm.opcodes()[acvm.instruction_pointer()] { + // We do not want + match &brillig.bytecode[brillig.bytecode.len() - 2] { + acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. } => { + ForeignCall::execution_allowed_after_failure(function) + } + _ => false, + } + // if matches!(&brillig.bytecode[brillig.bytecode.len() - 2], acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. }) { + // // if function + // // if function + // true + // } else { + // false + // } + } else { + false + } +} diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 31ffcbeeb13..9a8c0cf60e5 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -55,6 +55,13 @@ impl ForeignCall { _ => None, } } + + pub(crate) fn execution_allowed_after_failure(op_name: &str) -> bool { + match op_name { + "assert_message" => true, + _ => true, + } + } } /// This struct represents an oracle mock. It can be used for testing programs that use oracles. From f131b96429669ac7f2d180ea657e1d35086176c0 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 20:06:47 +0000 Subject: [PATCH 11/64] switch order of execution --- Cargo.lock | 2 +- acvm-repo/acvm/src/pwg/mod.rs | 18 ++- acvm-repo/brillig_vm/src/lib.rs | 3 - compiler/noirc_evaluator/src/ssa.rs | 1 - noirc_macros/src/lib.rs | 34 +++-- .../brillig_assert/src/main.nr | 4 +- .../execution_success/debug_logs/src/main.nr | 3 - tooling/nargo/src/ops/execute.rs | 130 +++++++----------- 8 files changed, 85 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fc487e2e02..1585d81d6e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2996,7 +2996,7 @@ dependencies = [ [[package]] name = "noirc_macros" -version = "0.22.0" +version = "0.23.0" dependencies = [ "iter-extended", "noirc_frontend", diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 7a5e416e246..2b17b6983ec 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -140,6 +140,8 @@ pub struct ACVM<'a, B: BlackBoxFunctionSolver> { witness_map: WitnessMap, brillig_solver: Option>, + + current_assert_message: Option, } impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { @@ -153,6 +155,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { instruction_pointer: 0, witness_map: initial_witness, brillig_solver: None, + current_assert_message: None, } } @@ -203,7 +206,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { /// Sets the VM status to [ACVMStatus::Failure] using the provided `error`. /// Returns the new status. fn fail(&mut self, error: OpcodeResolutionError) -> ACVMStatus { - self.instruction_pointer += 1; + // self.instruction_pointer += 1; self.status(ACVMStatus::Failure(error)) } @@ -235,7 +238,12 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { ACVMForeignCallResult::BrilligOutput(foreign_call_result) => { brillig_solver.resolve_pending_foreign_call(foreign_call_result); } - ACVMForeignCallResult::ResolvedAssertMessage(_) => { + ACVMForeignCallResult::ResolvedAssertMessage(assert_message) => { + if assert_message.is_empty() { + self.current_assert_message = None; + } else { + self.current_assert_message = Some(assert_message); + } brillig_solver.resolve_pending_foreign_call(ForeignCallResult::default()); } } @@ -259,7 +267,6 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { pub fn solve_opcode(&mut self) -> ACVMStatus { let opcode = &self.opcodes[self.instruction_pointer]; - let resolution = match opcode { Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr), Opcode::BlackBoxFuncCall(bb_func) => { @@ -296,6 +303,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { } } Err(mut error) => { + dbg!("got error"); match &mut error { // If we have an index out of bounds or an unsatisfied constraint, the opcode label will be unresolved // because the solvers do not have knowledge of this information. @@ -384,6 +392,10 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { self.brillig_solver = Some(solver); self.solve_opcode() } + + pub fn get_assert_message(&self) -> &Option { + &self.current_assert_message + } } // Returns the concrete value for a particular witness diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 4446b9a49bf..df4c8135bce 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -143,9 +143,6 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Indicating that the VM encountered a `Trap` Opcode /// or an invalid state. fn fail(&mut self, message: String) -> VMStatus { - dbg!(self.program_counter); - dbg!(&self.bytecode[self.program_counter]); - dbg!(&self.bytecode[self.program_counter - 1]); let mut error_stack: Vec<_> = self.call_stack.iter().map(|value| value.to_usize()).collect(); error_stack.push(self.program_counter); diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index e2da5652faf..0e3076923e0 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -63,7 +63,6 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") - .run_pass(Ssa::bubble_up_constrains, "After Constraint Bubbling:") .finish(); let brillig = ssa.to_brillig(print_brillig_trace); diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index c2b8bdb196d..349a7fa1892 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -5,6 +5,7 @@ use noirc_frontend::macros_api::SortedModule; use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{ Expression, ExpressionKind, HirContext, Ident, Path, PathKind, Span, Statement, StatementKind, + Literal, }; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; @@ -42,7 +43,6 @@ fn transform(mut ast: SortedModule) -> Result Result Field { // TODO: this is not accurately printing the assert message // got `Failed to solve brillig function, reason: explicit trap hit in brillig` // expected `x is false` - assert_eq(x, false, "x is false"); + assert_eq(x, true, "x is blah blah"); + let z = x as u8 + 20; + assert_eq(z, 25); 1 } diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index b733de44983..90db7578411 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -12,9 +12,6 @@ fn main(x: Field, y: pub Field) { // A `fmtstr` lets you easily perform string interpolation. let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; - // TODO - // assert(x == y, fmt_str); - let fmt_str = string_identity(fmt_str); std::println(fmt_str); diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 685bba714f7..ec7a45874e0 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,12 +1,12 @@ -use acvm::acir::circuit::{OpcodeLocation, Opcode}; -use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; +use acvm::acir::circuit::OpcodeLocation; +use acvm::pwg::{ACVMForeignCallResult, ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use crate::errors::ExecutionError; use crate::NargoError; -use super::foreign_calls::{ForeignCallExecutor, ForeignCall}; +use super::foreign_calls::ForeignCallExecutor; #[tracing::instrument(level = "trace", skip_all)] pub fn execute_circuit( @@ -17,70 +17,39 @@ pub fn execute_circuit( ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - let mut err: Option = None; loop { - if let Some(error) = &err { - // If there are two assertions in a row and the second one is false we could hit - // a failure status that will resolve a comptime assert message rather than a runtime assert - // message as we are expecting. - // If there is a Brillig assertion we are just going to process the next Brillig func rather than - // - dbg!("got err"); - // dbg!(&acvm.opcodes()[acvm.instruction_pointer()]); - // dbg!(&acvm.opcodes()[acvm.instruction_pointer() - 1]); - // dbg!(&acvm.opcodes()[acvm.instruction_pointer() - 2]); + let solver_status = acvm.solve(); - let call_stack = resolve_call_stack(error); - - // Consrtuct error - match call_stack.clone() { - Some((call_stack, is_brillig_fail)) => { - if is_brillig_fail { - dbg!("got brillig fail"); - let x = acvm.step_into_brillig_opcode(); - } - } - None => { - } + match solver_status { + ACVMStatus::Solved => break, + ACVMStatus::InProgress => { + unreachable!("Execution should not stop while in `InProgress` state.") } + ACVMStatus::Failure(error) => { + let call_stack = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { + opcode_location: ErrorLocation::Resolved(opcode_location), + } => Some(vec![*opcode_location]), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + Some(call_stack.clone()) + } + _ => None, + }; - let solver_status = if can_process_opcode_after_failure(&acvm) { - acvm.solve_opcode() - } else { - return Err(resolve_comptime_assert_message(error, circuit)); - }; - // dbg!(solver_status.clone()); - match solver_status { - ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - - let assert_message = foreign_call_result.get_assert_message().expect("Only assert message resolution is supported for execution after an ACVM failure"); - - return Err(NargoError::ExecutionError(match call_stack { - Some((call_stack, is_brillig_fail)) => { - ExecutionError::AssertionFailed(assert_message, call_stack) + return Err(NargoError::ExecutionError(match call_stack { + Some(call_stack) => { + if let Some(assert_message) = acvm.get_assert_message() { + ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) + } else { + ExecutionError::SolvingError(error) } - None => ExecutionError::SolvingError(error.clone()), - })); - } - _ => return Err(resolve_comptime_assert_message(error, circuit)), + } + None => ExecutionError::SolvingError(error), + })); } - } else { - let solver_status = acvm.solve(); - - match solver_status { - ACVMStatus::Solved => break, - ACVMStatus::InProgress => { - unreachable!("Execution should not stop while in `InProgress` state.") - } - ACVMStatus::Failure(error) => { - err = Some(error); - } - ACVMStatus::RequiresForeignCall(foreign_call) => { - // dbg!(foreign_call.clone()); - let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - acvm.resolve_pending_foreign_call(foreign_call_result); - } + ACVMStatus::RequiresForeignCall(foreign_call) => { + let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; + acvm.resolve_pending_foreign_call(foreign_call_result); } } } @@ -118,26 +87,19 @@ fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circ }) } -fn can_process_opcode_after_failure<'a, B: BlackBoxFunctionSolver>(acvm: &ACVM<'a, B>) -> bool { - if acvm.instruction_pointer() >= acvm.opcodes().len() { - return false; - } - if let Opcode::Brillig(brillig) = &acvm.opcodes()[acvm.instruction_pointer()] { - // We do not want - match &brillig.bytecode[brillig.bytecode.len() - 2] { - acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. } => { - ForeignCall::execution_allowed_after_failure(function) - } - _ => false, - } - // if matches!(&brillig.bytecode[brillig.bytecode.len() - 2], acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. }) { - // // if function - // // if function - // true - // } else { - // false - // } - } else { - false - } -} +// fn can_process_opcode_after_failure<'a, B: BlackBoxFunctionSolver>(acvm: &ACVM<'a, B>) -> bool { +// if acvm.instruction_pointer() >= acvm.opcodes().len() { +// return false; +// } +// if let Opcode::Brillig(brillig) = &acvm.opcodes()[acvm.instruction_pointer()] { +// // We do not want +// match &brillig.bytecode[brillig.bytecode.len() - 2] { +// acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. } => { +// ForeignCall::execution_allowed_after_failure(function) +// } +// _ => false, +// } +// } else { +// false +// } +// } From f253cd252c70dc2d565d446a231d5c61817a3f40 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 20:50:16 +0000 Subject: [PATCH 12/64] update parser tests and some cleanup --- acvm-repo/acvm/src/pwg/mod.rs | 1 - .../ssa/acir_gen/acir_ir/generated_acir.rs | 2 +- .../src/ssa/opt/flatten_cfg.rs | 2 +- .../src/ssa/ssa_gen/context.rs | 16 ++++++++----- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 3 ++- .../src/hir/def_collector/dc_crate.rs | 24 ------------------- compiler/noirc_frontend/src/parser/parser.rs | 18 ++++++++++---- noirc_macros/src/lib.rs | 6 ++--- tooling/nargo/src/ops/execute.rs | 4 ++-- 9 files changed, 33 insertions(+), 43 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 2b17b6983ec..0b78d5649b1 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -303,7 +303,6 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { } } Err(mut error) => { - dbg!("got error"); match &mut error { // If we have an index out of bounds or an unsatisfied constraint, the opcode label will be unresolved // because the solvers do not have knowledge of this information. diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 913b824a14c..efc64c5286e 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -523,7 +523,7 @@ impl GeneratedAcir { for (brillig_index, message) in generated_brillig.assert_messages { self.assert_messages.insert( OpcodeLocation::Brillig { acir_index: self.opcodes.len() - 1, brillig_index }, - message.clone(), + message, ); } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 04677ab93ef..6bdf2ab1c0a 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -660,7 +660,7 @@ impl<'f> Context<'f> { Instruction::binary(BinaryOp::Mul, rhs, casted_condition), call_stack, ); - dbg!(message.clone()); + Instruction::Constrain(lhs, rhs, message) } Instruction::Store { address, value } => { diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index ee3e79c76cb..3c05f52ad50 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -432,18 +432,22 @@ impl<'a> FunctionContext<'a> { Type::unsigned(bit_size), ); let sign = self.builder.insert_binary(rhs, BinaryOp::Lt, half_width); - // TODO: bring back assert message - // Some("attempt to bit-shift with overflow".to_owned()) - self.builder.set_location(location).insert_constrain(sign, one, None); + self.builder.set_location(location).insert_constrain( + sign, + one, + Some("attempt to bit-shift with overflow".to_owned()), + ); } let max = self .builder .numeric_constant(FieldElement::from(bit_size as i128), Type::unsigned(bit_size)); let overflow = self.builder.insert_binary(rhs, BinaryOp::Lt, max); - // TODO: bring back assert message - // Some("attempt to bit-shift with overflow".to_owned()) - self.builder.set_location(location).insert_constrain(overflow, one, None); + self.builder.set_location(location).insert_constrain( + overflow, + one, + Some("attempt to bit-shift with overflow".to_owned()), + ); self.builder.insert_truncate(result, bit_size, bit_size + 1) } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 0f3a2272fe5..01fa25c697b 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -107,7 +107,6 @@ pub(crate) fn generate_ssa(program: Program) -> Result { while let Some((src_function_id, dest_id)) = context.pop_next_function_in_queue() { let function = &context.program[src_function_id]; function_context.new_function(dest_id, function); - // dbg!(function.name.clone()); function_context.codegen_function_body(&function.body)?; } @@ -668,6 +667,8 @@ impl<'a> FunctionContext<'a> { ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); + // Assert messages from constrain statements specified by the user should be handled before SSA, + // thus the assert_message field here should always be `None` self.builder.set_location(location).insert_constrain(expr, true_literal, None); Ok(Self::unit_value()) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 9d2179c78be..8f1e7be48b2 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -362,30 +362,6 @@ impl DefCollector { macro_processor.process_typed_ast(&crate_id, context); } - // TODO: started process for inserting call to `resolve_assert_message` on typed ast - // maybe it is better to do it on the untyped ast as we do not need type resolution - // for func_ids in file_func_ids.iter() { - // let function_body = context.def_interner.function(&func_ids.1); - // let function_body_id = function_body.as_expr(); - // match context.def_interner.expression(function_body_id) { - // HirExpression::Block(block_expr) => { - // let statements = block_expr.statements(); - // for stmt in statements.iter() { - // match context.def_interner.statement(stmt) { - // HirStatement::Constrain(constrain_stmt) => { - // if let Some(assert_msg_expr) = constrain_stmt.2 { - // let hir_expr = context.def_interner.expression(function_body_id); - // dbg!(hir_expr.clone()); - // } - // } - // _ => {} - // } - // } - // } - // _ => {} - // } - // } - errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); // Type check all of the functions in the crate diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 73060396aba..499d2bb6053 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2082,8 +2082,13 @@ mod test { match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() { StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - // TODO: update this test - // assert_eq!(message, Some("assertion message".to_owned())); + let message = message.unwrap(); + match message.kind { + ExpressionKind::Literal(Literal::Str(message_string)) => { + assert_eq!(message_string, "assertion message".to_owned()) + } + _ => unreachable!(), + } } _ => unreachable!(), } @@ -2107,8 +2112,13 @@ mod test { .unwrap() { StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - // TODO: update this test - // assert_eq!(message, Some("assertion message".to_owned())); + let message = message.unwrap(); + match message.kind { + ExpressionKind::Literal(Literal::Str(message_string)) => { + assert_eq!(message_string, "assertion message".to_owned()) + } + _ => unreachable!(), + } } _ => unreachable!(), } diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 349a7fa1892..190b0dd4f3e 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -4,8 +4,8 @@ use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::SortedModule; use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{ - Expression, ExpressionKind, HirContext, Ident, Path, PathKind, Span, Statement, StatementKind, - Literal, + Expression, ExpressionKind, HirContext, Ident, Literal, Path, PathKind, Span, Statement, + StatementKind, }; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; @@ -85,7 +85,7 @@ fn transform(mut ast: SortedModule) -> Result Option<(Vec NargoError { - let call_stack= resolve_call_stack(error); + let call_stack = resolve_call_stack(error); NargoError::ExecutionError(match call_stack { Some((call_stack, _)) => { @@ -92,7 +92,7 @@ fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circ // return false; // } // if let Opcode::Brillig(brillig) = &acvm.opcodes()[acvm.instruction_pointer()] { -// // We do not want +// // We do not want // match &brillig.bytecode[brillig.bytecode.len() - 2] { // acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. } => { // ForeignCall::execution_allowed_after_failure(function) From e149ede409be86e0557b907e37340bd25a9fedd8 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 21:03:15 +0000 Subject: [PATCH 13/64] cargo clippy --- acvm-repo/acir/src/circuit/mod.rs | 4 +- noirc_macros/src/lib.rs | 3 +- tooling/nargo/src/ops/execute.rs | 58 ++++---------------------- tooling/nargo/src/ops/foreign_calls.rs | 7 ---- 4 files changed, 12 insertions(+), 60 deletions(-) diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index 8053fd640cb..dcb09d429eb 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -39,9 +39,9 @@ pub struct Circuit { // c++ code at the moment when it is, due to OpcodeLocation needing a comparison // implementation which is never generated. // - // TODO: These are only used for constrains that are generating during compilation. + // TODO: These are only used for constraints that are explicitly created during code generation (such as index out of bounds on slices) // TODO: We should move towards having all the checks being evaluated in the same manner - // TODO: as runtime assert messages specified by the user. + // TODO: as runtime assert messages specified by the user. This will also be a breaking change as the `Circuit` structure will change. pub assert_messages: Vec<(OpcodeLocation, String)>, } diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 190b0dd4f3e..b67cde7c085 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -4,8 +4,7 @@ use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::SortedModule; use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{ - Expression, ExpressionKind, HirContext, Ident, Literal, Path, PathKind, Span, Statement, - StatementKind, + Expression, ExpressionKind, HirContext, Ident, Path, PathKind, Span, Statement, StatementKind, }; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 22c64db9045..5eb536ec38e 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,5 +1,4 @@ -use acvm::acir::circuit::OpcodeLocation; -use acvm::pwg::{ACVMForeignCallResult, ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; +use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; @@ -38,8 +37,16 @@ pub fn execute_circuit( return Err(NargoError::ExecutionError(match call_stack { Some(call_stack) => { + // First check whether we have a runtime assertion message that should be returned on an ACVM failure + // If we do not have a runtime assertion message, we should check whether the circuit has any harcoded + // messages associated with a specific `OpcodeLocation`. + // Otherwise return the provided opcode resolution error. if let Some(assert_message) = acvm.get_assert_message() { ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) + } else if let Some(assert_message) = circuit.get_assert_message( + *call_stack.last().expect("Call stacks should not be empty"), + ) { + ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) } else { ExecutionError::SolvingError(error) } @@ -56,50 +63,3 @@ pub fn execute_circuit( Ok(acvm.finalize()) } - -fn resolve_call_stack(error: &OpcodeResolutionError) -> Option<(Vec, bool)> { - match error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some((vec![*opcode_location], false)), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - dbg!("got brillig func failed"); - Some((call_stack.clone(), true)) - } - _ => None, - } -} - -fn resolve_comptime_assert_message(error: &OpcodeResolutionError, circuit: &Circuit) -> NargoError { - let call_stack = resolve_call_stack(error); - - NargoError::ExecutionError(match call_stack { - Some((call_stack, _)) => { - if let Some(assert_message) = circuit - .get_assert_message(*call_stack.last().expect("Call stacks should not be empty")) - { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else { - ExecutionError::SolvingError(error.clone()) - } - } - None => ExecutionError::SolvingError(error.clone()), - }) -} - -// fn can_process_opcode_after_failure<'a, B: BlackBoxFunctionSolver>(acvm: &ACVM<'a, B>) -> bool { -// if acvm.instruction_pointer() >= acvm.opcodes().len() { -// return false; -// } -// if let Opcode::Brillig(brillig) = &acvm.opcodes()[acvm.instruction_pointer()] { -// // We do not want -// match &brillig.bytecode[brillig.bytecode.len() - 2] { -// acvm::brillig_vm::brillig::Opcode::ForeignCall { function, .. } => { -// ForeignCall::execution_allowed_after_failure(function) -// } -// _ => false, -// } -// } else { -// false -// } -// } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 9a8c0cf60e5..31ffcbeeb13 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -55,13 +55,6 @@ impl ForeignCall { _ => None, } } - - pub(crate) fn execution_allowed_after_failure(op_name: &str) -> bool { - match op_name { - "assert_message" => true, - _ => true, - } - } } /// This struct represents an oracle mock. It can be used for testing programs that use oracles. From 8eb77ade1cdd9bd0c7115b52ce518539dd0744ec Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 21:03:57 +0000 Subject: [PATCH 14/64] add back debug logs lines that were removed --- test_programs/execution_success/debug_logs/src/main.nr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index 90db7578411..5e1de86eb96 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -43,6 +43,9 @@ fn main(x: Field, y: pub Field) { let bar = fooStruct { my_struct: s_2, foo: 20 }; std::println(f"foo1: {foo}, foo2: {bar}"); + let struct_string = if x != 5 { f"{foo}" } else { f"{bar}" }; + std::println(struct_string); + let one_tuple = (1, 2, 3); let another_tuple = (4, 5, 6); std::println(f"one_tuple: {one_tuple}, another_tuple: {another_tuple}"); From d0234d2c84f435a9e2f715c0a52007f305fdce30 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 21:10:30 +0000 Subject: [PATCH 15/64] added compile failure tests --- .../compile_failure/assert_msg_runtime/Nargo.toml | 7 +++++++ .../compile_failure/assert_msg_runtime/Prover.toml | 2 ++ .../compile_failure/assert_msg_runtime/src/main.nr | 4 ++++ .../brillig_assert_msg_runtime/Nargo.toml | 7 +++++++ .../brillig_assert_msg_runtime/Prover.toml | 1 + .../brillig_assert_msg_runtime/src/main.nr | 10 ++++++++++ 6 files changed, 31 insertions(+) create mode 100644 test_programs/compile_failure/assert_msg_runtime/Nargo.toml create mode 100644 test_programs/compile_failure/assert_msg_runtime/Prover.toml create mode 100644 test_programs/compile_failure/assert_msg_runtime/src/main.nr create mode 100644 test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml create mode 100644 test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml create mode 100644 test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr diff --git a/test_programs/compile_failure/assert_msg_runtime/Nargo.toml b/test_programs/compile_failure/assert_msg_runtime/Nargo.toml new file mode 100644 index 00000000000..765f632ff74 --- /dev/null +++ b/test_programs/compile_failure/assert_msg_runtime/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_msg_runtime" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/assert_msg_runtime/Prover.toml b/test_programs/compile_failure/assert_msg_runtime/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/test_programs/compile_failure/assert_msg_runtime/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/compile_failure/assert_msg_runtime/src/main.nr new file mode 100644 index 00000000000..ef096786170 --- /dev/null +++ b/test_programs/compile_failure/assert_msg_runtime/src/main.nr @@ -0,0 +1,4 @@ +fn main(x: Field, y: pub Field) { + assert(x != y, f"Expected x != y, but got both equal {x}"); + assert_eq(x, y, f"Expected x == y, but x is {x} and y is {y}"); +} \ No newline at end of file diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml b/test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml new file mode 100644 index 00000000000..00f97b7273a --- /dev/null +++ b/test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_assert_msg_runtime" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml b/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml new file mode 100644 index 00000000000..0e5dfd5638d --- /dev/null +++ b/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml @@ -0,0 +1 @@ +x = "5" diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr b/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr new file mode 100644 index 00000000000..428b2006363 --- /dev/null +++ b/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr @@ -0,0 +1,10 @@ +fn main(x: Field) { + assert(1 == conditional(x)); +} + +unconstrained fn conditional(x: Field) -> Field { + let z = x as u8 + 20; + assert_eq(z, 25, f"Expected 25 but got {z}"); + assert(x == 10, f"Expected x to equal 10, but got {x}"); + 1 +} \ No newline at end of file From 0f0cbe0eb528067dfacc61d3ded6d571897f05f6 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 21:49:20 +0000 Subject: [PATCH 16/64] move transformation of call expression to resolution rather than have it be in the macro --- acvm-repo/acvm/src/pwg/mod.rs | 1 - .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 ++- .../src/hir/resolution/resolver.rs | 41 +++++++++- .../noirc_frontend/src/hir/type_check/stmt.rs | 3 + compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- .../src/monomorphization/ast.rs | 2 +- .../src/monomorphization/mod.rs | 4 +- noirc_macros/src/lib.rs | 77 ++++--------------- .../brillig_assert/src/main.nr | 9 +-- 9 files changed, 77 insertions(+), 74 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 0b78d5649b1..a8bf6bee4aa 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -206,7 +206,6 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { /// Sets the VM status to [ACVMStatus::Failure] using the provided `error`. /// Returns the new status. fn fail(&mut self, error: OpcodeResolutionError) -> ACVMStatus { - // self.instruction_pointer += 1; self.status(ACVMStatus::Failure(error)) } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 01fa25c697b..dfe5ee80f3e 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -140,7 +140,9 @@ impl<'a> FunctionContext<'a> { } Expression::Call(call) => self.codegen_call(call), Expression::Let(let_expr) => self.codegen_let(let_expr), - Expression::Constrain(expr, location) => self.codegen_constrain(expr, *location), + Expression::Constrain(expr, location, assert_message) => { + self.codegen_constrain(expr, *location, assert_message) + } Expression::Assign(assign) => self.codegen_assign(assign), Expression::Semi(semi) => self.codegen_semi(semi), } @@ -664,10 +666,16 @@ impl<'a> FunctionContext<'a> { &mut self, expr: &Expression, location: Location, + assert_message: &Option>, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); - // Assert messages from constrain statements specified by the user should be handled before SSA, + + assert_message + .as_ref() + .map(|assert_message_expr| self.codegen_expression(assert_message_expr.as_ref())); + + // Assert messages from constrain statements specified by the user are codegen'd with a call expression, // thus the assert_message field here should always be `None` self.builder.set_location(location).insert_constrain(expr, true_literal, None); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 9f42128c469..bf2a71dfbff 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1107,7 +1107,46 @@ impl<'a> Resolver<'a> { } StatementKind::Constrain(constrain_stmt) => { let expr_id = self.resolve_expression(constrain_stmt.0); - HirStatement::Constrain(HirConstrainStatement(expr_id, self.file)) + let call_resolve_msg_expr = constrain_stmt.1.map(|assert_msg_expr| { + let span = assert_msg_expr.span; + let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); + let call_expr = if is_in_stdlib { + Expression::call( + Expression { + kind: ExpressionKind::Variable(Path { + segments: vec![Ident::from("resolve_assert_message")], + kind: PathKind::Crate, + span, + }), + span, + }, + vec![assert_msg_expr.clone()], + span, + ) + } else { + Expression::call( + Expression { + kind: ExpressionKind::Variable(Path { + segments: vec![ + Ident::from("std"), + Ident::from("resolve_assert_message"), + ], + kind: PathKind::Dep, + span: Span::default(), + }), + span: Span::default(), + }, + vec![assert_msg_expr.clone()], + Span::default(), + ) + }; + self.resolve_expression(call_expr) + }); + HirStatement::Constrain(HirConstrainStatement( + expr_id, + self.file, + call_resolve_msg_expr, + )) } StatementKind::Expression(expr) => { HirStatement::Expression(self.resolve_expression(expr)) diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index c4e72f48c50..a8ac17715fe 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -305,6 +305,9 @@ impl<'interner> TypeChecker<'interner> { let expr_type = self.check_expression(&stmt.0); let expr_span = self.interner.expr_span(&stmt.0); + // Must type check the assertion message expression so that we instantiate bindings + stmt.2.map(|assert_msg_expr| self.check_expression(&assert_msg_expr)); + self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), expected_typ: Type::Bool.to_string(), diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index f76e0e37a6c..4d946690151 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -55,7 +55,7 @@ pub struct HirAssignStatement { /// originates from. This is used later in the SSA pass to issue /// an error if a constrain is found to be always false. #[derive(Debug, Clone)] -pub struct HirConstrainStatement(pub ExprId, pub FileId); +pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); #[derive(Debug, Clone, Hash)] pub enum HirPattern { diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 782393ec4b2..5172eabde46 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -31,7 +31,7 @@ pub enum Expression { ExtractTupleField(Box, usize), Call(Call), Let(Let), - Constrain(Box, Location), + Constrain(Box, Location, Option>), Assign(Assign), Semi(Box), } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index d9789353d7b..663731d462c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -479,7 +479,9 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Constrain(constrain) => { let expr = self.expr(constrain.0); let location = self.interner.expr_location(&constrain.0); - ast::Expression::Constrain(Box::new(expr), location) + let assert_message = + constrain.2.map(|assert_msg_expr| Box::new(self.expr(assert_msg_expr))); + ast::Expression::Constrain(Box::new(expr), location, assert_message) } HirStatement::Assign(assign) => self.assign(assign), HirStatement::For(for_loop) => { diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index b67cde7c085..55319353bb6 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -3,9 +3,7 @@ use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::SortedModule; use noirc_frontend::macros_api::{CrateId, FileId}; -use noirc_frontend::macros_api::{ - Expression, ExpressionKind, HirContext, Ident, Path, PathKind, Span, Statement, StatementKind, -}; +use noirc_frontend::macros_api::HirContext; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; pub struct AssertMessageMacro; @@ -14,17 +12,29 @@ impl MacroProcessor for AssertMessageMacro { fn process_untyped_ast( &self, ast: SortedModule, - _crate_id: &CrateId, + crate_id: &CrateId, _context: &HirContext, ) -> Result { - transform(ast) + transform(ast, crate_id) } // This macro does not need to process any information after name resolution fn process_typed_ast(&self, _crate_id: &CrateId, _context: &mut HirContext) {} } -fn transform(mut ast: SortedModule) -> Result { +fn transform(ast: SortedModule, crate_id: &CrateId) -> Result { + let ast = add_resolve_assert_message_funcs(ast, crate_id)?; + + Ok(ast) +} + +fn add_resolve_assert_message_funcs( + mut ast: SortedModule, + crate_id: &CrateId, +) -> Result { + if !crate_id.is_stdlib() { + return Ok(ast); + } let assert_message_oracles = " #[oracle(assert_message)] unconstrained fn assert_message_oracle(_input: T) {} @@ -34,63 +44,10 @@ fn transform(mut ast: SortedModule) -> Result Field { - assert(x, "x is false"); - // TODO: this is not accurately printing the assert message - // got `Failed to solve brillig function, reason: explicit trap hit in brillig` - // expected `x is false` - assert_eq(x, true, "x is blah blah"); - let z = x as u8 + 20; - assert_eq(z, 25); + assert(x, f"Expected x to be false but got {x}"); + assert_eq(x, true, f"Expected x to be false but got {x}"); 1 } From 85a40c40fb374d12e757df7eef0c43e335ffc2d4 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 21:57:36 +0000 Subject: [PATCH 17/64] handle parser rrs --- noirc_macros/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 55319353bb6..643cb382ee0 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -1,9 +1,9 @@ // use noirc_frontend::macros_api::{parse_program, SortedModule, CrateId use noirc_frontend::macros_api::parse_program; +use noirc_frontend::macros_api::HirContext; use noirc_frontend::macros_api::SortedModule; use noirc_frontend::macros_api::{CrateId, FileId}; -use noirc_frontend::macros_api::HirContext; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; pub struct AssertMessageMacro; @@ -42,7 +42,9 @@ fn add_resolve_assert_message_funcs( assert_message_oracle(input); }"; // TODO: return parsing errors? - let (assert_msg_funcs_ast, _) = parse_program(assert_message_oracles); + let (assert_msg_funcs_ast, errors) = parse_program(assert_message_oracles); + assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); + let assert_msg_funcs_ast = assert_msg_funcs_ast.into_sorted(); for func in assert_msg_funcs_ast.functions { From e42cb9ca50652bd0ebc5b4e3030eb62020288049 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 22:03:55 +0000 Subject: [PATCH 18/64] fix create_mock --- acvm-repo/brillig_vm/src/lib.rs | 2 +- noirc_macros/src/lib.rs | 2 +- tooling/nargo/src/ops/foreign_calls.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 00c20a704da..b32f7202193 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -242,7 +242,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.registers.set(*value_index, *value); } _ => unreachable!( - "Function result size does not match brillig bytecode (expected 1 result)" + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" ), }, RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 643cb382ee0..c279cc7222d 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -41,7 +41,7 @@ fn add_resolve_assert_message_funcs( unconstrained pub fn resolve_assert_message(input: T) { assert_message_oracle(input); }"; - // TODO: return parsing errors? + let (assert_msg_funcs_ast, errors) = parse_program(assert_message_oracles); assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 80fc379b6e7..1b8018c4e7f 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -187,7 +187,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { self.mocked_responses.push(MockedCall::new(id, mock_oracle_name)); self.last_mock_id += 1; - Ok(vec![Value::from(id)].into()) + Ok(Value::from(id).into()) } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; From ec87b69aa5818c33a4a553144aba2d8b8fa9f118 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 22:47:41 +0000 Subject: [PATCH 19/64] move to non-optional expr --- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 6 +- .../src/hir/resolution/resolver.rs | 108 ++++++++++++------ .../noirc_frontend/src/hir/type_check/stmt.rs | 2 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- .../src/monomorphization/ast.rs | 2 +- .../src/monomorphization/mod.rs | 5 +- 6 files changed, 79 insertions(+), 46 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index dfe5ee80f3e..f2881e09ebc 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -666,14 +666,12 @@ impl<'a> FunctionContext<'a> { &mut self, expr: &Expression, location: Location, - assert_message: &Option>, + assert_message: &Expression, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); - assert_message - .as_ref() - .map(|assert_message_expr| self.codegen_expression(assert_message_expr.as_ref())); + self.codegen_expression(assert_message)?; // Assert messages from constrain statements specified by the user are codegen'd with a call expression, // thus the assert_message field here should always be `None` diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index d1a6047eb22..04f882ce82e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1118,46 +1118,82 @@ impl<'a> Resolver<'a> { }) } StatementKind::Constrain(constrain_stmt) => { + let span = constrain_stmt.0.span; let expr_id = self.resolve_expression(constrain_stmt.0); - let call_resolve_msg_expr = constrain_stmt.1.map(|assert_msg_expr| { - let span = assert_msg_expr.span; - let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); - let call_expr = if is_in_stdlib { - Expression::call( - Expression { - kind: ExpressionKind::Variable(Path { - segments: vec![Ident::from("resolve_assert_message")], - kind: PathKind::Crate, - span, - }), - span, - }, - vec![assert_msg_expr.clone()], - span, - ) - } else { - Expression::call( - Expression { - kind: ExpressionKind::Variable(Path { - segments: vec![ - Ident::from("std"), - Ident::from("resolve_assert_message"), - ], - kind: PathKind::Dep, - span: Span::default(), - }), - span: Span::default(), - }, - vec![assert_msg_expr.clone()], - Span::default(), - ) - }; - self.resolve_expression(call_expr) - }); + // let call_resolve_msg_expr = constrain_stmt.1.map(|assert_msg_expr| { + // let span = assert_msg_expr.span; + // let call_expr = if is_in_stdlib { + // Expression::call( + // Expression { + // kind: ExpressionKind::Variable(Path { + // segments: vec![Ident::from("resolve_assert_message")], + // kind: PathKind::Crate, + // span, + // }), + // span, + // }, + // vec![assert_msg_expr.clone()], + // span, + // ) + // } else { + // Expression::call( + // Expression { + // kind: ExpressionKind::Variable(Path { + // segments: vec![ + // Ident::from("std"), + // Ident::from("resolve_assert_message"), + // ], + // kind: PathKind::Dep, + // span: Span::default(), + // }), + // span: Span::default(), + // }, + // vec![assert_msg_expr.clone()], + // span, + // ) + // }; + // self.resolve_expression(call_expr) + // }); + + let assert_msg_call_args = if let Some(assert_msg_expr) = constrain_stmt.1 { + vec![assert_msg_expr.clone()] + } else { + let kind = ExpressionKind::string("".to_owned()); + let arg = Expression { kind, span: Span::default() }; + vec![arg] + }; + + let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); + let assert_msg_call_path = if is_in_stdlib { + ExpressionKind::Variable(Path { + segments: vec![Ident::from("resolve_assert_message")], + kind: PathKind::Crate, + span, + }) + } else { + ExpressionKind::Variable(Path { + segments: vec![ + Ident::from("std"), + Ident::from("resolve_assert_message"), + ], + kind: PathKind::Dep, + span, + }) + }; + let assert_msg_call_expr = Expression::call( + Expression { + kind: assert_msg_call_path, + span: span, + }, + assert_msg_call_args, + span, + ); + let assert_msg_call_expr_id = self.resolve_expression(assert_msg_call_expr); + HirStatement::Constrain(HirConstrainStatement( expr_id, self.file, - call_resolve_msg_expr, + assert_msg_call_expr_id, )) } StatementKind::Expression(expr) => { diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index a8ac17715fe..b72ef5ce364 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -306,7 +306,7 @@ impl<'interner> TypeChecker<'interner> { let expr_span = self.interner.expr_span(&stmt.0); // Must type check the assertion message expression so that we instantiate bindings - stmt.2.map(|assert_msg_expr| self.check_expression(&assert_msg_expr)); + self.check_expression(&stmt.2); self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 4d946690151..90fb5024ceb 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -55,7 +55,7 @@ pub struct HirAssignStatement { /// originates from. This is used later in the SSA pass to issue /// an error if a constrain is found to be always false. #[derive(Debug, Clone)] -pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); +pub struct HirConstrainStatement(pub ExprId, pub FileId, pub ExprId); #[derive(Debug, Clone, Hash)] pub enum HirPattern { diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 5172eabde46..f69c7794f52 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -31,7 +31,7 @@ pub enum Expression { ExtractTupleField(Box, usize), Call(Call), Let(Let), - Constrain(Box, Location, Option>), + Constrain(Box, Location, Box), Assign(Assign), Semi(Box), } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 663731d462c..cd9f491918e 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -479,9 +479,8 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Constrain(constrain) => { let expr = self.expr(constrain.0); let location = self.interner.expr_location(&constrain.0); - let assert_message = - constrain.2.map(|assert_msg_expr| Box::new(self.expr(assert_msg_expr))); - ast::Expression::Constrain(Box::new(expr), location, assert_message) + let assert_message = self.expr(constrain.2); + ast::Expression::Constrain(Box::new(expr), location, Box::new(assert_message)) } HirStatement::Assign(assign) => self.assign(assign), HirStatement::For(for_loop) => { From bc317a0085fe80c76de0420fffd73d07e46e0661 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 22:49:19 +0000 Subject: [PATCH 20/64] clippy --- compiler/noirc_frontend/src/hir/resolution/resolver.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 04f882ce82e..038994b998b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1172,19 +1172,13 @@ impl<'a> Resolver<'a> { }) } else { ExpressionKind::Variable(Path { - segments: vec![ - Ident::from("std"), - Ident::from("resolve_assert_message"), - ], + segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], kind: PathKind::Dep, span, }) }; let assert_msg_call_expr = Expression::call( - Expression { - kind: assert_msg_call_path, - span: span, - }, + Expression { kind: assert_msg_call_path, span }, assert_msg_call_args, span, ); From 9284490939718bb584b70346a24a1f8e7f9cea53 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 Jan 2024 23:38:52 +0000 Subject: [PATCH 21/64] clear out circuits w/ only brillig --- compiler/noirc_driver/src/lib.rs | 1 + compiler/noirc_evaluator/src/ssa.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 6f9749495e9..e2d1f909a89 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -233,6 +233,7 @@ pub fn compile_main( let compiled_program = compile_no_check(context, options, main, cached_program, options.force_compile) .map_err(FileDiagnostic::from)?; + let compilation_warnings = vecmap(compiled_program.warnings.clone(), FileDiagnostic::from); if options.deny_warnings && !compilation_warnings.is_empty() { return Err(compilation_warnings); diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 0e3076923e0..55ae353f6ea 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -14,7 +14,7 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::acir::{ - circuit::{Circuit, PublicInputs}, + circuit::{Circuit, Opcode, PublicInputs}, native_types::Witness, }; @@ -87,7 +87,16 @@ pub fn create_circuit( let func_sig = program.main_function_signature.clone(); let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; - let opcodes = generated_acir.take_opcodes(); + let mut opcodes = generated_acir.take_opcodes(); + let mut only_brillig = true; + for opcode in opcodes.iter() { + if !matches!(opcode, Opcode::Brillig(_)) { + only_brillig = false; + } + } + if only_brillig { + opcodes = vec![]; + } let current_witness_index = generated_acir.current_witness_index().0; let GeneratedAcir { return_witnesses, From 4333637329f2a95a293b744a7702ddf76696475e Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 24 Jan 2024 01:52:52 +0000 Subject: [PATCH 22/64] update compile_empty_success test --- compiler/noirc_evaluator/src/ssa.rs | 13 +----- .../method_call_regression/Nargo.toml | 2 +- .../trait_static_methods/Nargo.toml | 2 +- tooling/nargo_cli/build.rs | 41 ++++++++++++------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 55ae353f6ea..0e3076923e0 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -14,7 +14,7 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::acir::{ - circuit::{Circuit, Opcode, PublicInputs}, + circuit::{Circuit, PublicInputs}, native_types::Witness, }; @@ -87,16 +87,7 @@ pub fn create_circuit( let func_sig = program.main_function_signature.clone(); let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; - let mut opcodes = generated_acir.take_opcodes(); - let mut only_brillig = true; - for opcode in opcodes.iter() { - if !matches!(opcode, Opcode::Brillig(_)) { - only_brillig = false; - } - } - if only_brillig { - opcodes = vec![]; - } + let opcodes = generated_acir.take_opcodes(); let current_witness_index = generated_acir.current_witness_index().0; let GeneratedAcir { return_witnesses, diff --git a/test_programs/compile_success_empty/method_call_regression/Nargo.toml b/test_programs/compile_success_empty/method_call_regression/Nargo.toml index 92c9b942008..09f95590aad 100644 --- a/test_programs/compile_success_empty/method_call_regression/Nargo.toml +++ b/test_programs/compile_success_empty/method_call_regression/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "short" +name = "method_call_regression" type = "bin" authors = [""] compiler_version = ">=0.19.4" diff --git a/test_programs/compile_success_empty/trait_static_methods/Nargo.toml b/test_programs/compile_success_empty/trait_static_methods/Nargo.toml index 71c541ccd4f..ea30031b9a5 100644 --- a/test_programs/compile_success_empty/trait_static_methods/Nargo.toml +++ b/test_programs/compile_success_empty/trait_static_methods/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "trait_self" +name = "trait_static_methods" type = "bin" authors = [""] diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 57aa487f66a..397e163aaf0 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -162,6 +162,10 @@ fn noir_test_failure_{test_name}() {{ } } +/// TODO: Certain tests may have foreign calls leftover (such as assert message resolution) +/// TODO: even though all assertion and other logic has been optimized away. +/// TODO: We should determine a way to tie certain foreign calls to a constraint so they can be optimized away +/// TODO: with the constraint. fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "compile_success_empty"; let test_data_dir = test_data_dir.join(test_sub_dir); @@ -189,25 +193,34 @@ fn compile_success_empty_{test_name}() {{ // but we must call a backend as part of querying the number of opcodes in the circuit. let test_program_dir = PathBuf::from("{test_dir}"); + let mut test_program_artifact = test_program_dir.clone(); + test_program_artifact.push("target"); + // TODO: We need more generalized handling for workspaces in this test + if "{test_name}" == "workspace_reexport_bug" {{ + test_program_artifact.push("binary"); + }} else {{ + test_program_artifact.push("{test_name}"); + }} + test_program_artifact.set_extension("json"); + let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("info"); - cmd.arg("--json"); - cmd.arg("--force"); + cmd.arg("compile").arg("--force"); - let output = cmd.output().expect("Failed to execute command"); + cmd.assert().success(); - if !output.status.success() {{ - panic!("`nargo info` failed with: {{}}", String::from_utf8(output.stderr).unwrap_or_default()); - }} + let input_string = + std::fs::read(&test_program_artifact).unwrap_or_else(|_| panic!("Failed to read program artifact")); + let program: nargo::artifacts::program::ProgramArtifact = serde_json::from_slice(&input_string).unwrap_or_else(|_| panic!("Failed to serialize program artifact")); - // `compile_success_empty` tests should be able to compile down to an empty circuit. - let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|_| {{ - panic!("JSON was not well-formatted {{:?}}",output.stdout) - }}); - let num_opcodes = &json["programs"][0]["acir_opcodes"]; - assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); + let mut only_brillig = true; + for opcode in program.bytecode.opcodes.iter() {{ + if !matches!(opcode, acvm::acir::circuit::Opcode::Brillig(_)) {{ + only_brillig = false; + }} + }} + assert_eq!(only_brillig, true); }} "#, test_dir = test_dir.display(), @@ -280,7 +293,7 @@ fn compile_failure_{test_name}() {{ let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); - cmd.arg("--program-dir").arg(test_program_dir); + cmd.arg("--program-dir").arg(test_program_dir.clone()); cmd.arg("execute").arg("--force"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); From 9bc00ebb50c75fc812d23283a8e894328f617c76 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 24 Jan 2024 02:09:52 +0000 Subject: [PATCH 23/64] fix format --- tooling/nargo_fmt/src/visitor/stmt.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 800a8656ef3..cfe5acb21a7 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -38,8 +38,7 @@ impl super::FmtVisitor<'_> { nested_shape.indent.block_indent(self.config); - let message = - message.map_or(String::new(), |message| format!(", \"{message}\"")); + let message = message.map_or(String::new(), |message| format!(", {message}")); let (callee, args) = match kind { ConstrainKind::Assert => { From f3083c84b466b18fd6a7a0c1b2e25a4f10b411a6 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 24 Jan 2024 02:13:34 +0000 Subject: [PATCH 24/64] cleanup resolve assert message --- .../src/hir/resolution/resolver.rs | 98 +++++++------------ 1 file changed, 36 insertions(+), 62 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 038994b998b..3993ceee21b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1120,69 +1120,8 @@ impl<'a> Resolver<'a> { StatementKind::Constrain(constrain_stmt) => { let span = constrain_stmt.0.span; let expr_id = self.resolve_expression(constrain_stmt.0); - // let call_resolve_msg_expr = constrain_stmt.1.map(|assert_msg_expr| { - // let span = assert_msg_expr.span; - // let call_expr = if is_in_stdlib { - // Expression::call( - // Expression { - // kind: ExpressionKind::Variable(Path { - // segments: vec![Ident::from("resolve_assert_message")], - // kind: PathKind::Crate, - // span, - // }), - // span, - // }, - // vec![assert_msg_expr.clone()], - // span, - // ) - // } else { - // Expression::call( - // Expression { - // kind: ExpressionKind::Variable(Path { - // segments: vec![ - // Ident::from("std"), - // Ident::from("resolve_assert_message"), - // ], - // kind: PathKind::Dep, - // span: Span::default(), - // }), - // span: Span::default(), - // }, - // vec![assert_msg_expr.clone()], - // span, - // ) - // }; - // self.resolve_expression(call_expr) - // }); - - let assert_msg_call_args = if let Some(assert_msg_expr) = constrain_stmt.1 { - vec![assert_msg_expr.clone()] - } else { - let kind = ExpressionKind::string("".to_owned()); - let arg = Expression { kind, span: Span::default() }; - vec![arg] - }; - let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); - let assert_msg_call_path = if is_in_stdlib { - ExpressionKind::Variable(Path { - segments: vec![Ident::from("resolve_assert_message")], - kind: PathKind::Crate, - span, - }) - } else { - ExpressionKind::Variable(Path { - segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], - kind: PathKind::Dep, - span, - }) - }; - let assert_msg_call_expr = Expression::call( - Expression { kind: assert_msg_call_path, span }, - assert_msg_call_args, - span, - ); - let assert_msg_call_expr_id = self.resolve_expression(assert_msg_call_expr); + let assert_msg_call_expr_id = self.resolve_assert_message(constrain_stmt.1, span); HirStatement::Constrain(HirConstrainStatement( expr_id, @@ -1237,6 +1176,41 @@ impl<'a> Resolver<'a> { } } + fn resolve_assert_message( + &mut self, + assert_message_expr: Option, + span: Span, + ) -> ExprId { + let assert_msg_call_args = if let Some(assert_message_expr) = assert_message_expr { + vec![assert_message_expr.clone()] + } else { + let kind = ExpressionKind::string("".to_owned()); + let arg = Expression { kind, span: Span::default() }; + vec![arg] + }; + + let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); + let assert_msg_call_path = if is_in_stdlib { + ExpressionKind::Variable(Path { + segments: vec![Ident::from("resolve_assert_message")], + kind: PathKind::Crate, + span, + }) + } else { + ExpressionKind::Variable(Path { + segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], + kind: PathKind::Dep, + span, + }) + }; + let assert_msg_call_expr = Expression::call( + Expression { kind: assert_msg_call_path, span }, + assert_msg_call_args, + span, + ); + self.resolve_expression(assert_msg_call_expr) + } + pub fn intern_stmt(&mut self, stmt: StatementKind) -> StmtId { let hir_stmt = self.resolve_stmt(stmt); self.interner.push_stmt(hir_stmt) From 185aba1f79bb3ba4bf0fd2148c1682e430c56b3d Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 24 Jan 2024 15:44:14 +0000 Subject: [PATCH 25/64] update debugger handle_foreign_call --- tooling/debugger/src/context.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index e5c38806eb1..270d6b77027 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -1,5 +1,6 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::brillig_vm::brillig::ForeignCallResult; use acvm::brillig_vm::{brillig::Value, Registers}; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, @@ -225,7 +226,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { - let foreign_call_result = foreign_call_result.get_brillig_output().expect("Debugger Error: Should only be attempting to execute foreign calls resolving back to Brillig"); + let foreign_call_result = foreign_call_result.get_brillig_output().unwrap_or(ForeignCallResult::default()); solver.resolve_pending_foreign_call(foreign_call_result); self.brillig_solver = Some(solver); } else { From 27ea4d9ae4ba02f036d6f1f9e424a9d98fad95c5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 24 Jan 2024 15:46:36 +0000 Subject: [PATCH 26/64] cargo fmt --- tooling/debugger/src/context.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 270d6b77027..c4906cb10cb 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -226,7 +226,9 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { match foreign_call_result { Ok(foreign_call_result) => { if let Some(mut solver) = self.brillig_solver.take() { - let foreign_call_result = foreign_call_result.get_brillig_output().unwrap_or(ForeignCallResult::default()); + let foreign_call_result = foreign_call_result + .get_brillig_output() + .unwrap_or(ForeignCallResult::default()); solver.resolve_pending_foreign_call(foreign_call_result); self.brillig_solver = Some(solver); } else { From 0c7ff786725f0f1cef23062afb563cbf52b4317b Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 24 Jan 2024 16:31:21 +0000 Subject: [PATCH 27/64] fix frontend tests, but acvm_js tests still failing --- compiler/noirc_frontend/src/parser/parser.rs | 4 ++-- compiler/noirc_frontend/src/tests.rs | 22 +++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 499d2bb6053..d2854085a8f 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2485,7 +2485,7 @@ mod test { Case { source: "assert(x == x, x)", expect: "constrain (plain::x == plain::x)", - errors: 1, + errors: 0, }, Case { source: "assert_eq(x,)", expect: "constrain (Error == Error)", errors: 1 }, Case { @@ -2496,7 +2496,7 @@ mod test { Case { source: "assert_eq(x, x, x)", expect: "constrain (plain::x == plain::x)", - errors: 1, + errors: 0, }, ]; diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9ccbddab9ec..be5608a4626 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -486,8 +486,9 @@ mod test { fn default(x: Field, y: NotAType) -> Field; } - fn main(x: Field, y: Field) { - assert(y == x); + fn main(x: Field, y: Field) -> pub bool { + // assert(y == x); + y == x }"; let errors = get_program_errors(src); assert!(!has_parser_error(&errors)); @@ -818,9 +819,9 @@ mod test { #[test] fn resolve_basic_function() { let src = r#" - fn main(x : Field) { + fn main(x : Field) -> pub bool { let y = x + x; - assert(y == x); + y == x } "#; assert!(get_program_errors(src).is_empty()); @@ -828,9 +829,10 @@ mod test { #[test] fn resolve_unused_var() { let src = r#" - fn main(x : Field) { + fn main(x : Field) -> pub bool { let y = x + x; - assert(x == x); + // assert(x == x); + x == x } "#; @@ -848,9 +850,9 @@ mod test { #[test] fn resolve_unresolved_var() { let src = r#" - fn main(x : Field) { + fn main(x : Field) -> pub bool { let y = x + x; - assert(y == z); + y == z } "#; let errors = get_program_errors(src); @@ -894,9 +896,9 @@ mod test { #[test] fn resolve_literal_expr() { let src = r#" - fn main(x : Field) { + fn main(x : Field) -> pub bool { let y = 5; - assert(y == x); + y == x } "#; assert!(get_program_errors(src).is_empty()); From 720e4708ca7913b2c4cd41b784ca5b937c79c7d4 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 24 Jan 2024 20:45:42 +0000 Subject: [PATCH 28/64] Update tooling/nargo/src/ops/execute.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- tooling/nargo/src/ops/execute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 5eb536ec38e..b41b92b8dbd 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -38,7 +38,7 @@ pub fn execute_circuit( return Err(NargoError::ExecutionError(match call_stack { Some(call_stack) => { // First check whether we have a runtime assertion message that should be returned on an ACVM failure - // If we do not have a runtime assertion message, we should check whether the circuit has any harcoded + // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded // messages associated with a specific `OpcodeLocation`. // Otherwise return the provided opcode resolution error. if let Some(assert_message) = acvm.get_assert_message() { From 869808366beea5bec7ef774a85b357db81052874 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 30 Jan 2024 19:37:16 +0000 Subject: [PATCH 29/64] some initial switches to putting resolve_assert_message under a predicate, still have to make call instr atomic w/ SSA --- acvm-repo/acvm/src/pwg/mod.rs | 78 +------------------ .../src/hir/resolution/resolver.rs | 8 +- noirc_macros/src/lib.rs | 6 +- .../assert_msg_runtime/src/main.nr | 7 ++ tooling/debugger/src/context.rs | 6 +- tooling/nargo/src/ops/execute.rs | 28 +++++-- tooling/nargo/src/ops/foreign_calls.rs | 63 ++++++++++++++- tooling/nargo_cli/build.rs | 63 ++++++++++----- 8 files changed, 145 insertions(+), 114 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index a8bf6bee4aa..a8fa3342c1d 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -140,8 +140,6 @@ pub struct ACVM<'a, B: BlackBoxFunctionSolver> { witness_map: WitnessMap, brillig_solver: Option>, - - current_assert_message: Option, } impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { @@ -155,7 +153,6 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { instruction_pointer: 0, witness_map: initial_witness, brillig_solver: None, - current_assert_message: None, } } @@ -227,25 +224,13 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { /// Resolves a foreign call's [result][acir::brillig_vm::ForeignCallResult] using a result calculated outside of the ACVM. /// /// The ACVM can then be restarted to solve the remaining Brillig VM process as well as the remaining ACIR opcodes. - pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ACVMForeignCallResult) { + pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult) { if !matches!(self.status, ACVMStatus::RequiresForeignCall(_)) { panic!("ACVM is not expecting a foreign call response as no call was made"); } let brillig_solver = self.brillig_solver.as_mut().expect("No active Brillig solver"); - match foreign_call_result { - ACVMForeignCallResult::BrilligOutput(foreign_call_result) => { - brillig_solver.resolve_pending_foreign_call(foreign_call_result); - } - ACVMForeignCallResult::ResolvedAssertMessage(assert_message) => { - if assert_message.is_empty() { - self.current_assert_message = None; - } else { - self.current_assert_message = Some(assert_message); - } - brillig_solver.resolve_pending_foreign_call(ForeignCallResult::default()); - } - } + brillig_solver.resolve_pending_foreign_call(foreign_call_result); // Now that the foreign call has been resolved then we can resume execution. self.status(ACVMStatus::InProgress); @@ -390,10 +375,6 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { self.brillig_solver = Some(solver); self.solve_opcode() } - - pub fn get_assert_message(&self) -> &Option { - &self.current_assert_message - } } // Returns the concrete value for a particular witness @@ -463,58 +444,3 @@ fn any_witness_from_expression(expr: &Expression) -> Option { Some(expr.linear_combinations[0].1) } } - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum ACVMForeignCallResult { - BrilligOutput(ForeignCallResult), - ResolvedAssertMessage(String), -} - -impl ACVMForeignCallResult { - pub fn get_assert_message(self) -> Option { - match self { - Self::ResolvedAssertMessage(msg) => Some(msg), - _ => None, - } - } - - pub fn get_brillig_output(self) -> Option { - match self { - Self::BrilligOutput(foreign_call_result) => Some(foreign_call_result), - _ => None, - } - } -} - -impl From for ACVMForeignCallResult { - fn from(value: ForeignCallResult) -> Self { - Self::BrilligOutput(value) - } -} - -impl From for ACVMForeignCallResult { - fn from(value: String) -> Self { - Self::ResolvedAssertMessage(value) - } -} - -impl From for ACVMForeignCallResult { - fn from(value: Value) -> Self { - let foreign_call_result: ForeignCallResult = value.into(); - foreign_call_result.into() - } -} - -impl From> for ACVMForeignCallResult { - fn from(values: Vec) -> Self { - let foreign_call_result: ForeignCallResult = values.into(); - foreign_call_result.into() - } -} - -impl From> for ACVMForeignCallResult { - fn from(values: Vec) -> Self { - let foreign_call_result: ForeignCallResult = values.into(); - foreign_call_result.into() - } -} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 3993ceee21b..dce99341c3b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1119,10 +1119,10 @@ impl<'a> Resolver<'a> { } StatementKind::Constrain(constrain_stmt) => { let span = constrain_stmt.0.span; + let assert_msg_call_expr_id = + self.resolve_assert_message(constrain_stmt.1, span, constrain_stmt.0.clone()); let expr_id = self.resolve_expression(constrain_stmt.0); - let assert_msg_call_expr_id = self.resolve_assert_message(constrain_stmt.1, span); - HirStatement::Constrain(HirConstrainStatement( expr_id, self.file, @@ -1180,14 +1180,16 @@ impl<'a> Resolver<'a> { &mut self, assert_message_expr: Option, span: Span, + condition: Expression, ) -> ExprId { - let assert_msg_call_args = if let Some(assert_message_expr) = assert_message_expr { + let mut assert_msg_call_args = if let Some(assert_message_expr) = assert_message_expr { vec![assert_message_expr.clone()] } else { let kind = ExpressionKind::string("".to_owned()); let arg = Expression { kind, span: Span::default() }; vec![arg] }; + assert_msg_call_args.push(condition); let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); let assert_msg_call_path = if is_in_stdlib { diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index c279cc7222d..478ac175b87 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -38,8 +38,10 @@ fn add_resolve_assert_message_funcs( let assert_message_oracles = " #[oracle(assert_message)] unconstrained fn assert_message_oracle(_input: T) {} - unconstrained pub fn resolve_assert_message(input: T) { - assert_message_oracle(input); + unconstrained pub fn resolve_assert_message(input: T, condition: bool) { + if !condition { + assert_message_oracle(input); + } }"; let (assert_msg_funcs_ast, errors) = parse_program(assert_message_oracles); diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/compile_failure/assert_msg_runtime/src/main.nr index ef096786170..0aa9e4ef3a8 100644 --- a/test_programs/compile_failure/assert_msg_runtime/src/main.nr +++ b/test_programs/compile_failure/assert_msg_runtime/src/main.nr @@ -1,4 +1,11 @@ fn main(x: Field, y: pub Field) { assert(x != y, f"Expected x != y, but got both equal {x}"); + assert(x != y, f"Expected x != y, but got both equal {x}"); + assert(x != y, f"Expected x != y, but got both equal {x}"); + assert(x != y, f"Expected x != y, but got both equal {x}"); + assert(x != y, f"Expected x != y, but got both equal {x}"); + let z = x + y; + assert(z != y, f"Expected x != y, but got both equal {x}"); + // assert_eq(x, y); assert_eq(x, y, f"Expected x == y, but x is {x} and y is {y}"); } \ No newline at end of file diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index c4906cb10cb..ab807c65a46 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -225,10 +225,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); match foreign_call_result { Ok(foreign_call_result) => { + let foreign_call_result = foreign_call_result + .get_brillig_output() + .unwrap_or(ForeignCallResult::default()); if let Some(mut solver) = self.brillig_solver.take() { - let foreign_call_result = foreign_call_result - .get_brillig_output() - .unwrap_or(ForeignCallResult::default()); solver.resolve_pending_foreign_call(foreign_call_result); self.brillig_solver = Some(solver); } else { diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 5eb536ec38e..96df001de7d 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,3 +1,4 @@ +use acvm::brillig_vm::brillig::ForeignCallResult; use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; @@ -5,7 +6,7 @@ use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; use crate::errors::ExecutionError; use crate::NargoError; -use super::foreign_calls::ForeignCallExecutor; +use super::foreign_calls::{ForeignCallExecutor, NargoForeignCallResult}; #[tracing::instrument(level = "trace", skip_all)] pub fn execute_circuit( @@ -15,16 +16,19 @@ pub fn execute_circuit( foreign_call_executor: &mut F, ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - + // TODO: remove only for debugging + let mut num_foreign_calls = 0; + let mut assert_message: Option = None; loop { let solver_status = acvm.solve(); - + // dbg!(acvm.get_assert_message()); match solver_status { ACVMStatus::Solved => break, ACVMStatus::InProgress => { unreachable!("Execution should not stop while in `InProgress` state.") } ACVMStatus::Failure(error) => { + dbg!(num_foreign_calls); let call_stack = match &error { OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Resolved(opcode_location), @@ -41,7 +45,7 @@ pub fn execute_circuit( // If we do not have a runtime assertion message, we should check whether the circuit has any harcoded // messages associated with a specific `OpcodeLocation`. // Otherwise return the provided opcode resolution error. - if let Some(assert_message) = acvm.get_assert_message() { + if let Some(assert_message) = assert_message { ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) } else if let Some(assert_message) = circuit.get_assert_message( *call_stack.last().expect("Call stacks should not be empty"), @@ -56,7 +60,21 @@ pub fn execute_circuit( } ACVMStatus::RequiresForeignCall(foreign_call) => { let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - acvm.resolve_pending_foreign_call(foreign_call_result); + dbg!(foreign_call_result.clone()); + match foreign_call_result { + NargoForeignCallResult::BrilligOutput(foreign_call_result) => { + acvm.resolve_pending_foreign_call(foreign_call_result); + } + NargoForeignCallResult::ResolvedAssertMessage(message) => { + if assert_message.is_some() { + panic!("ahhh we should not be resolving another assert message as the VM should have failed"); + } + if !message.is_empty() { + assert_message = Some(message); + } + acvm.resolve_pending_foreign_call(ForeignCallResult::default()); + } + } } } } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 1b8018c4e7f..f600b3047fc 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,6 +1,6 @@ use acvm::{ acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, - pwg::{ACVMForeignCallResult, ForeignCallWaitInfo}, + pwg::ForeignCallWaitInfo, }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; @@ -9,7 +9,62 @@ pub trait ForeignCallExecutor { fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, - ) -> Result; + ) -> Result; +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum NargoForeignCallResult { + BrilligOutput(ForeignCallResult), + ResolvedAssertMessage(String), +} + +impl NargoForeignCallResult { + pub fn get_assert_message(self) -> Option { + match self { + Self::ResolvedAssertMessage(msg) => Some(msg), + _ => None, + } + } + + pub fn get_brillig_output(self) -> Option { + match self { + Self::BrilligOutput(foreign_call_result) => Some(foreign_call_result), + _ => None, + } + } +} + +impl From for NargoForeignCallResult { + fn from(value: ForeignCallResult) -> Self { + Self::BrilligOutput(value) + } +} + +impl From for NargoForeignCallResult { + fn from(value: String) -> Self { + Self::ResolvedAssertMessage(value) + } +} + +impl From for NargoForeignCallResult { + fn from(value: Value) -> Self { + let foreign_call_result: ForeignCallResult = value.into(); + foreign_call_result.into() + } +} + +impl From> for NargoForeignCallResult { + fn from(values: Vec) -> Self { + let foreign_call_result: ForeignCallResult = values.into(); + foreign_call_result.into() + } +} + +impl From> for NargoForeignCallResult { + fn from(values: Vec) -> Self { + let foreign_call_result: ForeignCallResult = values.into(); + foreign_call_result.into() + } } /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. @@ -149,7 +204,7 @@ impl DefaultForeignCallExecutor { fn execute_assert_message( foreign_call_inputs: &[ForeignCallParam], - ) -> Result { + ) -> Result { let display_string = Self::format_printable_value(foreign_call_inputs, true)?; Ok(display_string.into()) } @@ -170,7 +225,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, - ) -> Result { + ) -> Result { let foreign_call_name = foreign_call.function.as_str(); match ForeignCall::lookup(foreign_call_name) { Some(ForeignCall::Print) => { diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 397e163aaf0..5b8e981d0c0 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -193,34 +193,55 @@ fn compile_success_empty_{test_name}() {{ // but we must call a backend as part of querying the number of opcodes in the circuit. let test_program_dir = PathBuf::from("{test_dir}"); - let mut test_program_artifact = test_program_dir.clone(); - test_program_artifact.push("target"); - // TODO: We need more generalized handling for workspaces in this test - if "{test_name}" == "workspace_reexport_bug" {{ - test_program_artifact.push("binary"); - }} else {{ - test_program_artifact.push("{test_name}"); - }} - test_program_artifact.set_extension("json"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("compile").arg("--force"); + cmd.arg("info"); + cmd.arg("--json"); + cmd.arg("--force"); - cmd.assert().success(); + let output = cmd.output().expect("Failed to execute command"); - let input_string = - std::fs::read(&test_program_artifact).unwrap_or_else(|_| panic!("Failed to read program artifact")); - let program: nargo::artifacts::program::ProgramArtifact = serde_json::from_slice(&input_string).unwrap_or_else(|_| panic!("Failed to serialize program artifact")); - - let mut only_brillig = true; - for opcode in program.bytecode.opcodes.iter() {{ - if !matches!(opcode, acvm::acir::circuit::Opcode::Brillig(_)) {{ - only_brillig = false; - }} + if !output.status.success() {{ + panic!("`nargo info` failed with: {{}}", String::from_utf8(output.stderr).unwrap_or_default()); }} - assert_eq!(only_brillig, true); + + // `compile_success_empty` tests should be able to compile down to an empty circuit. + let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|_| {{ + panic!("JSON was not well-formatted {{:?}}",output.stdout) + }}); + let num_opcodes = &json["programs"][0]["acir_opcodes"]; + assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); + + // let mut test_program_artifact = test_program_dir.clone(); + // test_program_artifact.push("target"); + // // TODO: We need more generalized handling for workspaces in this test + // if "{test_name}" == "workspace_reexport_bug" {{ + // test_program_artifact.push("binary"); + // }} else {{ + // test_program_artifact.push("{test_name}"); + // }} + // test_program_artifact.set_extension("json"); + + // let mut cmd = Command::cargo_bin("nargo").unwrap(); + // cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); + // cmd.arg("--program-dir").arg(test_program_dir); + // cmd.arg("compile").arg("--force"); + + // cmd.assert().success(); + + // let input_string = + // std::fs::read(&test_program_artifact).unwrap_or_else(|_| panic!("Failed to read program artifact")); + // let program: nargo::artifacts::program::ProgramArtifact = serde_json::from_slice(&input_string).unwrap_or_else(|_| panic!("Failed to serialize program artifact")); + + // let mut only_brillig = true; + // for opcode in program.bytecode.opcodes.iter() {{ + // if !matches!(opcode, acvm::acir::circuit::Opcode::Brillig(_)) {{ + // only_brillig = false; + // }} + // }} + // assert_eq!(only_brillig, true); }} "#, test_dir = test_dir.display(), From 1a3ea85f3db87378d7e3a48dd0010304c1324b55 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 30 Jan 2024 19:38:54 +0000 Subject: [PATCH 30/64] accept changes --- tooling/nargo/src/ops/execute.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 829394089da..3e87284d327 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -16,12 +16,12 @@ pub fn execute_circuit( foreign_call_executor: &mut F, ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - // TODO: remove only for debugging - let mut num_foreign_calls = 0; + + // This message will be resolved by a nargo foreign call only when we have an unsatisfied assertion. let mut assert_message: Option = None; loop { let solver_status = acvm.solve(); - // dbg!(acvm.get_assert_message()); + match solver_status { ACVMStatus::Solved => break, ACVMStatus::InProgress => { @@ -42,7 +42,7 @@ pub fn execute_circuit( return Err(NargoError::ExecutionError(match call_stack { Some(call_stack) => { // First check whether we have a runtime assertion message that should be returned on an ACVM failure - // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded + // If we do not have a runtime assertion message, we should check whether the circuit has any harcoded // messages associated with a specific `OpcodeLocation`. // Otherwise return the provided opcode resolution error. if let Some(assert_message) = assert_message { From 480297a1b175004f3190876879e0c8dd42524db4 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 30 Jan 2024 19:39:24 +0000 Subject: [PATCH 31/64] missing merge edit --- tooling/nargo/src/ops/execute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 3e87284d327..e48bb006daf 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -42,7 +42,7 @@ pub fn execute_circuit( return Err(NargoError::ExecutionError(match call_stack { Some(call_stack) => { // First check whether we have a runtime assertion message that should be returned on an ACVM failure - // If we do not have a runtime assertion message, we should check whether the circuit has any harcoded + // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded // messages associated with a specific `OpcodeLocation`. // Otherwise return the provided opcode resolution error. if let Some(assert_message) = assert_message { From 8de95390c49502d1c394017dcc22a5d437891874 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 30 Jan 2024 19:41:15 +0000 Subject: [PATCH 32/64] remove unused imports --- acvm-repo/acvm/src/pwg/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index a8fa3342c1d..e81991576c1 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use acir::{ - brillig::{ForeignCallParam, ForeignCallResult, Value}, + brillig::ForeignCallResult, circuit::{opcodes::BlockId, Opcode, OpcodeLocation}, native_types::{Expression, Witness, WitnessMap}, BlackBoxFunc, FieldElement, From 86cab043ee7a3c95596320f8a2e35b766a9b581a Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 30 Jan 2024 19:59:15 +0000 Subject: [PATCH 33/64] remove old dbg --- tooling/nargo/src/ops/execute.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index e48bb006daf..b153c4dae46 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -28,7 +28,6 @@ pub fn execute_circuit( unreachable!("Execution should not stop while in `InProgress` state.") } ACVMStatus::Failure(error) => { - dbg!(num_foreign_calls); let call_stack = match &error { OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Resolved(opcode_location), From 7afc3175a8ae786727a5dc6125d2268f353bf4b7 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 01:46:38 +0000 Subject: [PATCH 34/64] switch assert_message call instruction to be atomic with SSA and add a predicate check --- .../src/brillig/brillig_gen/brillig_block.rs | 34 +++++ .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 139 ++++++++++++------ .../src/ssa/function_builder/mod.rs | 5 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 79 ++++++++-- .../src/ssa/opt/bubble_up_constrains.rs | 8 +- .../src/ssa/opt/defunctionalize.rs | 36 ++++- .../src/ssa/ssa_gen/context.rs | 13 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 39 ++++- .../src/hir/resolution/resolver.rs | 8 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 2 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- .../src/monomorphization/ast.rs | 2 +- .../src/monomorphization/mod.rs | 5 +- .../assert_msg_runtime/src/main.nr | 4 +- tooling/nargo/src/ops/execute.rs | 10 +- tooling/nargo_cli/build.rs | 29 ---- 16 files changed, 292 insertions(+), 123 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index b084042981b..dd5f8edd04e 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -3,6 +3,7 @@ use crate::brillig::brillig_ir::{ BrilligBinaryOp, BrilligContext, BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, }; use crate::ssa::ir::dfg::CallStack; +use crate::ssa::ir::instruction::SsaError; use crate::ssa::ir::{ basic_block::{BasicBlock, BasicBlockId}, dfg::DataFlowGraph, @@ -265,6 +266,39 @@ impl<'block> BrilligBlock<'block> { condition, ); + let assert_message = if let Some(error) = assert_message { + match error.as_ref() { + SsaError::Static(string) => Some(string.clone()), + SsaError::Dynamic(call_instruction) => { + // self.convert_ssa_call( + // call_instruction, + // dfg, + // ssa, + // brillig, + // last_array_uses, + // &[], + // ); + match call_instruction { + Instruction::Call { func, arguments } => match &dfg[*func] { + Value::Function(func_id) => { + self.convert_ssa_function_call( + *func_id, + arguments, + dfg, + instruction_id, + ); + } + _ => panic!("ahhh expected a func"), + }, + _ => panic!("ahhhhhh should have a call"), + } + None + } + } + } else { + None + }; + self.brillig_context.constrain_instruction(condition, assert_message.clone()); self.brillig_context.deallocate_register(condition); } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index d832b8d0fbb..bc7ae896807 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -7,6 +7,7 @@ use std::fmt::Debug; use self::acir_ir::acir_variable::{AcirContext, AcirType, AcirVar}; use super::function_builder::data_bus::DataBus; use super::ir::dfg::CallStack; +use super::ir::instruction::SsaError; use super::{ ir::{ dfg::DataFlowGraph, @@ -401,8 +402,8 @@ impl Context { brillig: &Brillig, last_array_uses: &HashMap, ) -> Result, RuntimeError> { - let instruction = &dfg[instruction_id]; self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); + let instruction = &dfg[instruction_id]; let mut warnings = Vec::new(); match instruction { Instruction::Binary(binary) => { @@ -413,16 +414,95 @@ impl Context { let lhs = self.convert_numeric_value(*lhs, dfg)?; let rhs = self.convert_numeric_value(*rhs, dfg)?; - self.acir_context.assert_eq_var(lhs, rhs, assert_message.clone())?; + let assert_message = if let Some(error) = assert_message { + match error.as_ref() { + SsaError::Static(string) => Some(string.clone()), + SsaError::Dynamic(call_instruction) => { + self.convert_ssa_call(call_instruction, dfg, ssa, brillig, &[])?; + None + } + } + } else { + None + }; + + self.acir_context.assert_eq_var(lhs, rhs, assert_message)?; } Instruction::Cast(value_id, _) => { let acir_var = self.convert_numeric_value(*value_id, dfg)?; self.define_result_var(dfg, instruction_id, acir_var); } - Instruction::Call { func, arguments } => { + Instruction::Call { .. } => { let result_ids = dfg.instruction_results(instruction_id); + warnings.extend(self.convert_ssa_call( + instruction, + dfg, + ssa, + brillig, + result_ids, + )?); + } + Instruction::Not(value_id) => { + let (acir_var, typ) = match self.convert_value(*value_id, dfg) { + AcirValue::Var(acir_var, typ) => (acir_var, typ), + _ => unreachable!("NOT is only applied to numerics"), + }; + let result_acir_var = self.acir_context.not_var(acir_var, typ)?; + self.define_result_var(dfg, instruction_id, result_acir_var); + } + Instruction::Truncate { value, bit_size, max_bit_size } => { + let result_acir_var = + self.convert_ssa_truncate(*value, *bit_size, *max_bit_size, dfg)?; + self.define_result_var(dfg, instruction_id, result_acir_var); + } + Instruction::EnableSideEffects { condition } => { + let acir_var = self.convert_numeric_value(*condition, dfg)?; + self.current_side_effects_enabled_var = acir_var; + } + Instruction::ArrayGet { .. } | Instruction::ArraySet { .. } => { + self.handle_array_operation(instruction_id, dfg, last_array_uses)?; + } + Instruction::Allocate => { + unreachable!("Expected all allocate instructions to be removed before acir_gen") + } + Instruction::Store { .. } => { + unreachable!("Expected all store instructions to be removed before acir_gen") + } + Instruction::Load { .. } => { + unreachable!("Expected all load instructions to be removed before acir_gen") + } + Instruction::IncrementRc { .. } => { + // Do nothing. Only Brillig needs to worry about reference counted arrays + } + Instruction::RangeCheck { value, max_bit_size, assert_message } => { + let acir_var = self.convert_numeric_value(*value, dfg)?; + self.acir_context.range_constrain_var( + acir_var, + &NumericType::Unsigned { bit_size: *max_bit_size }, + assert_message.clone(), + )?; + } + } + + self.acir_context.set_call_stack(CallStack::new()); + Ok(warnings) + } + + fn convert_ssa_call( + &mut self, + instruction: &Instruction, + dfg: &DataFlowGraph, + ssa: &Ssa, + brillig: &Brillig, + result_ids: &[ValueId], + ) -> Result, RuntimeError> { + let mut warnings = Vec::new(); + + match instruction { + Instruction::Call { func, arguments } => { match &dfg[*func] { Value::Function(id) => { + dbg!("got here"); let func = &ssa.functions[id]; match func.runtime() { RuntimeType::Acir => unimplemented!( @@ -495,51 +575,14 @@ impl Context { Value::ForeignFunction(_) => unreachable!( "All `oracle` methods should be wrapped in an unconstrained fn" ), - _ => unreachable!("expected calling a function"), + _ => { + dbg!(&dfg[*func]); + unreachable!("expected calling a function") + } } } - Instruction::Not(value_id) => { - let (acir_var, typ) = match self.convert_value(*value_id, dfg) { - AcirValue::Var(acir_var, typ) => (acir_var, typ), - _ => unreachable!("NOT is only applied to numerics"), - }; - let result_acir_var = self.acir_context.not_var(acir_var, typ)?; - self.define_result_var(dfg, instruction_id, result_acir_var); - } - Instruction::Truncate { value, bit_size, max_bit_size } => { - let result_acir_var = - self.convert_ssa_truncate(*value, *bit_size, *max_bit_size, dfg)?; - self.define_result_var(dfg, instruction_id, result_acir_var); - } - Instruction::EnableSideEffects { condition } => { - let acir_var = self.convert_numeric_value(*condition, dfg)?; - self.current_side_effects_enabled_var = acir_var; - } - Instruction::ArrayGet { .. } | Instruction::ArraySet { .. } => { - self.handle_array_operation(instruction_id, dfg, last_array_uses)?; - } - Instruction::Allocate => { - unreachable!("Expected all allocate instructions to be removed before acir_gen") - } - Instruction::Store { .. } => { - unreachable!("Expected all store instructions to be removed before acir_gen") - } - Instruction::Load { .. } => { - unreachable!("Expected all load instructions to be removed before acir_gen") - } - Instruction::IncrementRc { .. } => { - // Do nothing. Only Brillig needs to worry about reference counted arrays - } - Instruction::RangeCheck { value, max_bit_size, assert_message } => { - let acir_var = self.convert_numeric_value(*value, dfg)?; - self.acir_context.range_constrain_var( - acir_var, - &NumericType::Unsigned { bit_size: *max_bit_size }, - assert_message.clone(), - )?; - } + _ => unreachable!("expected calling a call instruction"), } - self.acir_context.set_call_stack(CallStack::new()); Ok(warnings) } @@ -1332,7 +1375,11 @@ impl Context { Value::ForeignFunction(_) => unimplemented!( "Oracle calls directly in constrained functions are not yet available." ), - Value::Instruction { .. } | Value::Param { .. } => { + Value::Instruction { instruction, .. } => { + dbg!(&dfg[*instruction]); + unreachable!("ICE: Should have been in cache {value_id} {value:?}") + } + Value::Param { .. } => { unreachable!("ICE: Should have been in cache {value_id} {value:?}") } }; diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 44be423be10..ea98a2962b3 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -18,7 +18,7 @@ use super::{ basic_block::BasicBlock, dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, - instruction::{Endian, InstructionId, Intrinsic}, + instruction::{Endian, InstructionId, Intrinsic, SsaError}, types::NumericType, }, ssa_gen::Ssa, @@ -250,7 +250,7 @@ impl FunctionBuilder { &mut self, lhs: ValueId, rhs: ValueId, - assert_message: Option, + assert_message: Option>, ) { self.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message), None); } @@ -262,6 +262,7 @@ impl FunctionBuilder { max_bit_size: u32, assert_message: Option, ) { + // let assert_message = assert_message.map(|msg_string| Box::new(SsaError::Static(msg_string))); self.insert_instruction( Instruction::RangeCheck { value, max_bit_size, assert_message }, None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index dbad562b6f4..97e2ff31ae1 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -151,7 +151,7 @@ pub(crate) enum Instruction { Truncate { value: ValueId, bit_size: u32, max_bit_size: u32 }, /// Constrains two values to be equal to one another. - Constrain(ValueId, ValueId, Option), + Constrain(ValueId, ValueId, Option>), /// Range constrain `value` to `max_bit_size` RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, @@ -320,7 +320,17 @@ impl Instruction { max_bit_size: *max_bit_size, }, Instruction::Constrain(lhs, rhs, assert_message) => { - Instruction::Constrain(f(*lhs), f(*rhs), assert_message.clone()) + // Must do this as the value is moved with the closure + let lhs = f(*lhs); + let rhs = f(*rhs); + let assert_message = assert_message.as_ref().map(|error| match error.as_ref() { + SsaError::Dynamic(call_instr) => { + let new_instr = call_instr.map_values(f); + Box::new(SsaError::Dynamic(new_instr)) + } + _ => error.clone(), + }); + Instruction::Constrain(lhs, rhs, assert_message) } Instruction::Call { func, arguments } => Instruction::Call { func: f(*func), @@ -370,9 +380,14 @@ impl Instruction { | Instruction::Load { address: value } => { f(*value); } - Instruction::Constrain(lhs, rhs, _) => { + Instruction::Constrain(lhs, rhs, assert_error) => { f(*lhs); f(*rhs); + if let Some(error) = assert_error.as_ref() { + if let SsaError::Dynamic(call_instr) = error.as_ref() { + call_instr.for_each_value(f); + } + } } Instruction::Store { address, value } => { @@ -435,7 +450,7 @@ impl Instruction { } } Instruction::Constrain(lhs, rhs, msg) => { - let constraints = decompose_constrain(*lhs, *rhs, msg.clone(), dfg); + let constraints = decompose_constrain(*lhs, *rhs, msg, dfg); if constraints.is_empty() { Remove } else { @@ -545,6 +560,26 @@ impl Instruction { } } +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub(crate) enum SsaError { + // These are errors which have been hardcoded during SSA gen + Static(String), + // These are errors which come from runtime expressions specified by a Noir program + Dynamic(Instruction), +} + +impl From for SsaError { + fn from(value: String) -> Self { + SsaError::Static(value) + } +} + +impl From for Box { + fn from(value: String) -> Self { + Box::new(SsaError::Static(value)) + } +} + /// Try to simplify this cast instruction. If the instruction can be simplified to a known value, /// that value is returned. Otherwise None is returned. fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> SimplifyResult { @@ -600,11 +635,35 @@ fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> Sim fn decompose_constrain( lhs: ValueId, rhs: ValueId, - msg: Option, + msg: &Option>, dfg: &mut DataFlowGraph, ) -> Vec { let lhs = dfg.resolve(lhs); let rhs = dfg.resolve(rhs); + let msg = msg.clone(); + // let msg = if let Some(msg) = msg { + // match msg.as_ref() { + // SsaError::Dynamic(call_instruction) => { + + // } + // _ => { + // Some(*msg) + // } + // } + // } else { + // None + // }; + // dbg!(msg.clone()); + // let msg = msg.as_ref().map(|error| { + // match error.as_ref() { + // SsaError::Dynamic(call_instr) => { + // let new_instr = call_instr.map_values(|value| dfg.resolve(value)); + // Box::new(SsaError::Dynamic(new_instr)) + // } + // _ => error.clone() + // } + // }); + // dbg!(msg.clone()); if lhs == rhs { // Remove trivial case `assert_eq(x, x)` @@ -657,8 +716,8 @@ fn decompose_constrain( let one = dfg.make_constant(one, Type::bool()); [ - decompose_constrain(lhs, one, msg.clone(), dfg), - decompose_constrain(rhs, one, msg, dfg), + decompose_constrain(lhs, one, &msg, dfg), + decompose_constrain(rhs, one, &msg, dfg), ] .concat() } @@ -685,8 +744,8 @@ fn decompose_constrain( let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); [ - decompose_constrain(lhs, zero, msg.clone(), dfg), - decompose_constrain(rhs, zero, msg, dfg), + decompose_constrain(lhs, zero, &msg, dfg), + decompose_constrain(rhs, zero, &msg, dfg), ] .concat() } @@ -706,7 +765,7 @@ fn decompose_constrain( // will likely be removed through dead instruction elimination. let reversed_constant = FieldElement::from(!constant.is_one()); let reversed_constant = dfg.make_constant(reversed_constant, Type::bool()); - decompose_constrain(value, reversed_constant, msg, dfg) + decompose_constrain(value, reversed_constant, &msg, dfg) } _ => vec![Instruction::Constrain(lhs, rhs, msg)], diff --git a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs index 8a903cbd87b..b737c51d145 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs @@ -109,11 +109,11 @@ mod test { let v1 = builder.insert_binary(v0, BinaryOp::Add, one); let v2 = builder.insert_binary(v1, BinaryOp::Add, one); - builder.insert_constrain(v0, one, Some("With message".to_string())); + builder.insert_constrain(v0, one, Some("With message".to_string().into())); builder.insert_constrain(v2, three, None); builder.insert_constrain(v0, one, None); builder.insert_constrain(v1, two, None); - builder.insert_constrain(v1, two, Some("With message".to_string())); + builder.insert_constrain(v1, two, Some("With message".to_string().into())); builder.terminate_with_return(vec![]); let ssa = builder.finish(); @@ -137,11 +137,11 @@ mod test { assert_eq!(block.instructions().len(), 7); let expected_instructions = vec![ - Instruction::Constrain(v0, one, Some("With message".to_string())), + Instruction::Constrain(v0, one, Some("With message".to_string().into())), Instruction::Constrain(v0, one, None), Instruction::Binary(Binary { lhs: v0, rhs: one, operator: BinaryOp::Add }), Instruction::Constrain(v1, two, None), - Instruction::Constrain(v1, two, Some("With message".to_string())), + Instruction::Constrain(v1, two, Some("With message".to_string().into())), Instruction::Binary(Binary { lhs: v1, rhs: one, operator: BinaryOp::Add }), Instruction::Constrain(v2, three, None), ]; diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index b7f154397a6..269312da9d0 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -14,7 +14,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, function::{Function, FunctionId, RuntimeType, Signature}, - instruction::{BinaryOp, Instruction}, + instruction::{BinaryOp, Instruction, SsaError}, types::{NumericType, Type}, value::{Value, ValueId}, }, @@ -86,9 +86,24 @@ impl DefunctionalizationContext { let instruction = func.dfg[instruction_id].clone(); let mut replacement_instruction = None; // Operate on call instructions - let (target_func_id, mut arguments) = match instruction { + let (target_func_id, mut arguments) = match &instruction { Instruction::Call { func: target_func_id, arguments } => { - (target_func_id, arguments) + (*target_func_id, arguments.clone()) + } + Instruction::Constrain(_, _, constrain_error) => { + if let Some(error) = constrain_error { + if let SsaError::Dynamic(Instruction::Call { + func: target_func_id, + arguments, + }) = error.as_ref() + { + (*target_func_id, arguments.clone()) + } else { + continue; + } + } else { + continue; + } } _ => continue, }; @@ -120,7 +135,20 @@ impl DefunctionalizationContext { } _ => {} } - if let Some(new_instruction) = replacement_instruction { + if let Some(mut new_instruction) = replacement_instruction { + if let Instruction::Constrain(lhs, rhs, constrain_error_call) = instruction { + let new_error_call = if let Some(error) = constrain_error_call { + match error.as_ref() { + SsaError::Dynamic(_) => { + Some(Box::new(SsaError::Dynamic(new_instruction))) + } + _ => None, + } + } else { + None + }; + new_instruction = Instruction::Constrain(lhs, rhs, new_error_call); + } func.dfg[instruction_id] = new_instruction; } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 3c05f52ad50..adf68bacba4 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -435,7 +435,7 @@ impl<'a> FunctionContext<'a> { self.builder.set_location(location).insert_constrain( sign, one, - Some("attempt to bit-shift with overflow".to_owned()), + Some("attempt to bit-shift with overflow".to_owned().into()), ); } @@ -446,7 +446,7 @@ impl<'a> FunctionContext<'a> { self.builder.set_location(location).insert_constrain( overflow, one, - Some("attempt to bit-shift with overflow".to_owned()), + Some("attempt to bit-shift with overflow".to_owned().into()), ); self.builder.insert_truncate(result, bit_size, bit_size + 1) } @@ -498,8 +498,11 @@ impl<'a> FunctionContext<'a> { let sign_diff = self.builder.insert_binary(result_sign, BinaryOp::Eq, lhs_sign); let sign_diff_with_predicate = self.builder.insert_binary(sign_diff, BinaryOp::Mul, same_sign); - let overflow_check = - Instruction::Constrain(sign_diff_with_predicate, same_sign, Some(message)); + let overflow_check = Instruction::Constrain( + sign_diff_with_predicate, + same_sign, + Some(message.into()), + ); self.builder.set_location(location).insert_instruction(overflow_check, None); } BinaryOpKind::Multiply => { @@ -529,7 +532,7 @@ impl<'a> FunctionContext<'a> { self.builder.set_location(location).insert_constrain( product_overflow_check, one, - Some(message), + Some(message.into()), ); } _ => unreachable!("operator {} should not overflow", operator), diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index f2881e09ebc..8a40e141e9d 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -29,7 +29,7 @@ use super::{ function_builder::data_bus::DataBus, ir::{ function::RuntimeType, - instruction::{BinaryOp, TerminatorInstruction}, + instruction::{BinaryOp, Instruction, SsaError, TerminatorInstruction}, types::Type, value::ValueId, }, @@ -442,7 +442,7 @@ impl<'a> FunctionContext<'a> { self.builder.insert_constrain( is_offset_out_of_bounds, true_const, - Some("Index out of bounds".to_owned()), + Some(Box::new("Index out of bounds".to_owned().into())), ); } @@ -666,16 +666,45 @@ impl<'a> FunctionContext<'a> { &mut self, expr: &Expression, location: Location, - assert_message: &Expression, + assert_message: &Option>, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); - self.codegen_expression(assert_message)?; + // self.codegen_expression(assert_message)?; + + let assert_message = if let Some(assert_message_expr) = assert_message { + let expr = assert_message_expr.as_ref(); + match expr { + ast::Expression::Call(call) => { + let func = self.codegen_non_tuple_expression(&call.func)?; + let mut arguments = Vec::with_capacity(call.arguments.len()); + + for argument in &call.arguments { + let mut values = self.codegen_expression(argument)?.into_value_list(self); + arguments.append(&mut values); + } + + // If an array is passed as an argument we increase its reference count + for argument in &arguments { + self.builder.increment_array_reference_count(*argument); + } + // let result_types = Self::convert_type(&call.return_type).flatten(); + // assert!(result_types.is_empty()); + let instr = Instruction::Call { func, arguments }; + Some(Box::new(SsaError::Dynamic(instr))) + } + _ => { + panic!("ahh expected expression"); + } + } + } else { + None + }; // Assert messages from constrain statements specified by the user are codegen'd with a call expression, // thus the assert_message field here should always be `None` - self.builder.set_location(location).insert_constrain(expr, true_literal, None); + self.builder.set_location(location).insert_constrain(expr, true_literal, assert_message); Ok(Self::unit_value()) } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index dce99341c3b..5fef8eed278 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1181,13 +1181,11 @@ impl<'a> Resolver<'a> { assert_message_expr: Option, span: Span, condition: Expression, - ) -> ExprId { + ) -> Option { let mut assert_msg_call_args = if let Some(assert_message_expr) = assert_message_expr { vec![assert_message_expr.clone()] } else { - let kind = ExpressionKind::string("".to_owned()); - let arg = Expression { kind, span: Span::default() }; - vec![arg] + return None; }; assert_msg_call_args.push(condition); @@ -1210,7 +1208,7 @@ impl<'a> Resolver<'a> { assert_msg_call_args, span, ); - self.resolve_expression(assert_msg_call_expr) + Some(self.resolve_expression(assert_msg_call_expr)) } pub fn intern_stmt(&mut self, stmt: StatementKind) -> StmtId { diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index b72ef5ce364..a8ac17715fe 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -306,7 +306,7 @@ impl<'interner> TypeChecker<'interner> { let expr_span = self.interner.expr_span(&stmt.0); // Must type check the assertion message expression so that we instantiate bindings - self.check_expression(&stmt.2); + stmt.2.map(|assert_msg_expr| self.check_expression(&assert_msg_expr)); self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 90fb5024ceb..4d946690151 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -55,7 +55,7 @@ pub struct HirAssignStatement { /// originates from. This is used later in the SSA pass to issue /// an error if a constrain is found to be always false. #[derive(Debug, Clone)] -pub struct HirConstrainStatement(pub ExprId, pub FileId, pub ExprId); +pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); #[derive(Debug, Clone, Hash)] pub enum HirPattern { diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index f69c7794f52..5172eabde46 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -31,7 +31,7 @@ pub enum Expression { ExtractTupleField(Box, usize), Call(Call), Let(Let), - Constrain(Box, Location, Box), + Constrain(Box, Location, Option>), Assign(Assign), Semi(Box), } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index cd9f491918e..663731d462c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -479,8 +479,9 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Constrain(constrain) => { let expr = self.expr(constrain.0); let location = self.interner.expr_location(&constrain.0); - let assert_message = self.expr(constrain.2); - ast::Expression::Constrain(Box::new(expr), location, Box::new(assert_message)) + let assert_message = + constrain.2.map(|assert_msg_expr| Box::new(self.expr(assert_msg_expr))); + ast::Expression::Constrain(Box::new(expr), location, assert_message) } HirStatement::Assign(assign) => self.assign(assign), HirStatement::For(for_loop) => { diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/compile_failure/assert_msg_runtime/src/main.nr index 0aa9e4ef3a8..8a3bffd41ee 100644 --- a/test_programs/compile_failure/assert_msg_runtime/src/main.nr +++ b/test_programs/compile_failure/assert_msg_runtime/src/main.nr @@ -5,7 +5,7 @@ fn main(x: Field, y: pub Field) { assert(x != y, f"Expected x != y, but got both equal {x}"); assert(x != y, f"Expected x != y, but got both equal {x}"); let z = x + y; - assert(z != y, f"Expected x != y, but got both equal {x}"); - // assert_eq(x, y); + assert(z != y, f"Expected z != y, but got both equal {z}"); + assert_eq(x, y); assert_eq(x, y, f"Expected x == y, but x is {x} and y is {y}"); } \ No newline at end of file diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index b153c4dae46..1c3b24b69a5 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -17,7 +17,7 @@ pub fn execute_circuit( ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - // This message will be resolved by a nargo foreign call only when we have an unsatisfied assertion. + // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. let mut assert_message: Option = None; loop { let solver_status = acvm.solve(); @@ -40,7 +40,7 @@ pub fn execute_circuit( return Err(NargoError::ExecutionError(match call_stack { Some(call_stack) => { - // First check whether we have a runtime assertion message that should be returned on an ACVM failure + // First check whether we have a runtime assertion message that should be resolved on an ACVM failure // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded // messages associated with a specific `OpcodeLocation`. // Otherwise return the provided opcode resolution error. @@ -59,7 +59,6 @@ pub fn execute_circuit( } ACVMStatus::RequiresForeignCall(foreign_call) => { let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - dbg!(foreign_call_result.clone()); match foreign_call_result { NargoForeignCallResult::BrilligOutput(foreign_call_result) => { acvm.resolve_pending_foreign_call(foreign_call_result); @@ -68,9 +67,8 @@ pub fn execute_circuit( if assert_message.is_some() { panic!("ahhh we should not be resolving another assert message as the VM should have failed"); } - if !message.is_empty() { - assert_message = Some(message); - } + assert_message = Some(message); + acvm.resolve_pending_foreign_call(ForeignCallResult::default()); } } diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 5b8e981d0c0..0c3df1faec7 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -213,35 +213,6 @@ fn compile_success_empty_{test_name}() {{ }}); let num_opcodes = &json["programs"][0]["acir_opcodes"]; assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); - - // let mut test_program_artifact = test_program_dir.clone(); - // test_program_artifact.push("target"); - // // TODO: We need more generalized handling for workspaces in this test - // if "{test_name}" == "workspace_reexport_bug" {{ - // test_program_artifact.push("binary"); - // }} else {{ - // test_program_artifact.push("{test_name}"); - // }} - // test_program_artifact.set_extension("json"); - - // let mut cmd = Command::cargo_bin("nargo").unwrap(); - // cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); - // cmd.arg("--program-dir").arg(test_program_dir); - // cmd.arg("compile").arg("--force"); - - // cmd.assert().success(); - - // let input_string = - // std::fs::read(&test_program_artifact).unwrap_or_else(|_| panic!("Failed to read program artifact")); - // let program: nargo::artifacts::program::ProgramArtifact = serde_json::from_slice(&input_string).unwrap_or_else(|_| panic!("Failed to serialize program artifact")); - - // let mut only_brillig = true; - // for opcode in program.bytecode.opcodes.iter() {{ - // if !matches!(opcode, acvm::acir::circuit::Opcode::Brillig(_)) {{ - // only_brillig = false; - // }} - // }} - // assert_eq!(only_brillig, true); }} "#, test_dir = test_dir.display(), From 55de71f1eefd7853da9f07e2bd23b41da4763773 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 01:48:11 +0000 Subject: [PATCH 35/64] cleanup decompose_constrain --- .../noirc_evaluator/src/ssa/ir/instruction.rs | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 97e2ff31ae1..4e1e7b2feec 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -640,30 +640,6 @@ fn decompose_constrain( ) -> Vec { let lhs = dfg.resolve(lhs); let rhs = dfg.resolve(rhs); - let msg = msg.clone(); - // let msg = if let Some(msg) = msg { - // match msg.as_ref() { - // SsaError::Dynamic(call_instruction) => { - - // } - // _ => { - // Some(*msg) - // } - // } - // } else { - // None - // }; - // dbg!(msg.clone()); - // let msg = msg.as_ref().map(|error| { - // match error.as_ref() { - // SsaError::Dynamic(call_instr) => { - // let new_instr = call_instr.map_values(|value| dfg.resolve(value)); - // Box::new(SsaError::Dynamic(new_instr)) - // } - // _ => error.clone() - // } - // }); - // dbg!(msg.clone()); if lhs == rhs { // Remove trivial case `assert_eq(x, x)` @@ -691,7 +667,7 @@ fn decompose_constrain( // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. - vec![Instruction::Constrain(lhs, rhs, msg)] + vec![Instruction::Constrain(lhs, rhs, msg.clone())] } Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Mul }) @@ -768,11 +744,11 @@ fn decompose_constrain( decompose_constrain(value, reversed_constant, &msg, dfg) } - _ => vec![Instruction::Constrain(lhs, rhs, msg)], + _ => vec![Instruction::Constrain(lhs, rhs, msg.clone())], } } - _ => vec![Instruction::Constrain(lhs, rhs, msg)], + _ => vec![Instruction::Constrain(lhs, rhs, msg.clone())], } } } From 3ebc34a9e8cb5566f1818e3a3c18d964e2a536e6 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 02:01:53 +0000 Subject: [PATCH 36/64] some comment cleanup --- .../src/brillig/brillig_gen/brillig_block.rs | 12 ++---------- compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs | 2 +- compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 7 +------ 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index dd5f8edd04e..3eb6a2e1efa 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -270,14 +270,6 @@ impl<'block> BrilligBlock<'block> { match error.as_ref() { SsaError::Static(string) => Some(string.clone()), SsaError::Dynamic(call_instruction) => { - // self.convert_ssa_call( - // call_instruction, - // dfg, - // ssa, - // brillig, - // last_array_uses, - // &[], - // ); match call_instruction { Instruction::Call { func, arguments } => match &dfg[*func] { Value::Function(func_id) => { @@ -288,9 +280,9 @@ impl<'block> BrilligBlock<'block> { instruction_id, ); } - _ => panic!("ahhh expected a func"), + _ => panic!("expected a function"), }, - _ => panic!("ahhhhhh should have a call"), + _ => panic!("expected a call"), } None } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index bc7ae896807..78a749280b4 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -402,8 +402,8 @@ impl Context { brillig: &Brillig, last_array_uses: &HashMap, ) -> Result, RuntimeError> { - self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); let instruction = &dfg[instruction_id]; + self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); let mut warnings = Vec::new(); match instruction { Instruction::Binary(binary) => { diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 8a40e141e9d..9baad6a0827 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -671,8 +671,6 @@ impl<'a> FunctionContext<'a> { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); - // self.codegen_expression(assert_message)?; - let assert_message = if let Some(assert_message_expr) = assert_message { let expr = assert_message_expr.as_ref(); match expr { @@ -689,8 +687,7 @@ impl<'a> FunctionContext<'a> { for argument in &arguments { self.builder.increment_array_reference_count(*argument); } - // let result_types = Self::convert_type(&call.return_type).flatten(); - // assert!(result_types.is_empty()); + let instr = Instruction::Call { func, arguments }; Some(Box::new(SsaError::Dynamic(instr))) } @@ -702,8 +699,6 @@ impl<'a> FunctionContext<'a> { None }; - // Assert messages from constrain statements specified by the user are codegen'd with a call expression, - // thus the assert_message field here should always be `None` self.builder.set_location(location).insert_constrain(expr, true_literal, assert_message); Ok(Self::unit_value()) From c0d3403f19210b967ab6173bce65e7d8c3798051 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 02:10:02 +0000 Subject: [PATCH 37/64] remove old comments --- tooling/nargo_cli/build.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 0c3df1faec7..45833ab9c80 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -162,10 +162,6 @@ fn noir_test_failure_{test_name}() {{ } } -/// TODO: Certain tests may have foreign calls leftover (such as assert message resolution) -/// TODO: even though all assertion and other logic has been optimized away. -/// TODO: We should determine a way to tie certain foreign calls to a constraint so they can be optimized away -/// TODO: with the constraint. fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "compile_success_empty"; let test_data_dir = test_data_dir.join(test_sub_dir); @@ -285,7 +281,7 @@ fn compile_failure_{test_name}() {{ let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); - cmd.arg("--program-dir").arg(test_program_dir.clone()); + cmd.arg("--program-dir").arg(test_program_dir); cmd.arg("execute").arg("--force"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); From 9944a83671d3cd3a851464c076bd97ecfee32144 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 02:18:19 +0000 Subject: [PATCH 38/64] cargo fmt --- compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 51f3a4cdba6..ffe946eb77b 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -7,7 +7,7 @@ use super::{Binary, BinaryOp, DataFlowGraph, Instruction, SsaError, Type, Value, pub(super) fn decompose_constrain( lhs: ValueId, rhs: ValueId, - msg: &Option>, + msg: &Option>, dfg: &mut DataFlowGraph, ) -> Vec { let lhs = dfg.resolve(lhs); From 282a7d789fcd8d30dc01b5404fcaea8da639fd12 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 15:25:02 +0000 Subject: [PATCH 39/64] reorganize codegen of constrain error --- .../src/brillig/brillig_gen/brillig_block.rs | 6 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 6 +- .../src/ssa/function_builder/mod.rs | 4 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 20 +++--- .../src/ssa/ir/instruction/constrain.rs | 4 +- .../src/ssa/opt/defunctionalize.rs | 8 +-- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 67 ++++++++++++------- 7 files changed, 66 insertions(+), 49 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 3eb6a2e1efa..5fd8b4af69f 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -3,7 +3,7 @@ use crate::brillig::brillig_ir::{ BrilligBinaryOp, BrilligContext, BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, }; use crate::ssa::ir::dfg::CallStack; -use crate::ssa::ir::instruction::SsaError; +use crate::ssa::ir::instruction::ConstrainError; use crate::ssa::ir::{ basic_block::{BasicBlock, BasicBlockId}, dfg::DataFlowGraph, @@ -268,8 +268,8 @@ impl<'block> BrilligBlock<'block> { let assert_message = if let Some(error) = assert_message { match error.as_ref() { - SsaError::Static(string) => Some(string.clone()), - SsaError::Dynamic(call_instruction) => { + ConstrainError::Static(string) => Some(string.clone()), + ConstrainError::Dynamic(call_instruction) => { match call_instruction { Instruction::Call { func, arguments } => match &dfg[*func] { Value::Function(func_id) => { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index d7a9ca2aa14..b2eda0760ce 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use self::acir_ir::acir_variable::{AcirContext, AcirType, AcirVar}; use super::function_builder::data_bus::DataBus; use super::ir::dfg::CallStack; -use super::ir::instruction::SsaError; +use super::ir::instruction::ConstrainError; use super::{ ir::{ dfg::DataFlowGraph, @@ -416,8 +416,8 @@ impl Context { let assert_message = if let Some(error) = assert_message { match error.as_ref() { - SsaError::Static(string) => Some(string.clone()), - SsaError::Dynamic(call_instruction) => { + ConstrainError::Static(string) => Some(string.clone()), + ConstrainError::Dynamic(call_instruction) => { self.convert_ssa_call(call_instruction, dfg, ssa, brillig, &[])?; None } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index ea98a2962b3..184ccb9a1e2 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -18,7 +18,7 @@ use super::{ basic_block::BasicBlock, dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, - instruction::{Endian, InstructionId, Intrinsic, SsaError}, + instruction::{ConstrainError, Endian, InstructionId, Intrinsic}, types::NumericType, }, ssa_gen::Ssa, @@ -250,7 +250,7 @@ impl FunctionBuilder { &mut self, lhs: ValueId, rhs: ValueId, - assert_message: Option>, + assert_message: Option>, ) { self.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message), None); } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 517339c5941..8d6fdd669db 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -157,7 +157,7 @@ pub(crate) enum Instruction { Truncate { value: ValueId, bit_size: u32, max_bit_size: u32 }, /// Constrains two values to be equal to one another. - Constrain(ValueId, ValueId, Option>), + Constrain(ValueId, ValueId, Option>), /// Range constrain `value` to `max_bit_size` RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, @@ -330,9 +330,9 @@ impl Instruction { let lhs = f(*lhs); let rhs = f(*rhs); let assert_message = assert_message.as_ref().map(|error| match error.as_ref() { - SsaError::Dynamic(call_instr) => { + ConstrainError::Dynamic(call_instr) => { let new_instr = call_instr.map_values(f); - Box::new(SsaError::Dynamic(new_instr)) + Box::new(ConstrainError::Dynamic(new_instr)) } _ => error.clone(), }); @@ -390,7 +390,7 @@ impl Instruction { f(*lhs); f(*rhs); if let Some(error) = assert_error.as_ref() { - if let SsaError::Dynamic(call_instr) = error.as_ref() { + if let ConstrainError::Dynamic(call_instr) = error.as_ref() { call_instr.for_each_value(f); } } @@ -567,22 +567,24 @@ impl Instruction { } #[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub(crate) enum SsaError { +pub(crate) enum ConstrainError { // These are errors which have been hardcoded during SSA gen Static(String), // These are errors which come from runtime expressions specified by a Noir program + // We store an `Instruction` as we want this Instruction to be atomic in SSA with + // a constrain instruction, and leave codegen of this instruction to lower level passes. Dynamic(Instruction), } -impl From for SsaError { +impl From for ConstrainError { fn from(value: String) -> Self { - SsaError::Static(value) + ConstrainError::Static(value) } } -impl From for Box { +impl From for Box { fn from(value: String) -> Self { - Box::new(SsaError::Static(value)) + Box::new(ConstrainError::Static(value)) } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index ffe946eb77b..0ae0f8f2892 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -1,13 +1,13 @@ use acvm::FieldElement; -use super::{Binary, BinaryOp, DataFlowGraph, Instruction, SsaError, Type, Value, ValueId}; +use super::{Binary, BinaryOp, ConstrainError, DataFlowGraph, Instruction, Type, Value, ValueId}; /// Try to decompose this constrain instruction. This constraint will be broken down such that it instead constrains /// all the values which are used to compute the values which were being constrained. pub(super) fn decompose_constrain( lhs: ValueId, rhs: ValueId, - msg: &Option>, + msg: &Option>, dfg: &mut DataFlowGraph, ) -> Vec { let lhs = dfg.resolve(lhs); diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index 269312da9d0..f0f32438864 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -14,7 +14,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, function::{Function, FunctionId, RuntimeType, Signature}, - instruction::{BinaryOp, Instruction, SsaError}, + instruction::{BinaryOp, ConstrainError, Instruction}, types::{NumericType, Type}, value::{Value, ValueId}, }, @@ -92,7 +92,7 @@ impl DefunctionalizationContext { } Instruction::Constrain(_, _, constrain_error) => { if let Some(error) = constrain_error { - if let SsaError::Dynamic(Instruction::Call { + if let ConstrainError::Dynamic(Instruction::Call { func: target_func_id, arguments, }) = error.as_ref() @@ -139,8 +139,8 @@ impl DefunctionalizationContext { if let Instruction::Constrain(lhs, rhs, constrain_error_call) = instruction { let new_error_call = if let Some(error) = constrain_error_call { match error.as_ref() { - SsaError::Dynamic(_) => { - Some(Box::new(SsaError::Dynamic(new_instruction))) + ConstrainError::Dynamic(_) => { + Some(Box::new(ConstrainError::Dynamic(new_instruction))) } _ => None, } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 9baad6a0827..6b11910801f 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -13,7 +13,7 @@ use noirc_frontend::{ }; use crate::{ - errors::RuntimeError, + errors::{InternalError, RuntimeError}, ssa::{ function_builder::data_bus::DataBusBuilder, ir::{instruction::Intrinsic, types::NumericType}, @@ -29,7 +29,7 @@ use super::{ function_builder::data_bus::DataBus, ir::{ function::RuntimeType, - instruction::{BinaryOp, Instruction, SsaError, TerminatorInstruction}, + instruction::{BinaryOp, ConstrainError, Instruction, TerminatorInstruction}, types::Type, value::ValueId, }, @@ -671,37 +671,52 @@ impl<'a> FunctionContext<'a> { let expr = self.codegen_non_tuple_expression(expr)?; let true_literal = self.builder.numeric_constant(true, Type::bool()); - let assert_message = if let Some(assert_message_expr) = assert_message { - let expr = assert_message_expr.as_ref(); - match expr { - ast::Expression::Call(call) => { - let func = self.codegen_non_tuple_expression(&call.func)?; - let mut arguments = Vec::with_capacity(call.arguments.len()); + // Set the location here for any errors that may occur when we codegen the assert message + self.builder.set_location(location); - for argument in &call.arguments { - let mut values = self.codegen_expression(argument)?.into_value_list(self); - arguments.append(&mut values); - } + let assert_message = self.codegen_constrain_error(assert_message)?; - // If an array is passed as an argument we increase its reference count - for argument in &arguments { - self.builder.increment_array_reference_count(*argument); - } + self.builder.insert_constrain(expr, true_literal, assert_message); + + Ok(Self::unit_value()) + } - let instr = Instruction::Call { func, arguments }; - Some(Box::new(SsaError::Dynamic(instr))) + // This method does not necessary codegen the full assert message expression, thus it does not + // return a `Values` object. Instead we check the internals of the expression to make sure + // we have an `Expression::Call` as expected. An `Instruction::Call` is then constructed but not + // inserted to the SSA as we want that instruction to be atomic in SSA with a constrain instruction. + fn codegen_constrain_error( + &mut self, + assert_message: &Option>, + ) -> Result>, RuntimeError> { + if let Some(assert_message_expr) = assert_message { + if let ast::Expression::Call(call) = assert_message_expr.as_ref() { + let func = self.codegen_non_tuple_expression(&call.func)?; + let mut arguments = Vec::with_capacity(call.arguments.len()); + + for argument in &call.arguments { + let mut values = self.codegen_expression(argument)?.into_value_list(self); + arguments.append(&mut values); } - _ => { - panic!("ahh expected expression"); + + // If an array is passed as an argument we increase its reference count + for argument in &arguments { + self.builder.increment_array_reference_count(*argument); } + + let instr = Instruction::Call { func, arguments }; + Ok(Some(Box::new(ConstrainError::Dynamic(instr)))) + } else { + Err(InternalError::Unexpected { + expected: "Expected a call expression".to_owned(), + found: "Instead found {expr:?}".to_owned(), + call_stack: self.builder.get_call_stack(), + } + .into()) } } else { - None - }; - - self.builder.set_location(location).insert_constrain(expr, true_literal, assert_message); - - Ok(Self::unit_value()) + Ok(None) + } } fn codegen_assign(&mut self, assign: &ast::Assign) -> Result { From 4cd925534fa4e82d6e14446a7482118c3a1a24f7 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 15:41:50 +0000 Subject: [PATCH 40/64] defunc cleanup --- compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs | 15 ++++----------- .../src/ssa/function_builder/mod.rs | 1 - .../noirc_evaluator/src/ssa/ir/instruction.rs | 4 ++-- .../src/ssa/opt/defunctionalize.rs | 7 ++++--- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index b2eda0760ce..5d067737930 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -500,9 +500,9 @@ impl Context { match instruction { Instruction::Call { func, arguments } => { - match &dfg[*func] { + let function_value = &dfg[*func]; + match function_value { Value::Function(id) => { - dbg!("got here"); let func = &ssa.functions[id]; match func.runtime() { RuntimeType::Acir => unimplemented!( @@ -575,10 +575,7 @@ impl Context { Value::ForeignFunction(_) => unreachable!( "All `oracle` methods should be wrapped in an unconstrained fn" ), - _ => { - dbg!(&dfg[*func]); - unreachable!("expected calling a function") - } + _ => unreachable!("expected calling a function but got {function_value:?}"), } } _ => unreachable!("expected calling a call instruction"), @@ -1375,11 +1372,7 @@ impl Context { Value::ForeignFunction(_) => unimplemented!( "Oracle calls directly in constrained functions are not yet available." ), - Value::Instruction { instruction, .. } => { - dbg!(&dfg[*instruction]); - unreachable!("ICE: Should have been in cache {value_id} {value:?}") - } - Value::Param { .. } => { + Value::Instruction { .. } | Value::Param { .. } => { unreachable!("ICE: Should have been in cache {value_id} {value:?}") } }; diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 184ccb9a1e2..1cac0aa67ed 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -262,7 +262,6 @@ impl FunctionBuilder { max_bit_size: u32, assert_message: Option, ) { - // let assert_message = assert_message.map(|msg_string| Box::new(SsaError::Static(msg_string))); self.insert_instruction( Instruction::RangeCheck { value, max_bit_size, assert_message }, None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 8d6fdd669db..59f88fe99eb 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -326,7 +326,7 @@ impl Instruction { max_bit_size: *max_bit_size, }, Instruction::Constrain(lhs, rhs, assert_message) => { - // Must do this as the value is moved with the closure + // Must map the `lhs` and `rhs` first as the value `f` is moved with the closure let lhs = f(*lhs); let rhs = f(*rhs); let assert_message = assert_message.as_ref().map(|error| match error.as_ref() { @@ -584,7 +584,7 @@ impl From for ConstrainError { impl From for Box { fn from(value: String) -> Self { - Box::new(ConstrainError::Static(value)) + Box::new(value.into()) } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index f0f32438864..a033ea7873d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -86,9 +86,9 @@ impl DefunctionalizationContext { let instruction = func.dfg[instruction_id].clone(); let mut replacement_instruction = None; // Operate on call instructions - let (target_func_id, mut arguments) = match &instruction { + let (target_func_id, arguments) = match &instruction { Instruction::Call { func: target_func_id, arguments } => { - (*target_func_id, arguments.clone()) + (*target_func_id, arguments) } Instruction::Constrain(_, _, constrain_error) => { if let Some(error) = constrain_error { @@ -97,7 +97,7 @@ impl DefunctionalizationContext { arguments, }) = error.as_ref() { - (*target_func_id, arguments.clone()) + (*target_func_id, arguments) } else { continue; } @@ -111,6 +111,7 @@ impl DefunctionalizationContext { match func.dfg[target_func_id] { // If the target is a function used as value Value::Param { .. } | Value::Instruction { .. } => { + let mut arguments = arguments.clone(); let results = func.dfg.instruction_results(instruction_id); let signature = Signature { params: vecmap(&arguments, |param| func.dfg.type_of_value(*param)), From 9796a1b39e78d8a6edddc0379e72c7eb1ecd8dba Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 15:47:18 +0000 Subject: [PATCH 41/64] cleanup --- compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs | 2 ++ tooling/nargo/src/ops/execute.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index a033ea7873d..7d68267cd9a 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -90,6 +90,8 @@ impl DefunctionalizationContext { Instruction::Call { func: target_func_id, arguments } => { (*target_func_id, arguments) } + // Constrain instruction potentially hold a call instruction themselves + // thus we need to account for them. Instruction::Constrain(_, _, constrain_error) => { if let Some(error) = constrain_error { if let ConstrainError::Dynamic(Instruction::Call { diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 1c3b24b69a5..370393fea09 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -65,7 +65,7 @@ pub fn execute_circuit( } NargoForeignCallResult::ResolvedAssertMessage(message) => { if assert_message.is_some() { - panic!("ahhh we should not be resolving another assert message as the VM should have failed"); + unreachable!("Resolving an assert message should happen only once as the VM should have failed"); } assert_message = Some(message); From 8579816ea59b4ddeffb59ab509651b8d8077d241 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 15:54:30 +0000 Subject: [PATCH 42/64] cleanup brillig assert message gen: --- .../src/brillig/brillig_gen/brillig_block.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 5fd8b4af69f..fca011eff8f 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -277,12 +277,12 @@ impl<'block> BrilligBlock<'block> { *func_id, arguments, dfg, - instruction_id, + &[], ); } - _ => panic!("expected a function"), + _ => unreachable!("expected a function value"), }, - _ => panic!("expected a call"), + _ => unreachable!("expected a call instruction"), } None } @@ -395,7 +395,8 @@ impl<'block> BrilligBlock<'block> { } } Value::Function(func_id) => { - self.convert_ssa_function_call(*func_id, arguments, dfg, instruction_id); + let result_ids = dfg.instruction_results(instruction_id); + self.convert_ssa_function_call(*func_id, arguments, dfg, result_ids); } Value::Intrinsic(Intrinsic::BlackBox(bb_func)) => { // Slices are represented as a tuple of (length, slice contents). @@ -664,7 +665,7 @@ impl<'block> BrilligBlock<'block> { func_id: FunctionId, arguments: &[ValueId], dfg: &DataFlowGraph, - instruction_id: InstructionId, + result_ids: &[ValueId], ) { // Convert the arguments to registers casting those to the types of the receiving function let argument_registers: Vec = arguments @@ -672,8 +673,6 @@ impl<'block> BrilligBlock<'block> { .flat_map(|argument_id| self.convert_ssa_value(*argument_id, dfg).extract_registers()) .collect(); - let result_ids = dfg.instruction_results(instruction_id); - // Create label for the function that will be called let label_of_function_to_call = FunctionContext::function_id_to_function_label(func_id); From ae06f9e1cc26705d2b3745d4acac40990172b840 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 15:56:47 +0000 Subject: [PATCH 43/64] macro errs update --- noirc_macros/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 478ac175b87..927d6e495cc 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -19,7 +19,13 @@ impl MacroProcessor for AssertMessageMacro { } // This macro does not need to process any information after name resolution - fn process_typed_ast(&self, _crate_id: &CrateId, _context: &mut HirContext) {} + fn process_typed_ast( + &self, + _crate_id: &CrateId, + _context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)> { + Ok(()) + } } fn transform(ast: SortedModule, crate_id: &CrateId) -> Result { From 4e4d27332cbce9d062cf821a01346c3e787eec02 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 16:02:42 +0000 Subject: [PATCH 44/64] add an expect func to option --- noir_stdlib/src/option.nr | 5 +++++ test_programs/compile_success_empty/option/src/main.nr | 2 ++ 2 files changed, 7 insertions(+) diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index 137d57f33db..0e1e64ccb55 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -56,6 +56,11 @@ impl Option { } } + fn expect(self, message: str) -> T { + assert(self.is_some(), message); + self._value + } + /// If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. pub fn map(self, f: fn[Env](T) -> U) -> Option { if self._is_some { diff --git a/test_programs/compile_success_empty/option/src/main.nr b/test_programs/compile_success_empty/option/src/main.nr index 1f879bd375f..2fd57a4cd07 100644 --- a/test_programs/compile_success_empty/option/src/main.nr +++ b/test_programs/compile_success_empty/option/src/main.nr @@ -22,6 +22,8 @@ fn main() { assert(some.map(|x| x * 2).unwrap() == 6); assert(some.map(|x| x * ten).unwrap() == 30); + assert(some.expect("Should have a value") == 3); + assert(none.map_or(0, |x| x * 2) == 0); assert(some.map_or(0, |x| x * 2) == 6); assert(none.map_or(0, |x| x * ten) == 0); From e516c27795c73282378be16a4f0920bdb83ebea2 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 16:16:36 +0000 Subject: [PATCH 45/64] leave option expect for separate branch --- noir_stdlib/src/option.nr | 5 ----- test_programs/compile_success_empty/option/src/main.nr | 2 -- 2 files changed, 7 deletions(-) diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index 0e1e64ccb55..137d57f33db 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -56,11 +56,6 @@ impl Option { } } - fn expect(self, message: str) -> T { - assert(self.is_some(), message); - self._value - } - /// If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. pub fn map(self, f: fn[Env](T) -> U) -> Option { if self._is_some { diff --git a/test_programs/compile_success_empty/option/src/main.nr b/test_programs/compile_success_empty/option/src/main.nr index 2fd57a4cd07..1f879bd375f 100644 --- a/test_programs/compile_success_empty/option/src/main.nr +++ b/test_programs/compile_success_empty/option/src/main.nr @@ -22,8 +22,6 @@ fn main() { assert(some.map(|x| x * 2).unwrap() == 6); assert(some.map(|x| x * ten).unwrap() == 30); - assert(some.expect("Should have a value") == 3); - assert(none.map_or(0, |x| x * 2) == 0); assert(some.map_or(0, |x| x * 2) == 6); assert(none.map_or(0, |x| x * ten) == 0); From 0c77f8f7d0d77eee971ea3e71acdff3cc3ace972 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 31 Jan 2024 16:35:57 +0000 Subject: [PATCH 46/64] Update compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index 7d68267cd9a..c6d0f3d68d5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -92,19 +92,16 @@ impl DefunctionalizationContext { } // Constrain instruction potentially hold a call instruction themselves // thus we need to account for them. - Instruction::Constrain(_, _, constrain_error) => { - if let Some(error) = constrain_error { + Instruction::Constrain(_, _, Some(constrain_error)) => { if let ConstrainError::Dynamic(Instruction::Call { func: target_func_id, arguments, - }) = error.as_ref() + }) = constrain_error.as_ref() { (*target_func_id, arguments) } else { continue; } - } else { - continue; } } _ => continue, From 5cd9335e3dbb94a12109081313545762b04bdc9d Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 16:36:59 +0000 Subject: [PATCH 47/64] fixup git suggestion --- .../src/ssa/opt/defunctionalize.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index c6d0f3d68d5..1f09a132132 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -93,15 +93,14 @@ impl DefunctionalizationContext { // Constrain instruction potentially hold a call instruction themselves // thus we need to account for them. Instruction::Constrain(_, _, Some(constrain_error)) => { - if let ConstrainError::Dynamic(Instruction::Call { - func: target_func_id, - arguments, - }) = constrain_error.as_ref() - { - (*target_func_id, arguments) - } else { - continue; - } + if let ConstrainError::Dynamic(Instruction::Call { + func: target_func_id, + arguments, + }) = constrain_error.as_ref() + { + (*target_func_id, arguments) + } else { + continue; } } _ => continue, From c8170f34af1663f6ecef4d4939addad7d827ca8b Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 17:45:09 +0000 Subject: [PATCH 48/64] use AssertMessageMacro when disable_macros is enabled --- compiler/noirc_driver/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 04b40794c99..25d4229048e 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -76,7 +76,7 @@ pub struct CompileOptions { #[arg(long, hide = true)] pub only_acir: bool, - /// Disables the builtin macros being used in the compiler + /// Disables the builtin Aztec macros being used in the compiler #[arg(long, hide = true)] pub disable_macros: bool, @@ -191,7 +191,7 @@ pub fn check_crate( disable_macros: bool, ) -> CompilationResult<()> { let macros: Vec<&dyn MacroProcessor> = if disable_macros { - vec![] + vec![&noirc_macros::AssertMessageMacro as &dyn MacroProcessor] } else { vec![ &aztec_macros::AztecMacro as &dyn MacroProcessor, From ae23e5c77fa0107b943be50df5f8aeff599080fd Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 31 Jan 2024 17:47:49 +0000 Subject: [PATCH 49/64] chore: reduce diff and fix clippy warnings --- acvm-repo/acvm/src/pwg/mod.rs | 1 + compiler/noirc_frontend/src/parser/parser.rs | 4 ++-- test_programs/execution_success/debug_logs/src/main.nr | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index e81991576c1..41b96572658 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -251,6 +251,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { pub fn solve_opcode(&mut self) -> ACVMStatus { let opcode = &self.opcodes[self.instruction_pointer]; + let resolution = match opcode { Opcode::AssertZero(expr) => ExpressionSolver::solve(&mut self.witness_map, expr), Opcode::BlackBoxFuncCall(bb_func) => { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 6831182fae5..1bdb276d4fb 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -2074,7 +2074,7 @@ mod test { let message = message.unwrap(); match message.kind { ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message".to_owned()) + assert_eq!(message_string, "assertion message".to_owned()); } _ => unreachable!(), } @@ -2104,7 +2104,7 @@ mod test { let message = message.unwrap(); match message.kind { ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message".to_owned()) + assert_eq!(message_string, "assertion message".to_owned()); } _ => unreachable!(), } diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index 5e1de86eb96..b640e4f5f0c 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -11,7 +11,7 @@ fn main(x: Field, y: pub Field) { // A `fmtstr` lets you easily perform string interpolation. let fmt_str: fmtstr<14, (Field, Field)> = f"i: {x}, j: {y}"; - + let fmt_str = string_identity(fmt_str); std::println(fmt_str); @@ -45,7 +45,7 @@ fn main(x: Field, y: pub Field) { let struct_string = if x != 5 { f"{foo}" } else { f"{bar}" }; std::println(struct_string); - + let one_tuple = (1, 2, 3); let another_tuple = (4, 5, 6); std::println(f"one_tuple: {one_tuple}, another_tuple: {another_tuple}"); From 5e0e1989d8fb9581e606ae3a48eb28764686043e Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 31 Jan 2024 17:48:55 +0000 Subject: [PATCH 50/64] chore: reduce diff --- compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 7bb7d70a051..f7441750fc8 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -371,7 +371,6 @@ impl DefCollector { }, ); } - errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); // Type check all of the functions in the crate From fa6b50cdbcdc7d60e4c7618572ed2f357c2a62f2 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 18:23:44 +0000 Subject: [PATCH 51/64] format constrain err better --- .../noirc_evaluator/src/ssa/ir/printer.rs | 40 +++++++++++++++---- .../assert_msg_runtime/src/main.nr | 6 +-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 144cee30022..9bd43fab1ff 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -9,7 +9,7 @@ use iter_extended::vecmap; use super::{ basic_block::BasicBlockId, function::Function, - instruction::{Instruction, InstructionId, TerminatorInstruction}, + instruction::{ConstrainError, Instruction, InstructionId, TerminatorInstruction}, value::ValueId, }; @@ -133,9 +133,17 @@ pub(crate) fn display_instruction( write!(f, "{} = ", value_list(function, results))?; } + display_instruction_inner(function, &function.dfg[instruction], f) +} + +fn display_instruction_inner( + function: &Function, + instruction: &Instruction, + f: &mut Formatter, +) -> Result { let show = |id| value(function, id); - match &function.dfg[instruction] { + match instruction { Instruction::Binary(binary) => { writeln!(f, "{} {}, {}", binary.operator, show(binary.lhs), show(binary.rhs)) } @@ -145,12 +153,15 @@ pub(crate) fn display_instruction( let value = show(*value); writeln!(f, "truncate {value} to {bit_size} bits, max_bit_size: {max_bit_size}",) } - Instruction::Constrain(lhs, rhs, message) => match message { - Some(message) => { - writeln!(f, "constrain {} == {} '{message:?}'", show(*lhs), show(*rhs)) + Instruction::Constrain(lhs, rhs, error) => { + write!(f, "constrain {} == {}", show(*lhs), show(*rhs))?; + if let Some(error) = error { + write!(f, " ")?; + display_constrain_error(function, error, f) + } else { + writeln!(f) } - None => writeln!(f, "constrain {} == {}", show(*lhs), show(*rhs)), - }, + } Instruction::Call { func, arguments } => { writeln!(f, "call {}({})", show(*func), value_list(function, arguments)) } @@ -182,3 +193,18 @@ pub(crate) fn display_instruction( } } } + +fn display_constrain_error( + function: &Function, + error: &ConstrainError, + f: &mut Formatter, +) -> Result { + match error { + ConstrainError::Static(assert_message_string) => { + writeln!(f, "{assert_message_string:?}") + } + ConstrainError::Dynamic(assert_message_call) => { + display_instruction_inner(function, assert_message_call, f) + } + } +} diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/compile_failure/assert_msg_runtime/src/main.nr index 8a3bffd41ee..bec3082550a 100644 --- a/test_programs/compile_failure/assert_msg_runtime/src/main.nr +++ b/test_programs/compile_failure/assert_msg_runtime/src/main.nr @@ -1,11 +1,7 @@ fn main(x: Field, y: pub Field) { assert(x != y, f"Expected x != y, but got both equal {x}"); - assert(x != y, f"Expected x != y, but got both equal {x}"); - assert(x != y, f"Expected x != y, but got both equal {x}"); - assert(x != y, f"Expected x != y, but got both equal {x}"); - assert(x != y, f"Expected x != y, but got both equal {x}"); + assert(x != y); let z = x + y; assert(z != y, f"Expected z != y, but got both equal {z}"); - assert_eq(x, y); assert_eq(x, y, f"Expected x == y, but x is {x} and y is {y}"); } \ No newline at end of file From f5598fe48b11458cbbdb198fbc507b59b6062b6c Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 31 Jan 2024 18:25:07 +0000 Subject: [PATCH 52/64] Update compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 6b11910801f..8c8f61f6c45 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -689,34 +689,34 @@ impl<'a> FunctionContext<'a> { &mut self, assert_message: &Option>, ) -> Result>, RuntimeError> { - if let Some(assert_message_expr) = assert_message { - if let ast::Expression::Call(call) = assert_message_expr.as_ref() { - let func = self.codegen_non_tuple_expression(&call.func)?; - let mut arguments = Vec::with_capacity(call.arguments.len()); - - for argument in &call.arguments { - let mut values = self.codegen_expression(argument)?.into_value_list(self); - arguments.append(&mut values); - } - - // If an array is passed as an argument we increase its reference count - for argument in &arguments { - self.builder.increment_array_reference_count(*argument); - } + let Some(assert_message_expr) = assert_message else { + return Ok(None) + }; - let instr = Instruction::Call { func, arguments }; - Ok(Some(Box::new(ConstrainError::Dynamic(instr)))) - } else { - Err(InternalError::Unexpected { - expected: "Expected a call expression".to_owned(), - found: "Instead found {expr:?}".to_owned(), - call_stack: self.builder.get_call_stack(), - } - .into()) + let ast::Expression::Call(call) = assert_message_expr.as_ref() else { + return Err(InternalError::Unexpected { + expected: "Expected a call expression".to_owned(), + found: "Instead found {expr:?}".to_owned(), + call_stack: self.builder.get_call_stack(), } - } else { - Ok(None) + .into()); + }; + + let func = self.codegen_non_tuple_expression(&call.func)?; + let mut arguments = Vec::with_capacity(call.arguments.len()); + + for argument in &call.arguments { + let mut values = self.codegen_expression(argument)?.into_value_list(self); + arguments.append(&mut values); + } + + // If an array is passed as an argument we increase its reference count + for argument in &arguments { + self.builder.increment_array_reference_count(*argument); } + + let instr = Instruction::Call { func, arguments }; + Ok(Some(Box::new(ConstrainError::Dynamic(instr)))) } fn codegen_assign(&mut self, assign: &ast::Assign) -> Result { From d3c2f0e6ba66b70031b7097faf849846a1de42fe Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 18:25:56 +0000 Subject: [PATCH 53/64] cargo fmt --- compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 8c8f61f6c45..0efc73fb85b 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -689,7 +689,7 @@ impl<'a> FunctionContext<'a> { &mut self, assert_message: &Option>, ) -> Result>, RuntimeError> { - let Some(assert_message_expr) = assert_message else { + let Some(assert_message_expr) = assert_message else { return Ok(None) }; From bb34b4581180595de27ef3925e3bde84be88e731 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 18:51:11 +0000 Subject: [PATCH 54/64] fix old test updates --- compiler/noirc_frontend/src/tests.rs | 32 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 07985f7dc85..46913b08a54 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -59,6 +59,16 @@ mod test { let root_crate_id = context.crate_graph.add_crate_root(root_file_id); let (program, parser_errors) = parse_program(src); + // match noirc_macros::AssertMessageMacro.process_untyped_ast(ast.clone(), &crate_id, context) { + // Ok(processed_ast) => { + // ast = processed_ast; + // } + // Err((error, file_id)) => { + // let def_error = DefCollectorErrorKind::MacroError(error); + // errors.push((def_error.into(), file_id)); + // } + // } + let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); @@ -488,9 +498,8 @@ mod test { fn default(x: Field, y: NotAType) -> Field; } - fn main(x: Field, y: Field) -> pub bool { - // assert(y == x); - y == x + fn main(x: Field, y: Field) { + assert(y == x); }"; let errors = get_program_errors(src); assert!(!has_parser_error(&errors)); @@ -821,9 +830,9 @@ mod test { #[test] fn resolve_basic_function() { let src = r#" - fn main(x : Field) -> pub bool { + fn main(x : Field) { let y = x + x; - y == x + assert(y == x); } "#; assert!(get_program_errors(src).is_empty()); @@ -831,10 +840,9 @@ mod test { #[test] fn resolve_unused_var() { let src = r#" - fn main(x : Field) -> pub bool { + fn main(x : Field) { let y = x + x; - // assert(x == x); - x == x + assert(x == x); } "#; @@ -852,9 +860,9 @@ mod test { #[test] fn resolve_unresolved_var() { let src = r#" - fn main(x : Field) -> pub bool { + fn main(x : Field) { let y = x + x; - y == z + assert(y == z); } "#; let errors = get_program_errors(src); @@ -898,9 +906,9 @@ mod test { #[test] fn resolve_literal_expr() { let src = r#" - fn main(x : Field) -> pub bool { + fn main(x : Field) { let y = 5; - y == x + assert(y == x); } "#; assert!(get_program_errors(src).is_empty()); From 819c95aca4b686900505f4cc04c79879c8c911c7 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 18:52:03 +0000 Subject: [PATCH 55/64] remove old comment --- compiler/noirc_frontend/src/tests.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 46913b08a54..c9181ac3ea9 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -58,17 +58,7 @@ mod test { let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - let (program, parser_errors) = parse_program(src); - // match noirc_macros::AssertMessageMacro.process_untyped_ast(ast.clone(), &crate_id, context) { - // Ok(processed_ast) => { - // ast = processed_ast; - // } - // Err((error, file_id)) => { - // let def_error = DefCollectorErrorKind::MacroError(error); - // errors.push((def_error.into(), file_id)); - // } - // } - + let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); From c62bec8d6f057ea66354e70113350f9bb97f102a Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 18:52:35 +0000 Subject: [PATCH 56/64] reduce diff --- compiler/noirc_frontend/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c9181ac3ea9..a4246a9fe7d 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -58,7 +58,7 @@ mod test { let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - let (program, parser_errors) = parse_program(src); + let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); From 7f174dcedf74c4ed73e6e7b0406d1ff7d3e1b87a Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 19:43:51 +0000 Subject: [PATCH 57/64] update assert docs --- docs/docs/noir/concepts/assert.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/docs/noir/concepts/assert.md b/docs/docs/noir/concepts/assert.md index c5f9aff139c..bcff613a695 100644 --- a/docs/docs/noir/concepts/assert.md +++ b/docs/docs/noir/concepts/assert.md @@ -18,10 +18,28 @@ fn main(x : Field, y : Field) { } ``` +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + You can optionally provide a message to be logged when the assertion fails: ```rust assert(x == y, "x and y are not equal"); ``` -> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + From dd056a1b7ef001d46a4575e39db14f22a1300567 Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 31 Jan 2024 19:54:32 +0000 Subject: [PATCH 58/64] chore: remove some indentation --- .../src/brillig/brillig_gen/brillig_block.rs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index fca011eff8f..3d4358dbcca 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -270,20 +270,18 @@ impl<'block> BrilligBlock<'block> { match error.as_ref() { ConstrainError::Static(string) => Some(string.clone()), ConstrainError::Dynamic(call_instruction) => { - match call_instruction { - Instruction::Call { func, arguments } => match &dfg[*func] { - Value::Function(func_id) => { - self.convert_ssa_function_call( - *func_id, - arguments, - dfg, - &[], - ); - } - _ => unreachable!("expected a function value"), - }, - _ => unreachable!("expected a call instruction"), - } + let Instruction::Call { func, arguments } = call_instruction else { + unreachable!("expected a call instruction") + }; + + let Value::Function(func_id) = &dfg[*func] else { + unreachable!("expected a function value") + }; + + self.convert_ssa_function_call(*func_id, arguments, dfg, &[]); + + // Dynamic assert message is handled in the generated function call. + // We then don't need to attach one to the constrain instruction. None } } @@ -291,7 +289,7 @@ impl<'block> BrilligBlock<'block> { None }; - self.brillig_context.constrain_instruction(condition, assert_message.clone()); + self.brillig_context.constrain_instruction(condition, assert_message); self.brillig_context.deallocate_register(condition); } Instruction::Allocate => { From 757c74038348f7eeb819c17247bb10018791b5e7 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 21:43:47 +0000 Subject: [PATCH 59/64] noir js assert_lt test to show usage of resolve_assert_message --- tooling/noir_js/src/witness_generation.ts | 6 ++++++ .../test/noir_compiled_examples/assert_lt/src/main.nr | 3 +++ .../noir_compiled_examples/assert_lt/target/assert_lt.json | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tooling/noir_js/src/witness_generation.ts b/tooling/noir_js/src/witness_generation.ts index 1f233422061..cef1d817d9b 100644 --- a/tooling/noir_js/src/witness_generation.ts +++ b/tooling/noir_js/src/witness_generation.ts @@ -26,6 +26,12 @@ const defaultForeignCallHandler: ForeignCallHandler = async (name: string, args: // // If a user needs to print values then they should provide a custom foreign call handler. return []; + } else if (name == 'assert_message') { + // By default we do not do anything for `assert_message` foreign calls due to a need for formatting, + // however we provide an empty response in order to not halt execution. + // + // If a user needs to use dynamic assertion messages then they should provide a custom foreign call handler. + return []; } throw Error(`Unexpected oracle during execution: ${name}(${args.join(', ')})`); }; diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr index 693e7285736..1cdfe59c378 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr @@ -4,6 +4,9 @@ fn main(x: u64, y: pub u64) -> pub u64 { // We include a println statement to show that noirJS will ignore this and continue execution std::println("foo"); + // A dynamic assertion message is used to show that noirJS will ignore the call and continue execution + assert(x < y, f"Expected x < y but got {x} < {y}"); + assert(x < y); x + y } diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json b/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json index 5b511cdc140..83f96c72a13 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json @@ -1 +1 @@ -{"noir_version":"0.20.0+010fdb69616f47fc0a9f252a65a903316d3cbe80","hash":17538653710107541030,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{"start":1,"end":2}],"y":[{"start":2,"end":3}]},"return_type":{"abi_type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},"return_witnesses":[5]},"bytecode":"H4sIAAAAAAAA/9Wa627aMBiGHcKhBBLOR+0Hl+Ak5PQP9U6gBK3SNqoqWm9kFzzMYu2DujhbPjvFEkrsJu/7vJ+TFDAtQkiH/GnG6VXLtxvQr+V9Mx/jx5pE3Db5lpZqYahI96Ba91e+bee1g60J6objS90mehbqN8nfuUbSpLAeNVAjXg++bZxeD/m+k9esjlyz6+t3A/p1MFfYvkyzharpPrXzmsFmXPU3YL8F8jUV5HvA1aRMs42qGe2YhgVqwuvH2Tvg721QLwu5Xgbw5Lq8bynz9Tye8Vb+joCjozF/R5lveJ7/riR/V8DR1Zi/q8w3TJiGLclvCzhsjfltZb5hyjQcSX5HwOFozO8o8w3XTKMnyd8TcPQ05od8RVmtilnxff0t0+hL8vcFHH2N+SFfUVarYlZ838hnGgNJ/oGAY6Ax/0CZb3R+rgwl+YcCjqHG/ENlvtH5fdVIkn8k4BhpzA/5irJ274jVrpgV3zeMmMZYkn8s4BhrzA/5irJaFbPi+3pPTGMiyT8RcEw05od8RVmtilnxfcPzXE0l+acCjqnG/FNlvmHANGaS/DMBx0xjfshXlNW+I9bRHbEOKmbF9w1jpjGX5J8LOOYa80O+oqzWHbH2KmbF9/XPnwUXkvwLAcdCY/6FMt9ozzSWkvxLAcdSY/4l8MVet3gAmV9en39kHKAOYPg+X2xlzQRjXOALOIeDtsj7hR60qpnk/eolAWNYPgbQ8k/fTK7TyEtd391SL9nFAV0HuzB2YzeIg70X+34ar+Mo2SURTdy1n7qHIPEPuVjt/7nc6wFBdDRtWFe46tgAE18D44/geLgCb4A5eQTniI4xPtBpgzF+vkMUXljQHEvTJJc/T+C6ZS8oE4+R8kmtk8vlWELwfxKg6qYqq9VErOet+v0jJ73idE3EzHXEeS1Rv5sPuM9839yaZ1quXdwntFxzMe+TBsF/7nDNj/6B8P0GuXz4848R/B3INsvS7y/ZKjuutvv96u05+7o6/kxfD9+Ob78Bhjydn08mAAA="} \ No newline at end of file +{"noir_version":"0.23.0+2dc4805116123ecde584fe0f2cd280de817d3915","hash":3604098377358348093,"abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/+2cbW/aSBDHbUhIgJjHQAJ5opXuvY3BhndRP8hVEOCu0vVSJej6Re4DX9b2qoNZs74ys+5IsRRhOzDz+48X2+z+1xXLsupWvNhvf6Vk/RFs28l2OVmX7y1b6uUxeXWPWoKAKO6GOu6/yWsV1FMuFVA3nLyuV0HX4voV68exRorpwnqUQI1kPeTr6dvfebLeSGp2glyzdPt9BNsn4Fhh5xUxz1Bjek/VpGZwsVPbj2D9DOirEOg7x43piphV1JjhUsSogZrI+kn2Ovh/FdSrhlwvG+SUceV2jSzveCw1HtJfV3DUDeqvk+UNouN/odF/oeC4MKj/gixvMBcxHI1+R8HhGNTvkOUN1iJGQ6O/oeBoGNTfIMsbTESMpkZ/U8HRNKgf8uVlrRXMip/XX4gYLY3+loKjZVA/5MvLWiuYFT9v6IsYbY3+toKjbVB/myxvGJ1XOhr9HQVHx6D+DlneMLqv6mr0dxUcXYP6IV9e1gtGrE7BrPh5g1DEuNTov1RwXBrUD/nystYKZsXPO34SMXoa/T0FR8+gfsiXl7VWMCt+3iA6Vn2N/r6Co29Qf58sbzAVMa40+q8UHFcG9UO+vKwOI9YuI9Z2waz4eYOZiHGt0X+t4Lg2qB/y5WWtMWJtFsyKn9ePfgsONPoHCo6BQf0DsrzhSsQYavQPFRxDg/qHIC/2uMU50Pzt5cvfWwlwAmDkOhx8LYN9MsAt+IwEPbP2B3rQqla29kcvLWt3hBgjD4zlv/VMTtbheO353sIdz5ezqTuZLoOZN/Oms+lqPPP99WwyC+fLeejOvYm/9jbTub9Jgtk/z+Wldyiko8UuKepZBgcbNgbRQD6B98MReBvE+AQ+o3qPnRGnqmBpWIQNS4orW7vDkrIImLlgLMzGdWysUzyN7qH6/U9ON8XplRA1lxGP6xH12/sypr94I0u94LBPpjRxwxlRXJcmbkBUh8AnqsOEWR2IeMdE7YGq/VLx+k/M6jvnxRuMiepAdf5l9n2LhxQI4m7e6xvxLoniUl3nV7zqy+78S9UeiM7r++0BL7a7c25Hthl7iLFcSk7bMsN57O+2EvKxEcvnBFJlXaawJYs8Z6n6iH2yJ60E9skOkTLYJ7vSoTXcQmUcu9SW+1Ogp5zSUQXazsC+dJdtNfUZ+So/UwO5pL1JWvhxLdy73bqyo6ua0lC29of8hc7f7R/6kbt9D04tkLkILN1T2J4tUBcrxSIXODyEfWwIrOIuvg06viZDG3Taem7SBl1PHSe5TWeDjqcBNDT6Tdmgs/QT2qCj49/U6Ddlg87ST2ctjqcBtDT6TVmLs/TT2XXjaQBtjX5Tdt0s/XR23WDPrqvSb8qum6Uf8uVldQpmxc8bTwPoavSbshZn6c+y6x5ipbbr6ljx88Z9jJca/absuln6s+y6h1hbBbPi542nbPQ0+k3ZdbP0Z9l1D7E2C2YlsOtGfdZ9jX5Tdt0s/ZAvL2urYFYT1mKVflPW4iz9WRbYQ6zUFlhM1iYj1lbBrPh54zGra41+U9biLP1Zdt1DrH1GrD1GrEXXFT9vPCVyoNFvygadpR/y5WV1CmbFzxtPXRtq9JuybGfph3x5WZ2CWfHzhtG98I1G/42C48agfsiXl7XJiLXNiPWqYFb8vPHUrVuN/lsFx61B/ZAvL6vDiLVTMCt+Xj969OCdRv+dguPOoP47srx+9LvlXqP/XsFxb1D/PVne2Pv0oNH/oOB4MKj/AeQdoeaNvQQwr1gOeQlGhDUQMT/gxoz8Uh8Bv9T6AWzL/8Nx/4/I2myQU8aV25AvL6vDiLXBiLXJiLXFiLXNiJXTd6vDiJVTXbuMWDmdBy4ZsXK6FvQYsXJqr31GrJyuse/3WTSsV4xYOX23OJ1fOdX1mhErp/vXASNWTnUdMmLldI3ldO/C6Rp7w4j1vd+FhvWWEesdI9b7glkJxks8Mf4l++sXr6/rl+3nr+vX18Ufa0kCZ1eXkvURIIXPPIQzvivgc3KU7Tewr57aZwF1cCYv6bMQYUw7FfsXml5/8FFw7nEL5mMVdh59cGz9ThDrJxuvjGkrjrsN1k9S75FTquWpdbHdrr9+2462z6PFajX6/mX75+j5n/XL5q/n7/8BxzOHw3pxAAA="} \ No newline at end of file From e002ba2258d66d64283bd74eb5fa674de08b3d01 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 21:45:41 +0000 Subject: [PATCH 60/64] improve comment --- .../noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 3d4358dbcca..632add74e6d 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -280,7 +280,7 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_function_call(*func_id, arguments, dfg, &[]); - // Dynamic assert message is handled in the generated function call. + // Dynamic assert messages are handled in the generated function call. // We then don't need to attach one to the constrain instruction. None } From c6fe90027d9f477ed8437c4bbc1abf64e4a17cc1 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 22:17:31 +0000 Subject: [PATCH 61/64] add assert_msg_runtime execution failure test to noirJS --- tooling/noir_js/test/node/execute.test.ts | 17 +++++++++++++++++ .../assert_lt/src/main.nr | 1 + .../assert_lt/target/assert_lt.json | 2 +- .../assert_msg_runtime/Nargo.toml | 7 +++++++ .../assert_msg_runtime/src/main.nr | 6 ++++++ .../target/assert_msg_runtime.json | 1 + 6 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/Nargo.toml create mode 100644 tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/src/main.nr create mode 100644 tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/target/assert_msg_runtime.json diff --git a/tooling/noir_js/test/node/execute.test.ts b/tooling/noir_js/test/node/execute.test.ts index bfaf80882ab..3a1513c3a57 100644 --- a/tooling/noir_js/test/node/execute.test.ts +++ b/tooling/noir_js/test/node/execute.test.ts @@ -1,9 +1,11 @@ import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' }; +import assert_msg_json from '../noir_compiled_examples/assert_msg_runtime/target/assert_msg_runtime.json' assert { type: 'json' }; import { Noir } from '@noir-lang/noir_js'; import { CompiledCircuit } from '@noir-lang/types'; import { expect } from 'chai'; const assert_lt_program = assert_lt_json as CompiledCircuit; +const assert_msg_runtime = assert_msg_json as CompiledCircuit; it('returns the return value of the circuit', async () => { const inputs = { @@ -14,3 +16,18 @@ it('returns the return value of the circuit', async () => { expect(returnValue).to.be.eq('0x05'); }); + +it('circuit with a dynamic assert message should fail on an assert failure not the foreign call handler', async () => { + const inputs = { + x: '10', + y: '5', + }; + try { + await new Noir(assert_msg_runtime).execute(inputs); + } catch (error) { + const knownError = error as Error; + expect(knownError.message).to.equal( + 'Circuit execution failed: Error: Cannot satisfy constraint' + ) + } +}); diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr index 1cdfe59c378..a9aaae5f2f7 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr @@ -5,6 +5,7 @@ fn main(x: u64, y: pub u64) -> pub u64 { std::println("foo"); // A dynamic assertion message is used to show that noirJS will ignore the call and continue execution + // The assertion passes and thus the foreign call for resolving an assertion message should not be called. assert(x < y, f"Expected x < y but got {x} < {y}"); assert(x < y); diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json b/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json index 83f96c72a13..027df0b39d9 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json @@ -1 +1 @@ -{"noir_version":"0.23.0+2dc4805116123ecde584fe0f2cd280de817d3915","hash":3604098377358348093,"abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/+2cbW/aSBDHbUhIgJjHQAJ5opXuvY3BhndRP8hVEOCu0vVSJej6Re4DX9b2qoNZs74ys+5IsRRhOzDz+48X2+z+1xXLsupWvNhvf6Vk/RFs28l2OVmX7y1b6uUxeXWPWoKAKO6GOu6/yWsV1FMuFVA3nLyuV0HX4voV68exRorpwnqUQI1kPeTr6dvfebLeSGp2glyzdPt9BNsn4Fhh5xUxz1Bjek/VpGZwsVPbj2D9DOirEOg7x43piphV1JjhUsSogZrI+kn2Ovh/FdSrhlwvG+SUceV2jSzveCw1HtJfV3DUDeqvk+UNouN/odF/oeC4MKj/gixvMBcxHI1+R8HhGNTvkOUN1iJGQ6O/oeBoGNTfIMsbTESMpkZ/U8HRNKgf8uVlrRXMip/XX4gYLY3+loKjZVA/5MvLWiuYFT9v6IsYbY3+toKjbVB/myxvGJ1XOhr9HQVHx6D+DlneMLqv6mr0dxUcXYP6IV9e1gtGrE7BrPh5g1DEuNTov1RwXBrUD/nystYKZsXPO34SMXoa/T0FR8+gfsiXl7VWMCt+3iA6Vn2N/r6Co29Qf58sbzAVMa40+q8UHFcG9UO+vKwOI9YuI9Z2waz4eYOZiHGt0X+t4Lg2qB/y5WWtMWJtFsyKn9ePfgsONPoHCo6BQf0DsrzhSsQYavQPFRxDg/qHIC/2uMU50Pzt5cvfWwlwAmDkOhx8LYN9MsAt+IwEPbP2B3rQqla29kcvLWt3hBgjD4zlv/VMTtbheO353sIdz5ezqTuZLoOZN/Oms+lqPPP99WwyC+fLeejOvYm/9jbTub9Jgtk/z+Wldyiko8UuKepZBgcbNgbRQD6B98MReBvE+AQ+o3qPnRGnqmBpWIQNS4orW7vDkrIImLlgLMzGdWysUzyN7qH6/U9ON8XplRA1lxGP6xH12/sypr94I0u94LBPpjRxwxlRXJcmbkBUh8AnqsOEWR2IeMdE7YGq/VLx+k/M6jvnxRuMiepAdf5l9n2LhxQI4m7e6xvxLoniUl3nV7zqy+78S9UeiM7r++0BL7a7c25Hthl7iLFcSk7bMsN57O+2EvKxEcvnBFJlXaawJYs8Z6n6iH2yJ60E9skOkTLYJ7vSoTXcQmUcu9SW+1Ogp5zSUQXazsC+dJdtNfUZ+So/UwO5pL1JWvhxLdy73bqyo6ua0lC29of8hc7f7R/6kbt9D04tkLkILN1T2J4tUBcrxSIXODyEfWwIrOIuvg06viZDG3Taem7SBl1PHSe5TWeDjqcBNDT6Tdmgs/QT2qCj49/U6Ddlg87ST2ctjqcBtDT6TVmLs/TT2XXjaQBtjX5Tdt0s/XR23WDPrqvSb8qum6Uf8uVldQpmxc8bTwPoavSbshZn6c+y6x5ipbbr6ljx88Z9jJca/absuln6s+y6h1hbBbPi542nbPQ0+k3ZdbP0Z9l1D7E2C2YlsOtGfdZ9jX5Tdt0s/ZAvL2urYFYT1mKVflPW4iz9WRbYQ6zUFlhM1iYj1lbBrPh54zGra41+U9biLP1Zdt1DrH1GrD1GrEXXFT9vPCVyoNFvygadpR/y5WV1CmbFzxtPXRtq9JuybGfph3x5WZ2CWfHzhtG98I1G/42C48agfsiXl7XJiLXNiPWqYFb8vPHUrVuN/lsFx61B/ZAvL6vDiLVTMCt+Xj969OCdRv+dguPOoP47srx+9LvlXqP/XsFxb1D/PVne2Pv0oNH/oOB4MKj/AeQdoeaNvQQwr1gOeQlGhDUQMT/gxoz8Uh8Bv9T6AWzL/8Nx/4/I2myQU8aV25AvL6vDiLXBiLXJiLXFiLXNiJXTd6vDiJVTXbuMWDmdBy4ZsXK6FvQYsXJqr31GrJyuse/3WTSsV4xYOX23OJ1fOdX1mhErp/vXASNWTnUdMmLldI3ldO/C6Rp7w4j1vd+FhvWWEesdI9b7glkJxks8Mf4l++sXr6/rl+3nr+vX18Ufa0kCZ1eXkvURIIXPPIQzvivgc3KU7Tewr57aZwF1cCYv6bMQYUw7FfsXml5/8FFw7nEL5mMVdh59cGz9ThDrJxuvjGkrjrsN1k9S75FTquWpdbHdrr9+2462z6PFajX6/mX75+j5n/XL5q/n7/8BxzOHw3pxAAA="} \ No newline at end of file +{"noir_version":"0.23.0+2dc4805116123ecde584fe0f2cd280de817d3915","hash":17955244824623030861,"abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/+2cbW/aSBDHbUhIgJjHQAJ5opXuvY3BhndRP8hVEOCu0vVSJej6Re4DX9b2qoNZs74ys+5IsRRhOzDz+48X2+z+1xXLsupWvNhvf6Vk/RFs28l2OVmX7y1b6uUxeXWPWoKAKO6GOu6/yWsV1FMuFVA3nLyuV0HX4voV68exRorpwnqUQI1kPeTr6dvfebLeSGp2glyzdPt9BNsn4Fhh5xUxz1Bjek/VpGZwsVPbj2D9DOirEOg7x43piphV1JjhUsSogZrI+kn2Ovh/FdSrhlwvG+SUceV2jSzveCw1HtJfV3DUDeqvk+UNouN/odF/oeC4MKj/gixvMBcxHI1+R8HhGNTvkOUN1iJGQ6O/oeBoGNTfIMsbTESMpkZ/U8HRNKgf8uVlrRXMip/XX4gYLY3+loKjZVA/5MvLWiuYFT9v6IsYbY3+toKjbVB/myxvGJ1XOhr9HQVHx6D+DlneMLqv6mr0dxUcXYP6IV9e1gtGrE7BrPh5g1DEuNTov1RwXBrUD/nystYKZsXPO34SMXoa/T0FR8+gfsiXl7VWMCt+3iA6Vn2N/r6Co29Qf58sbzAVMa40+q8UHFcG9UO+vKwOI9YuI9Z2waz4eYOZiHGt0X+t4Lg2qB/y5WWtMWJtFsyKn9ePfgsONPoHCo6BQf0DsrzhSsQYavQPFRxDg/qHIC/2uMU50Pzt5cvfWwlwAmDkOhx8LYN9MsAt+IwEPbP2B3rQqla29kcvLWt3hBgjD4zlv/VMTtbheO353sIdz5ezqTuZLoOZN/Oms+lqPPP99WwyC+fLeejOvYm/9jbTub9Jgtk/z+Wldyiko8UuKepZBgcbNgbRQD6B98MReBvE+AQ+o3qPnRGnqmBpWIQNS4orW7vDkrIImLlgLMzGdWysUzyN7qH6/U9ON8XplRA1lxGP6xH12/sypr94I0u94LBPpjRxwxlRXJcmbkBUh8AnqsOEWR2IeMdE7YGq/VLx+k/M6jvnxRuMiepAdf5l9n2LhxQI4m7e6xvxLoniUl3nV7zqy+78S9UeiM7r++0BL7a7c25Hthl7iLFcSk7bMsN57O+2EvKxEcvnBFJlXaawJYs8Z6n6iH2yJ60E9skOkTLYJ7vSoTXcQmUcu9SW+1Ogp5zSUQXazsC+dJdtNfUZ+So/UwO5pL1JWvhxLdy73bqyo6ua0lC29of8hc7f7R/6kbt9D04tkLkILN1T2J4tUBcrxSIXODyEfWwIrOIuvg06viZDG3Taem7SBl1PHSe5TWeDjqcBNDT6Tdmgs/QT2qCj49/U6Ddlg87ST2ctjqcBtDT6TVmLs/TT2XXjaQBtjX5Tdt0s/XR23WDPrqvSb8qum6Uf8uVldQpmxc8bTwPoavSbshZn6c+y6x5ipbbr6ljx88Z9jJca/absuln6s+y6h1hbBbPi542nbPQ0+k3ZdbP0Z9l1D7E2C2YlsOtGfdZ9jX5Tdt0s/ZAvL2urYFYT1mKVflPW4iz9WRbYQ6zUFlhM1iYj1lbBrPh54zGra41+U9biLP1Zdt1DrH1GrD1GrEXXFT9vPCVyoNFvygadpR/y5WV1CmbFzxtPXRtq9JuybGfph3x5WZ2CWfHzhtG98I1G/42C48agfsiXl7XJiLXNiPWqYFb8vPHUrVuN/lsFx61B/ZAvL6vDiLVTMCt+Xj969OCdRv+dguPOoP47srx+9LvlXqP/XsFxb1D/PVne2Pv0oNH/oOB4MKj/AeQdoeaNvQQwr1gOeQlGhDUQMT/gxoz8Uh8Bv9T6AWzL/8Nx/4/I2myQU8aV25AvL6vDiLXBiLXJiLXFiLXNiJXTd6vDiJVTXbuMWDmdBy4ZsXK6FvQYsXJqr31GrJyuse/3WTSsV4xYOX23OJ1fOdX1mhErp/vXASNWTnUdMmLldI3ldO/C6Rp7w4j1vd+FhvWWEesdI9b7glkJxks8Mf4l++sXr6/rl+3nr+vX18Ufa0kCZ1eXkvURIIXPPIQzvivgc3KU7Tewr57aZwF1cCYv6bMQYUw7FfsXml5/8FFw7nEL5mMVdh59cGz9ThDrJxuvjGkrjrsN1k9S75FTquWpdbHdrr9+2462z6PFajX6/mX75+j5n/XL5q/n7/8BxzOHw3pxAAA=","debug_symbols":"ndvNilVHFIbhezljCftbq/auqr6VkIEkBgTREJ1J33sSYjLQ/uE8Mx180OhLD55d6+vtw6df3355/+nj59vD11vdHn7+evv8x9uP//zt85e3f365PeRcb27vPv7295/mfHxz+/39h3e3h1GPv7y59VODcexvg5Hx3WDcOzjvHVz3Dua9g3XvYD85mP/9s47V3w1y3Luon578qeqY3ybV5/+TWv9O9v2Tp3+yOerbZl7zx01gU7Bp2AzYnLC5YDNhs2Cz798UdFDQQUEHBR0UdFDQQUEHBR0UdFDQQUMHDR00dNDQQUMHDR00dNDQQUMHDR0M6GBABwM6GNDBgA4GdDCggwEdDOhgQAcndHBCByd0cEIHJ3RwQgcndHBCByd0cEIHF3RwQQcXdHBBBxd0cEEHF3RwQQcXdHBBBxM6mNDBhA4mdDChgwkdTOhgQgcTOpjQwYIOFnSwoIMFHSzoYEEHCzpY0MGCDhZ0sKGDDR1s6GBDBxs62NDBhg42dLChgw0d5BBIOkSSDqGkQyzpEEw6RJMO4aRDPOkQUDqkCKNFKYJwkXSReJF8kYCRhJGIUYwxgowp0mYpQpwxAo0RaYxQY8QaI9gY0cYIN0a8MU0fIKQIIceIOUbQMaKOEXaMuGMEHiPyGKHHDPomJUWIPuZ+fpzPfMp66ePkfOZT1subOmAT2Dz5//rSZ9D5zCemVzYDNidsLthM2CzY7Ps3T//Of2UT2EAHDR00dNDQQUMHDR00dNDQwYAOBnQwoIMBHQzoYEAHAzoY0MGADgZ0cEIHJ3RwQgcndHBCByd0cEIHJ3RwQgcndHBBBxd0cEEHF3RwQQcXdHBBBxd0cEEHF3QwoYMJHUzoYEIHEzqY0MGEDiZ0MKGDCR0s6GBBBws6WNDBgg4WdLCggwUdLOhgQQcbOtjQwYYONnSwoYMNHWzoYEMHGzrY0MEzn5heG0VGJaOW0ZDRKaNLRlNGS0ZSRKSISBGRIiJFRIqIFBEpIlJEpIhIESVFlBQhzBhxxgg0RqQxQo0Ra4xgY0QbI9wY8cYIOEbEMUKOEXOMoGNEHSPsGHHHCDxG5DFCjxF7jOBjRB8j/BjxxwhARgQyQpARg4wgZEQhIwwZccgIREYkMkKREYuMYGREIyMcGfHICEhGRDJCkhGTjKBkRCUjLBlxyQhMRmQyQpMRm4zgZEQnIzwZ8ckIUEaEMkKUEaOMIGVEKSNMGXHKCFRGpDJClRGrjGBlRCsjXBnxyghYRsQyQpYRsywxyxKzLDHLErMsMcsSsywxyxKzLDHLErMsMcsSsywxyxKzLDHLErMsMcsSsywxyxKzLDHLErMsehpJbyPpcSS9jqTnkfQ+kh5IilmWmGWJWVbTY1kpQsyyxCxLzLLELEvMssQsS8yyxCxLzLIGvZ+WIsQsS8yyxCxLzLLELEvMssQsS8yyxCxLzLLELEvMssQsS8yyxCxLzLLELEvMssQsS8yyLrqykCLELEvMssQsS8yyxCxLzLLELEvMssQsa9LhjRQhZlliliVmWWKWJWZZYpYlZlliliVmWYtusaQIMcsSsywxyxKzLDHLErMsMcsSsywxy9p0nkf3eXKgJ2bZYpYtZtlili1m2WKWLWbZYpYtZtmhk00pQsyyxSxbzLLFLFvMssUsW8yyxSxbzLKLrnilCDHLFrNsMcsWs2wxyxazbLrqprNuuuu2w24pgk676babjrvpupvOu8UsW8yyxSxbzLIH3fpLEWKWLWbZYpZ9r1k+Pv4F","file_map":{"28":{"source":"mod hash;\nmod array;\nmod slice;\nmod merkle;\nmod schnorr;\nmod ecdsa_secp256k1;\nmod ecdsa_secp256r1;\nmod eddsa;\nmod grumpkin_scalar;\nmod grumpkin_scalar_mul;\nmod scalar_mul;\nmod sha256;\nmod sha512;\nmod field;\nmod ec;\nmod unsafe;\nmod collections;\nmod compat;\nmod convert;\nmod option;\nmod string;\nmod test;\nmod cmp;\nmod ops;\nmod default;\nmod prelude;\nmod uint128;\n// mod bigint;\n\n// Oracle calls are required to be wrapped in an unconstrained function\n// Thus, the only argument to the `println` oracle is expected to always be an ident\n#[oracle(print)]\nunconstrained fn print_oracle(with_newline: bool, input: T) {}\n\nunconstrained pub fn print(input: T) {\n print_oracle(false, input);\n}\n\nunconstrained pub fn println(input: T) {\n print_oracle(true, input);\n}\n\n#[foreign(recursive_aggregation)]\npub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {}\n\n// Asserts that the given value is known at compile-time.\n// Useful for debugging for-loop bounds.\n#[builtin(assert_constant)]\npub fn assert_constant(x: T) {}\n// from_field and as_field are private since they are not valid for every type.\n// `as` should be the default for users to cast between primitive types, and in the future\n// traits can be used to work with generic types.\n#[builtin(from_field)]\nfn from_field(x: Field) -> T {}\n\n#[builtin(as_field)]\nfn as_field(x: T) -> Field {}\n\npub fn wrapping_add(x: T, y: T) -> T {\n crate::from_field(crate::as_field(x) + crate::as_field(y))\n}\n\npub fn wrapping_sub(x: T, y: T) -> T {\n //340282366920938463463374607431768211456 is 2^128, it is used to avoid underflow\n crate::from_field(crate::as_field(x) + 340282366920938463463374607431768211456 - crate::as_field(y))\n}\n\npub fn wrapping_mul(x: T, y: T) -> T {\n crate::from_field(crate::as_field(x) * crate::as_field(y))\n}\n","path":"std/lib.nr"},"42":{"source":"use dep::std;\n\nfn main(x: u64, y: pub u64) -> pub u64 {\n // We include a println statement to show that noirJS will ignore this and continue execution\n std::println(\"foo\");\n\n // A dynamic assertion message is used to show that noirJS will ignore the call and continue execution\n // The assertion passes and thus the foreign call for resolving an assertion message should not be called.\n assert(x < y, f\"Expected x < y but got {x} < {y}\");\n\n assert(x < y);\n x + y\n}\n","path":"/mnt/user-data/maxim/noir/tooling/noir_js/test/noir_compiled_examples/assert_lt/src/main.nr"}}} \ No newline at end of file diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/Nargo.toml b/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/Nargo.toml new file mode 100644 index 00000000000..765f632ff74 --- /dev/null +++ b/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_msg_runtime" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/src/main.nr b/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/src/main.nr new file mode 100644 index 00000000000..40e447cad02 --- /dev/null +++ b/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/src/main.nr @@ -0,0 +1,6 @@ +fn main(x: u64, y: pub u64) { + // A dynamic assertion message is used to show that noirJS will ignore the call and continue execution + // We need this assertion to fail as the `assert_message` oracle in Noir is only called + // upon a failing condition in an assert. + assert(x < y, f"Expected x < y but got {x} < {y}"); +} diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/target/assert_msg_runtime.json b/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/target/assert_msg_runtime.json new file mode 100644 index 00000000000..300e9a06e13 --- /dev/null +++ b/tooling/noir_js/test/noir_compiled_examples/assert_msg_runtime/target/assert_msg_runtime.json @@ -0,0 +1 @@ +{"noir_version":"0.23.0+2dc4805116123ecde584fe0f2cd280de817d3915","hash":8331765605233675558,"abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2c2W6bQBSGwVviJBi870si9Z4dfFflQRrZDelVb+q+vxqWUU/wNEPrM0OPlJEsYDDnfP/PsHgG3NY07VrLi/76aRTzn8GyXiw3wXpY4DZpsS8rDozl2aHvJ5GbOJ5zsN39MQ5sPziGsRM7QRw8u7HnJbEfR/vjPrL3ju8lzkuw916KYPq/cznlCo50tNgNjp+p323Ovmm9fh7B93Uw1UGMR7AN7zv6H+J0OSw9sP5S8WdmMHFNEFMDJmDmgrEwG9elsVp4Gu33/PtLTrvE6TQQNTcR9+sF/p0djOUDb6fxCw67H8iJG8WS4tpy4oaSfAg9ST74xHyQxOtKag+y2q8sXu8rMX/3tHhDV5IPss6/xI63MJIU9+XD34z3KCmurOv8My1/yZ1/ZbUHSef18/aAF9t+c27XcWM7iLFsmZy6pobz0t9tDeR9k5anAjLtfGhrb0un+GB6nea5KvmT1rHOsAaoYx0iTVB3U8y3AKOGyujaHQ29jdldoK0N9DRLOrpA2xWoY9tcgzq4DZuybW5ALquY7xXbdHG1ZccP7MzUQQ7G0wTfGRXTVOcX/bf+awlczF8Wt1nysQO8wsnrBrA9a8AXrcTCyg1gwd43acxb3JjZ8XGHGjO/JhvAE+bfLZiy9XfALwPZLx3kZHHZsiEtr5tdh3sC/T0OR0+h/p60vGG2/02BfpPDYSrUb0rLG2b3jpZAv8XhsBTqt6TlDZM0Rl+gv8/h6CvU35eWN+8jHAj0DzgcA4X6IV9VVqNmVvy83iGNMRToH3I4hgr1Q76qrEbNrPh58z7GkUD/iMMxUqgf8lVltWpmxc8bZeNFY4H+MYdjrFA/5KvKatbMip8377OeCPRPOBwThfohX1VWq2ZW/Lz5uOZUoH/K4Zgq1A/5qrL2CbGahFitmlnx8+ZjVjOB/hmHY6ZQP+SryjohxDomxFq3r/h5o6yPaS7QP+dwzBXqh3xVWY2aWfHzutmY6kKgf8HhWCjUD/mqsho1s+LnjbJ74aVA/5LDsVSoH/JVZTUJsfYJsU5rZsXPG2bPwqwE+lccjpVC/ZCvKqtBiHVQMyt+Xi9MY6wF+tccjrVC/Wtpeb3sd8tGoH/D4dgo1L+Rljd/9mkr0L/lcGwV6t+CvDvUvPmzBDBvWt57lmAn0YM05j1uzOx5qQfAz7Teg2W2Ho77PyBr00FOFpctQ76qrAYh1h4hVpMQq0WItU+IldKxNSDESsnXISFWSueBESFWSteCMSFWSu11QoiV0jX24z5LDuuUECulY4vS+ZWSrzNCrJTuX+eEWCn5uiDESukaS+nehdI1dkmI9aPfRQ7rihDrmhDrpmZWCeMlTjr+xfrrD6dT8uPn0/fkdDp8SxgJfLu6UczvAGkLzMM3vjtgOzbK9gnU3ZbqNKAOvsl7pUn8yzoYUy/F/o9er7d1DueZKa/lFybuFpAIUgAA"} \ No newline at end of file From 1d06de2f76a28d838e48f9073b44b02fcd500b62 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 31 Jan 2024 22:25:34 +0000 Subject: [PATCH 62/64] prettier --- tooling/noir_js/test/node/execute.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tooling/noir_js/test/node/execute.test.ts b/tooling/noir_js/test/node/execute.test.ts index 3a1513c3a57..491bcb0dfc4 100644 --- a/tooling/noir_js/test/node/execute.test.ts +++ b/tooling/noir_js/test/node/execute.test.ts @@ -25,9 +25,7 @@ it('circuit with a dynamic assert message should fail on an assert failure not t try { await new Noir(assert_msg_runtime).execute(inputs); } catch (error) { - const knownError = error as Error; - expect(knownError.message).to.equal( - 'Circuit execution failed: Error: Cannot satisfy constraint' - ) + const knownError = error as Error; + expect(knownError.message).to.equal('Circuit execution failed: Error: Cannot satisfy constraint'); } }); From a48f0655392d7acbb08d504eefe17694c1344412 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 1 Feb 2024 13:12:14 +0000 Subject: [PATCH 63/64] Apply suggestions from code review --- noirc_macros/src/lib.rs | 2 -- tooling/nargo_cli/build.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 927d6e495cc..4337214d69f 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -1,5 +1,3 @@ -// use noirc_frontend::macros_api::{parse_program, SortedModule, CrateId - use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::HirContext; use noirc_frontend::macros_api::SortedModule; diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 45833ab9c80..57aa487f66a 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -189,7 +189,6 @@ fn compile_success_empty_{test_name}() {{ // but we must call a backend as part of querying the number of opcodes in the circuit. let test_program_dir = PathBuf::from("{test_dir}"); - let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); cmd.arg("--program-dir").arg(test_program_dir); From 6f27d407cf6cd057bc9415141b1d44bdfbd727c9 Mon Sep 17 00:00:00 2001 From: Tom French Date: Thu, 1 Feb 2024 13:12:30 +0000 Subject: [PATCH 64/64] chore: delete unnecessary files --- test_programs/compile_failure/assert_msg_runtime/Prover.toml | 2 -- .../compile_failure/brillig_assert_msg_runtime/Prover.toml | 1 - 2 files changed, 3 deletions(-) delete mode 100644 test_programs/compile_failure/assert_msg_runtime/Prover.toml delete mode 100644 test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml diff --git a/test_programs/compile_failure/assert_msg_runtime/Prover.toml b/test_programs/compile_failure/assert_msg_runtime/Prover.toml deleted file mode 100644 index f28f2f8cc48..00000000000 --- a/test_programs/compile_failure/assert_msg_runtime/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "5" -y = "10" diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml b/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml deleted file mode 100644 index 0e5dfd5638d..00000000000 --- a/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = "5"