diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 0b93dbaa634..79b1eabb310 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -574,7 +574,7 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte true, )), )], - &BlockExpression(vec![storage_constructor_statement]), + &BlockExpression { is_unsafe: false, statements: vec![storage_constructor_statement] }, &[], &return_type(chained_path!("Self")), )); @@ -609,12 +609,12 @@ fn transform_function( // Add access to the storage struct if storage_defined { let storage_def = abstract_storage(&ty.to_lowercase(), false); - func.def.body.0.insert(0, storage_def); + func.def.body.statements.insert(0, storage_def); } // Insert the context creation as the first action let create_context = create_context(&context_name, &func.def.parameters)?; - func.def.body.0.splice(0..0, (create_context).iter().cloned()); + func.def.body.statements.splice(0..0, (create_context).iter().cloned()); // Add the inputs to the params let input = create_inputs(&inputs_name); @@ -622,12 +622,12 @@ fn transform_function( // Abstract return types such that they get added to the kernel's return_values if let Some(return_values) = abstract_return_values(func) { - func.def.body.0.push(return_values); + func.def.body.statements.push(return_values); } // Push the finish method call to the end of the function let finish_def = create_context_finish(); - func.def.body.0.push(finish_def); + func.def.body.statements.push(finish_def); let return_type = create_return_type(&return_type_name); func.def.return_type = return_type; @@ -651,7 +651,7 @@ fn transform_vm_function( ) -> Result<(), AztecMacroError> { // Push Avm context creation to the beginning of the function let create_context = create_avm_context()?; - func.def.body.0.insert(0, create_context); + func.def.body.statements.insert(0, create_context); // We want the function to be seen as a public function func.def.is_open = true; @@ -671,7 +671,7 @@ fn transform_vm_function( /// /// This will allow developers to access their contract' storage struct in unconstrained functions fn transform_unconstrained(func: &mut NoirFunction) { - func.def.body.0.insert(0, abstract_storage("Unconstrained", true)); + func.def.body.statements.insert(0, abstract_storage("Unconstrained", true)); } fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec { @@ -1002,10 +1002,15 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { let mut from_signature_path = selector_path.clone(); from_signature_path.segments.push(ident("from_signature")); - let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call( - variable_path(from_signature_path), - vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))], - )))]); + let selector_fun_body = BlockExpression { + is_unsafe: false, + statements: vec![make_statement(StatementKind::Expression(call( + variable_path(from_signature_path), + vec![expression(ExpressionKind::Literal(Literal::Str( + SIGNATURE_PLACEHOLDER.to_string(), + )))], + )))], + }; // Define `FunctionSelector` return type let return_type = @@ -1230,7 +1235,7 @@ fn create_avm_context() -> Result { fn abstract_return_values(func: &NoirFunction) -> Option { let current_return_type = func.return_type().typ; let len = func.def.body.len(); - let last_statement = &func.def.body.0[len - 1]; + let last_statement = &func.def.body.statements[len - 1]; // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size // Doesn't need done until we have settled on a kernel size @@ -1487,7 +1492,10 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { // What will be looped over // - `hasher.add({ident}[i] as Field)` - let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); + let for_loop_block = expression(ExpressionKind::Block(BlockExpression { + is_unsafe: false, + statements: loop_body, + })); // `for i in 0..{ident}.len()` make_statement(StatementKind::For(ForLoopStatement { diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 2a252633a29..dd4eca24784 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -180,16 +180,19 @@ impl Expression { // with tuples without calling them. E.g. `if c { t } else { e }(a, b)` is interpreted // as a sequence of { if, tuple } rather than a function call. This behavior matches rust. let kind = if matches!(&lhs.kind, ExpressionKind::If(..)) { - ExpressionKind::Block(BlockExpression(vec![ - Statement { kind: StatementKind::Expression(lhs), span }, - Statement { - kind: StatementKind::Expression(Expression::new( - ExpressionKind::Tuple(arguments), + ExpressionKind::Block(BlockExpression { + is_unsafe: false, + statements: vec![ + Statement { kind: StatementKind::Expression(lhs), span }, + Statement { + kind: StatementKind::Expression(Expression::new( + ExpressionKind::Tuple(arguments), + span, + )), span, - )), - span, - }, - ])) + }, + ], + }) } else { ExpressionKind::Call(Box::new(CallExpression { func: Box::new(lhs), arguments })) }; @@ -456,19 +459,22 @@ pub struct IndexExpression { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct BlockExpression(pub Vec); +pub struct BlockExpression { + pub is_unsafe: bool, + pub statements: Vec, +} impl BlockExpression { pub fn pop(&mut self) -> Option { - self.0.pop().map(|stmt| stmt.kind) + self.statements.pop().map(|stmt| stmt.kind) } pub fn len(&self) -> usize { - self.0.len() + self.statements.len() } pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.statements.is_empty() } } @@ -537,8 +543,9 @@ impl Display for Literal { impl Display for BlockExpression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{{")?; - for statement in &self.0 { + let safety = if self.is_unsafe { "unsafe " } else { "" }; + writeln!(f, "{safety}{{")?; + for statement in &self.statements { let statement = statement.kind.to_string(); for line in statement.lines() { writeln!(f, " {line}")?; diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index 46f0ac0fa0f..3e8b78c1312 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -83,7 +83,7 @@ impl NoirFunction { &mut self.def } pub fn number_of_statements(&self) -> usize { - self.def.body.0.len() + self.def.body.statements.len() } pub fn span(&self) -> Span { self.def.span diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index f39b71405d3..654554fe0e8 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -588,10 +588,13 @@ impl ForRange { }; let block_span = block.span; - let new_block = BlockExpression(vec![ - let_elem, - Statement { kind: StatementKind::Expression(block), span: block_span }, - ]); + let new_block = BlockExpression { + is_unsafe: false, + statements: vec![ + let_elem, + Statement { kind: StatementKind::Expression(block), span: block_span }, + ], + }; let new_block = Expression::new(ExpressionKind::Block(new_block), block_span); let for_loop = Statement { kind: StatementKind::For(ForLoopStatement { @@ -603,7 +606,10 @@ impl ForRange { span: for_loop_span, }; - let block = ExpressionKind::Block(BlockExpression(vec![let_array, for_loop])); + let block = ExpressionKind::Block(BlockExpression { + is_unsafe: false, + statements: vec![let_array, for_loop], + }); StatementKind::Expression(Expression::new(block, for_loop_span)) } } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index a88567fcaf9..983ec1ccc81 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -93,10 +93,10 @@ impl DebugInstrumenter { }) .collect(); - self.walk_scope(&mut func.body.0, func.span); + self.walk_scope(&mut func.body.statements, func.span); // prepend fn params: - func.body.0 = [set_fn_params, func.body.0.clone()].concat(); + func.body.statements = [set_fn_params, func.body.statements.clone()].concat(); } // Modify a vector of statements in-place, adding instrumentation for sets and drops. @@ -212,7 +212,10 @@ impl DebugInstrumenter { pattern: ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), r#type: ast::UnresolvedType::unspecified(), expression: ast::Expression { - kind: ast::ExpressionKind::Block(ast::BlockExpression(block_stmts)), + kind: ast::ExpressionKind::Block(ast::BlockExpression { + is_unsafe: true, + statements: block_stmts, + }), span: let_stmt.expression.span, }, }), @@ -299,11 +302,14 @@ impl DebugInstrumenter { kind: ast::StatementKind::Assign(ast::AssignStatement { lvalue: assign_stmt.lvalue.clone(), expression: ast::Expression { - kind: ast::ExpressionKind::Block(ast::BlockExpression(vec![ - ast::Statement { kind: let_kind, span: expression_span }, - new_assign_stmt, - ast::Statement { kind: ret_kind, span: expression_span }, - ])), + kind: ast::ExpressionKind::Block(ast::BlockExpression { + is_unsafe: false, + statements: vec![ + ast::Statement { kind: let_kind, span: expression_span }, + new_assign_stmt, + ast::Statement { kind: ret_kind, span: expression_span }, + ], + }), span: expression_span, }, }), @@ -313,7 +319,7 @@ impl DebugInstrumenter { fn walk_expr(&mut self, expr: &mut ast::Expression) { match &mut expr.kind { - ast::ExpressionKind::Block(ast::BlockExpression(ref mut statements)) => { + ast::ExpressionKind::Block(ast::BlockExpression { ref mut statements, .. }) => { self.scope.push(HashMap::default()); self.walk_scope(statements, expr.span); } @@ -384,14 +390,17 @@ impl DebugInstrumenter { self.walk_expr(&mut for_stmt.block); for_stmt.block = ast::Expression { - kind: ast::ExpressionKind::Block(ast::BlockExpression(vec![ - set_stmt, - ast::Statement { - kind: ast::StatementKind::Semi(for_stmt.block.clone()), - span: for_stmt.block.span, - }, - drop_stmt, - ])), + kind: ast::ExpressionKind::Block(ast::BlockExpression { + is_unsafe: false, + statements: vec![ + set_stmt, + ast::Statement { + kind: ast::StatementKind::Semi(for_stmt.block.clone()), + span: for_stmt.block.span, + }, + drop_stmt, + ], + }), span: for_stmt.span, }; } @@ -447,7 +456,9 @@ pub fn build_debug_crate_file() -> String { __debug_var_assign_oracle(var_id, value); } pub fn __debug_var_assign(var_id: u32, value: T) { - __debug_var_assign_inner(var_id, value); + unsafe {{ + __debug_var_assign_inner(var_id, value); + }} } #[oracle(__debug_var_drop)] @@ -456,7 +467,9 @@ pub fn build_debug_crate_file() -> String { __debug_var_drop_oracle(var_id); } pub fn __debug_var_drop(var_id: u32) { - __debug_var_drop_inner(var_id); + unsafe {{ + __debug_var_drop_inner(var_id); + }} } #[oracle(__debug_dereference_assign)] @@ -465,7 +478,9 @@ pub fn build_debug_crate_file() -> String { __debug_dereference_assign_oracle(var_id, value); } pub fn __debug_dereference_assign(var_id: u32, value: T) { - __debug_dereference_assign_inner(var_id, value); + unsafe {{ + __debug_dereference_assign_inner(var_id, value); + }} } "# .to_string(), @@ -489,7 +504,9 @@ pub fn build_debug_crate_file() -> String { __debug_oracle_member_assign_{n}(var_id, value, {vars}); }} pub fn __debug_member_assign_{n}(var_id: u32, value: T, {var_sig}) {{ - __debug_inner_member_assign_{n}(var_id, value, {vars}); + unsafe {{ + __debug_inner_member_assign_{n}(var_id, value, {vars}); + }} }} "# diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7f9e48353a7..ad469f8146d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -244,7 +244,7 @@ impl<'a> Resolver<'a> { typ: typ.clone(), span: name.span(), }), - body: BlockExpression(Vec::new()), + body: BlockExpression { is_unsafe: false, statements: Vec::new() }, span: name.span(), where_clause: where_clause.to_vec(), return_type: return_type.clone(), @@ -1926,9 +1926,9 @@ impl<'a> Resolver<'a> { } fn resolve_block(&mut self, block_expr: BlockExpression) -> HirExpression { - let statements = - self.in_new_scope(|this| vecmap(block_expr.0, |stmt| this.intern_stmt(stmt.kind))); - HirExpression::Block(HirBlockExpression(statements)) + let statements = self + .in_new_scope(|this| vecmap(block_expr.statements, |stmt| this.intern_stmt(stmt.kind))); + HirExpression::Block(HirBlockExpression { is_unsafe: block_expr.is_unsafe, statements }) } pub fn intern_block(&mut self, block: BlockExpression) -> ExprId { diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 96d30100d8b..ff2a4977e72 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -122,6 +122,8 @@ pub enum TypeCheckError { ConstrainedReferenceToUnconstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, + #[error("unsafe")] + Unsafe { span: Span }, } impl TypeCheckError { @@ -211,7 +213,7 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } - | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { + | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } | TypeCheckError::Unsafe { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index b78f07c88f2..ce0246f1e94 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -56,13 +56,14 @@ impl<'interner> TypeChecker<'interner> { /// an equivalent HirExpression::Call in the form `foo(a, b, c)`. This cannot /// be done earlier since we need to know the type of the object `a` to resolve which /// function `foo` to refer to. - pub(crate) fn check_expression(&mut self, expr_id: &ExprId) -> Type { + pub(crate) fn check_expression(&mut self, expr_id: &ExprId, allow_unsafe_call: bool) -> Type { let typ = match self.interner.expression(expr_id) { HirExpression::Ident(ident) => self.check_ident(ident, expr_id), HirExpression::Literal(literal) => { match literal { HirLiteral::Array(HirArrayLiteral::Standard(arr)) => { - let elem_types = vecmap(&arr, |arg| self.check_expression(arg)); + let elem_types = + vecmap(&arr, |arg| self.check_expression(arg, allow_unsafe_call)); let first_elem_type = elem_types .first() @@ -94,7 +95,7 @@ impl<'interner> TypeChecker<'interner> { arr_type } HirLiteral::Array(HirArrayLiteral::Repeated { repeated_element, length }) => { - let elem_type = self.check_expression(&repeated_element); + let elem_type = self.check_expression(&repeated_element, allow_unsafe_call); let length = match length { Type::Constant(length) => { Type::constant_variable(length, self.interner) @@ -111,7 +112,8 @@ impl<'interner> TypeChecker<'interner> { } HirLiteral::FmtStr(string, idents) => { let len = Type::Constant(string.len() as u64); - let types = vecmap(&idents, |elem| self.check_expression(elem)); + let types = + vecmap(&idents, |elem| self.check_expression(elem, allow_unsafe_call)); Type::FmtString(Box::new(len), Box::new(Type::Tuple(types))) } HirLiteral::Unit => Type::Unit, @@ -119,8 +121,8 @@ impl<'interner> TypeChecker<'interner> { } HirExpression::Infix(infix_expr) => { // The type of the infix expression must be looked up from a type table - let lhs_type = self.check_expression(&infix_expr.lhs); - let rhs_type = self.check_expression(&infix_expr.rhs); + let lhs_type = self.check_expression(&infix_expr.lhs, allow_unsafe_call); + let rhs_type = self.check_expression(&infix_expr.rhs, allow_unsafe_call); let lhs_span = self.interner.expr_span(&infix_expr.lhs); let rhs_span = self.interner.expr_span(&infix_expr.rhs); @@ -149,7 +151,9 @@ impl<'interner> TypeChecker<'interner> { } } } - HirExpression::Index(index_expr) => self.check_index_expression(expr_id, index_expr), + HirExpression::Index(index_expr) => { + self.check_index_expression(expr_id, index_expr, allow_unsafe_call) + } HirExpression::Call(call_expr) => { // Need to setup these flags here as `self` is borrowed mutably to type check the rest of the call expression // These flags are later used to type check calls to unconstrained functions from constrained functions @@ -161,15 +165,22 @@ impl<'interner> TypeChecker<'interner> { self.check_if_deprecated(&call_expr.func); - let function = self.check_expression(&call_expr.func); + let function = self.check_expression(&call_expr.func, allow_unsafe_call); let args = vecmap(&call_expr.arguments, |arg| { - let typ = self.check_expression(arg); + let typ = self.check_expression(arg, allow_unsafe_call); (typ, *arg, self.interner.expr_span(arg)) }); - // Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime if is_current_func_constrained && is_unconstrained_call { + if !allow_unsafe_call { + self.errors.push(TypeCheckError::Unsafe { + span: self.interner.expr_span(expr_id), + }); + return Type::Error; + } + + // Check that we are not passing a mutable reference from a constrained runtime to an unconstrained runtime for (typ, _, _) in args.iter() { if matches!(&typ.follow_bindings(), Type::MutableReference(_)) { self.errors.push(TypeCheckError::ConstrainedReferenceToUnconstrained { @@ -197,7 +208,8 @@ impl<'interner> TypeChecker<'interner> { return_type } HirExpression::MethodCall(mut method_call) => { - let mut object_type = self.check_expression(&method_call.object).follow_bindings(); + let mut object_type = + self.check_expression(&method_call.object, allow_unsafe_call).follow_bindings(); let method_name = method_call.method.0.contents.as_str(); match self.lookup_method(&object_type, method_name, expr_id) { Some(method_ref) => { @@ -232,23 +244,24 @@ impl<'interner> TypeChecker<'interner> { // Type check the new call now that it has been changed from a method call // to a function call. This way we avoid duplicating code. - self.check_expression(expr_id) + self.check_expression(expr_id, allow_unsafe_call) } None => Type::Error, } } HirExpression::Cast(cast_expr) => { // Evaluate the LHS - let lhs_type = self.check_expression(&cast_expr.lhs); + let lhs_type = self.check_expression(&cast_expr.lhs, allow_unsafe_call); let span = self.interner.expr_span(expr_id); self.check_cast(lhs_type, cast_expr.r#type, span) } HirExpression::Block(block_expr) => { let mut block_type = Type::Unit; + let allow_unsafe = allow_unsafe_call || block_expr.is_unsafe; let statements = block_expr.statements(); for (i, stmt) in statements.iter().enumerate() { - let expr_type = self.check_statement(stmt); + let expr_type = self.check_statement(stmt, allow_unsafe); if let crate::hir_def::stmt::HirStatement::Semi(expr) = self.interner.statement(stmt) @@ -272,17 +285,21 @@ impl<'interner> TypeChecker<'interner> { block_type } HirExpression::Prefix(prefix_expr) => { - let rhs_type = self.check_expression(&prefix_expr.rhs); + let rhs_type = self.check_expression(&prefix_expr.rhs, allow_unsafe_call); let span = self.interner.expr_span(&prefix_expr.rhs); self.type_check_prefix_operand(&prefix_expr.operator, &rhs_type, span) } - HirExpression::If(if_expr) => self.check_if_expr(&if_expr, expr_id), - HirExpression::Constructor(constructor) => self.check_constructor(constructor, expr_id), - HirExpression::MemberAccess(access) => self.check_member_access(access, *expr_id), - HirExpression::Error => Type::Error, - HirExpression::Tuple(elements) => { - Type::Tuple(vecmap(&elements, |elem| self.check_expression(elem))) + HirExpression::If(if_expr) => self.check_if_expr(&if_expr, expr_id, allow_unsafe_call), + HirExpression::Constructor(constructor) => { + self.check_constructor(constructor, expr_id, allow_unsafe_call) + } + HirExpression::MemberAccess(access) => { + self.check_member_access(access, *expr_id, allow_unsafe_call) } + HirExpression::Error => Type::Error, + HirExpression::Tuple(elements) => Type::Tuple(vecmap(&elements, |elem| { + self.check_expression(elem, allow_unsafe_call) + })), HirExpression::Lambda(lambda) => { let captured_vars = vecmap(lambda.captures, |capture| { self.interner.definition_type(capture.ident.id) @@ -296,7 +313,7 @@ impl<'interner> TypeChecker<'interner> { typ }); - let actual_return = self.check_expression(&lambda.body); + let actual_return = self.check_expression(&lambda.body, allow_unsafe_call); let span = self.interner.expr_span(&lambda.body); self.unify(&actual_return, &lambda.return_type, || TypeCheckError::TypeMismatch { @@ -525,8 +542,9 @@ impl<'interner> TypeChecker<'interner> { &mut self, id: &ExprId, mut index_expr: expr::HirIndexExpression, + allow_unsafe_call: bool, ) -> Type { - let index_type = self.check_expression(&index_expr.index); + let index_type = self.check_expression(&index_expr.index, allow_unsafe_call); let span = self.interner.expr_span(&index_expr.index); index_type.unify( @@ -541,7 +559,7 @@ impl<'interner> TypeChecker<'interner> { // When writing `a[i]`, if `a : &mut ...` then automatically dereference `a` as many // times as needed to get the underlying array. - let lhs_type = self.check_expression(&index_expr.collection); + let lhs_type = self.check_expression(&index_expr.collection, allow_unsafe_call); let (new_lhs, lhs_type) = self.insert_auto_dereferences(index_expr.collection, lhs_type); index_expr.collection = new_lhs; self.interner.replace_expr(id, HirExpression::Index(index_expr)); @@ -594,9 +612,14 @@ impl<'interner> TypeChecker<'interner> { } } - fn check_if_expr(&mut self, if_expr: &expr::HirIfExpression, expr_id: &ExprId) -> Type { - let cond_type = self.check_expression(&if_expr.condition); - let then_type = self.check_expression(&if_expr.consequence); + fn check_if_expr( + &mut self, + if_expr: &expr::HirIfExpression, + expr_id: &ExprId, + allow_unsafe_call: bool, + ) -> Type { + let cond_type = self.check_expression(&if_expr.condition, allow_unsafe_call); + let then_type = self.check_expression(&if_expr.consequence, allow_unsafe_call); let expr_span = self.interner.expr_span(&if_expr.condition); @@ -609,7 +632,7 @@ impl<'interner> TypeChecker<'interner> { match if_expr.alternative { None => Type::Unit, Some(alternative) => { - let else_type = self.check_expression(&alternative); + let else_type = self.check_expression(&alternative, allow_unsafe_call); let expr_span = self.interner.expr_span(expr_id); self.unify(&then_type, &else_type, || { @@ -639,6 +662,7 @@ impl<'interner> TypeChecker<'interner> { &mut self, constructor: expr::HirConstructorExpression, expr_id: &ExprId, + allow_unsafe_call: bool, ) -> Type { let typ = constructor.r#type; let generics = constructor.struct_generics; @@ -658,7 +682,7 @@ impl<'interner> TypeChecker<'interner> { // mismatch here as long as we continue typechecking the rest of the program to the best // of our ability. if param_name == arg_ident.0.contents { - let arg_type = self.check_expression(&arg); + let arg_type = self.check_expression(&arg, allow_unsafe_call); let span = self.interner.expr_span(expr_id); self.unify_with_coercions(&arg_type, ¶m_type, arg, || { @@ -674,8 +698,13 @@ impl<'interner> TypeChecker<'interner> { Type::Struct(typ, generics) } - fn check_member_access(&mut self, mut access: expr::HirMemberAccess, expr_id: ExprId) -> Type { - let lhs_type = self.check_expression(&access.lhs).follow_bindings(); + fn check_member_access( + &mut self, + mut access: expr::HirMemberAccess, + expr_id: ExprId, + allow_unsafe_call: bool, + ) -> Type { + let lhs_type = self.check_expression(&access.lhs, allow_unsafe_call).follow_bindings(); let span = self.interner.expr_span(&expr_id); let access_lhs = &mut access.lhs; diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 21d1c75a0f2..57e2e47ed8c 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -168,7 +168,7 @@ impl<'interner> TypeChecker<'interner> { } fn check_function_body(&mut self, body: &ExprId) -> Type { - self.check_expression(body) + self.check_expression(body, false) } pub fn check_global( @@ -182,7 +182,7 @@ impl<'interner> TypeChecker<'interner> { current_function: None, }; let statement = this.interner.get_global(id).let_statement; - this.check_statement(&statement); + this.check_statement(&statement, false); this.errors } @@ -297,7 +297,10 @@ mod test { expression: expr_id, }; let stmt_id = interner.push_stmt(HirStatement::Let(let_stmt)); - let expr_id = interner.push_expr(HirExpression::Block(HirBlockExpression(vec![stmt_id]))); + let expr_id = interner.push_expr(HirExpression::Block(HirBlockExpression { + is_unsafe: false, + statements: vec![stmt_id], + })); interner.push_expr_location(expr_id, Span::single_char(0), file); // Create function to enclose the let statement diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 358bea86922..13595bc5bc5 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -19,7 +19,7 @@ impl<'interner> TypeChecker<'interner> { /// All statements have a unit type `()` as their type so the type of the statement /// is not interesting. Type checking must still be done on statements to ensure any /// expressions used within them are typed correctly. - pub(crate) fn check_statement(&mut self, stmt_id: &StmtId) -> Type { + pub(crate) fn check_statement(&mut self, stmt_id: &StmtId, allow_unsafe_call: bool) -> Type { match self.interner.statement(stmt_id) { // Lets lay out a convincing argument that the handling of // SemiExpressions and Expressions below is correct. @@ -42,23 +42,27 @@ impl<'interner> TypeChecker<'interner> { // // The reason why we still modify the database, is to make sure it is future-proof HirStatement::Expression(expr_id) => { - return self.check_expression(&expr_id); + return self.check_expression(&expr_id, allow_unsafe_call); } HirStatement::Semi(expr_id) => { - self.check_expression(&expr_id); + self.check_expression(&expr_id, allow_unsafe_call); } - HirStatement::Let(let_stmt) => self.check_let_stmt(let_stmt), - HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt), - HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id), - HirStatement::For(for_loop) => self.check_for_loop(for_loop), + HirStatement::Let(let_stmt) => self.check_let_stmt(let_stmt, allow_unsafe_call), + HirStatement::Constrain(constrain_stmt) => { + self.check_constrain_stmt(constrain_stmt, allow_unsafe_call); + } + HirStatement::Assign(assign_stmt) => { + self.check_assign_stmt(assign_stmt, stmt_id, allow_unsafe_call); + } + HirStatement::For(for_loop) => self.check_for_loop(for_loop, allow_unsafe_call), HirStatement::Error => (), } Type::Unit } - fn check_for_loop(&mut self, for_loop: HirForStatement) { - let start_range_type = self.check_expression(&for_loop.start_range); - let end_range_type = self.check_expression(&for_loop.end_range); + fn check_for_loop(&mut self, for_loop: HirForStatement, allow_unsafe_call: bool) { + let start_range_type = self.check_expression(&for_loop.start_range, allow_unsafe_call); + let end_range_type = self.check_expression(&for_loop.end_range, allow_unsafe_call); let start_span = self.interner.expr_span(&for_loop.start_range); let end_span = self.interner.expr_span(&for_loop.end_range); @@ -81,7 +85,7 @@ impl<'interner> TypeChecker<'interner> { self.interner.push_definition_type(for_loop.identifier.id, start_range_type); - self.check_expression(&for_loop.block); + self.check_expression(&for_loop.block, allow_unsafe_call); } /// Associate a given HirPattern with the given Type, and remember @@ -132,10 +136,16 @@ impl<'interner> TypeChecker<'interner> { } } - fn check_assign_stmt(&mut self, assign_stmt: HirAssignStatement, stmt_id: &StmtId) { - let expr_type = self.check_expression(&assign_stmt.expression); + fn check_assign_stmt( + &mut self, + assign_stmt: HirAssignStatement, + stmt_id: &StmtId, + allow_unsafe_call: bool, + ) { + let expr_type = self.check_expression(&assign_stmt.expression, allow_unsafe_call); let span = self.interner.expr_span(&assign_stmt.expression); - let (lvalue_type, new_lvalue, mutable) = self.check_lvalue(&assign_stmt.lvalue, span); + let (lvalue_type, new_lvalue, mutable) = + self.check_lvalue(&assign_stmt.lvalue, span, allow_unsafe_call); if !mutable { let (name, span) = self.get_lvalue_name_and_span(&assign_stmt.lvalue); @@ -177,7 +187,12 @@ impl<'interner> TypeChecker<'interner> { } /// Type check an lvalue - the left hand side of an assignment statement. - fn check_lvalue(&mut self, lvalue: &HirLValue, assign_span: Span) -> (Type, HirLValue, bool) { + fn check_lvalue( + &mut self, + lvalue: &HirLValue, + assign_span: Span, + allow_unsafe_call: bool, + ) -> (Type, HirLValue, bool) { match lvalue { HirLValue::Ident(ident, _) => { let mut mutable = true; @@ -196,7 +211,8 @@ impl<'interner> TypeChecker<'interner> { (typ.clone(), HirLValue::Ident(ident.clone(), typ), mutable) } HirLValue::MemberAccess { object, field_name, .. } => { - let (lhs_type, object, mut mutable) = self.check_lvalue(object, assign_span); + let (lhs_type, object, mut mutable) = + self.check_lvalue(object, assign_span, allow_unsafe_call); let mut object = Box::new(object); let span = field_name.span(); let field_name = field_name.clone(); @@ -228,7 +244,7 @@ impl<'interner> TypeChecker<'interner> { (object_type, lvalue, mutable) } HirLValue::Index { array, index, .. } => { - let index_type = self.check_expression(index); + let index_type = self.check_expression(index, allow_unsafe_call); let expr_span = self.interner.expr_span(index); index_type.unify( @@ -242,7 +258,7 @@ impl<'interner> TypeChecker<'interner> { ); let (mut lvalue_type, mut lvalue, mut mutable) = - self.check_lvalue(array, assign_span); + self.check_lvalue(array, assign_span, allow_unsafe_call); // Before we check that the lvalue is an array, try to dereference it as many times // as needed to unwrap any &mut wrappers. @@ -272,7 +288,8 @@ impl<'interner> TypeChecker<'interner> { (typ.clone(), HirLValue::Index { array, index: *index, typ }, mutable) } HirLValue::Dereference { lvalue, element_type: _ } => { - let (reference_type, lvalue, _) = self.check_lvalue(lvalue, assign_span); + let (reference_type, lvalue, _) = + self.check_lvalue(lvalue, assign_span, allow_unsafe_call); let lvalue = Box::new(lvalue); let element_type = Type::type_variable(self.interner.next_type_variable_id()); @@ -290,19 +307,20 @@ impl<'interner> TypeChecker<'interner> { } } - fn check_let_stmt(&mut self, let_stmt: HirLetStatement) { - let resolved_type = self.check_declaration(let_stmt.expression, let_stmt.r#type); + fn check_let_stmt(&mut self, let_stmt: HirLetStatement, allow_unsafe_call: bool) { + let resolved_type = + self.check_declaration(let_stmt.expression, let_stmt.r#type, allow_unsafe_call); // Set the type of the pattern to be equal to the annotated type self.bind_pattern(&let_stmt.pattern, resolved_type); } - fn check_constrain_stmt(&mut self, stmt: HirConstrainStatement) { - let expr_type = self.check_expression(&stmt.0); + fn check_constrain_stmt(&mut self, stmt: HirConstrainStatement, allow_unsafe_call: bool) { + let expr_type = self.check_expression(&stmt.0, allow_unsafe_call); 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)); + stmt.2.map(|assert_msg_expr| self.check_expression(&assert_msg_expr, true)); self.unify(&expr_type, &Type::Bool, || TypeCheckError::TypeMismatch { expr_typ: expr_type.to_string(), @@ -314,9 +332,14 @@ impl<'interner> TypeChecker<'interner> { /// All declaration statements check that the user specified type(UST) is equal to the /// expression on the RHS, unless the UST is unspecified in which case /// the type of the declaration is inferred to match the RHS. - fn check_declaration(&mut self, rhs_expr: ExprId, annotated_type: Type) -> Type { + fn check_declaration( + &mut self, + rhs_expr: ExprId, + annotated_type: Type, + allow_unsafe_call: bool, + ) -> Type { // Type check the expression on the RHS - let expr_type = self.check_expression(&rhs_expr); + let expr_type = self.check_expression(&rhs_expr, allow_unsafe_call); // First check if the LHS is unspecified // If so, then we give it the same type as the expression diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index b4c590de491..f094e1058dd 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -36,7 +36,7 @@ pub enum HirExpression { impl HirExpression { /// Returns an empty block expression pub const fn empty_block() -> HirExpression { - HirExpression::Block(HirBlockExpression(vec![])) + HirExpression::Block(HirBlockExpression { is_unsafe: false, statements: vec![] }) } } @@ -247,11 +247,14 @@ pub struct HirIndexExpression { } #[derive(Debug, Clone)] -pub struct HirBlockExpression(pub Vec); +pub struct HirBlockExpression { + pub is_unsafe: bool, + pub statements: Vec, +} impl HirBlockExpression { pub fn statements(&self) -> &[StmtId] { - &self.0 + &self.statements } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index fe12132e202..a772f5f1089 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -674,6 +674,7 @@ pub enum Keyword { Trait, Type, Unconstrained, + Unsafe, Use, Where, While, @@ -716,6 +717,7 @@ impl fmt::Display for Keyword { Keyword::Trait => write!(f, "trait"), Keyword::Type => write!(f, "type"), Keyword::Unconstrained => write!(f, "unconstrained"), + Keyword::Unsafe => write!(f, "unsafe"), Keyword::Use => write!(f, "use"), Keyword::Where => write!(f, "where"), Keyword::While => write!(f, "while"), @@ -761,6 +763,8 @@ impl Keyword { "trait" => Keyword::Trait, "type" => Keyword::Type, "unconstrained" => Keyword::Unconstrained, + "unsafe" => Keyword::Unsafe, + "use" => Keyword::Use, "where" => Keyword::Where, "while" => Keyword::While, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2e714da21c6..0ac629af18e 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -373,7 +373,7 @@ impl<'interner> Monomorphizer<'interner> { } }, HirExpression::Literal(HirLiteral::Unit) => ast::Expression::Block(vec![]), - HirExpression::Block(block) => self.block(block.0), + HirExpression::Block(block) => self.block(block.statements), HirExpression::Prefix(prefix) => { let location = self.interner.expr_location(&expr); diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 75f4a6359bf..38146a67e7c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -333,21 +333,28 @@ fn block_expr<'a>( fn block<'a>( statement: impl NoirParser + 'a, ) -> impl NoirParser + 'a { + use crate::token::Keyword; use Token::*; - statement - .recover_via(statement_recovery()) - .then(just(Semicolon).or_not().map_with_span(|s, span| (s, span))) - .map_with_span(|(kind, rest), span| (Statement { kind, span }, rest)) - .repeated() - .validate(check_statements_require_semicolon) - .delimited_by(just(LeftBrace), just(RightBrace)) - .recover_with(nested_delimiters( - LeftBrace, - RightBrace, - [(LeftParen, RightParen), (LeftBracket, RightBracket)], - |span| vec![Statement { kind: StatementKind::Error, span }], - )) - .map(BlockExpression) + + keyword(Keyword::Unsafe) + .or_not() + .map(|unsafe_token| unsafe_token.is_some()) + .then( + statement + .recover_via(statement_recovery()) + .then(just(Semicolon).or_not().map_with_span(|s, span| (s, span))) + .map_with_span(|(kind, rest), span| (Statement { kind, span }, rest)) + .repeated() + .validate(check_statements_require_semicolon) + .delimited_by(just(LeftBrace), just(RightBrace)) + .recover_with(nested_delimiters( + LeftBrace, + RightBrace, + [(LeftParen, RightParen), (LeftBracket, RightBracket)], + |span| vec![Statement { kind: StatementKind::Error, span }], + )), + ) + .map(|(is_unsafe, statements)| BlockExpression { is_unsafe, statements }) } fn check_statements_require_semicolon( @@ -993,10 +1000,13 @@ where // Wrap the inner `if` expression in a block expression. // i.e. rewrite the sugared form `if cond1 {} else if cond2 {}` as `if cond1 {} else { if cond2 {} }`. let if_expression = Expression::new(kind, span); - let desugared_else = BlockExpression(vec![Statement { - kind: StatementKind::Expression(if_expression), - span, - }]); + let desugared_else = BlockExpression { + is_unsafe: false, + statements: vec![Statement { + kind: StatementKind::Expression(if_expression), + span, + }], + }; Expression::new(ExpressionKind::Block(desugared_else), span) })); @@ -1288,13 +1298,13 @@ mod test { // Regression for #1310: this should be parsed as a block and not a function call let res = parse_with(block(fresh_statement()), "{ if true { 1 } else { 2 } (3, 4) }").unwrap(); - match unwrap_expr(&res.0.last().unwrap().kind) { + match unwrap_expr(&res.statements.last().unwrap().kind) { // The `if` followed by a tuple is currently creates a block around both in case // there was none to start with, so there is an extra block here. ExpressionKind::Block(block) => { - assert_eq!(block.0.len(), 2); - assert!(matches!(unwrap_expr(&block.0[0].kind), ExpressionKind::If(_))); - assert!(matches!(unwrap_expr(&block.0[1].kind), ExpressionKind::Tuple(_))); + assert_eq!(block.statements.len(), 2); + assert!(matches!(unwrap_expr(&block.statements[0].kind), ExpressionKind::If(_))); + assert!(matches!(unwrap_expr(&block.statements[1].kind), ExpressionKind::Tuple(_))); } _ => unreachable!(), } diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index 7871b1a6f9a..c7558ed8ed7 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -11,17 +11,25 @@ impl [T; N] { } pub fn sort_via(self, ordering: fn[Env](T, T) -> bool) -> Self { - let sorted_index = self.get_sorting_index(ordering); + + let sorted_index = unsafe { + // Safety: This is safe we we enforce proper ordering using these indices + let sorted_index: [u64; N] = self.get_sorting_index(ordering); + + // Ensure the indexes are correct + for i in 0..N { + let pos = find_index(sorted_index, i); + assert(sorted_index[pos] == i); + } + + sorted_index + }; + + // Sort the array using the indexes let mut result = self; - // Ensure the indexes are correct for i in 0..N { - let pos = find_index(sorted_index, i); - assert(sorted_index[pos] == i); - } - // Sort the array using the indexes - for i in 0..N { result[i] = self[sorted_index[i]]; - } + } // Ensure the array is sorted for i in 0..N-1 { assert(ordering(result[i], result[i+1])); diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr index d9eb83ff5dc..99189ed6e1e 100644 --- a/noir_stdlib/src/collections/map.nr +++ b/noir_stdlib/src/collections/map.nr @@ -171,7 +171,7 @@ impl HashMap { for slot in self._table { if slot.is_valid() { - let (_, value) = slot.key_value_unchecked(); + let (_, value) = unsafe {slot.key_value_unchecked()}; values[valid_amount] = Option::some(value); valid_amount += 1; } diff --git a/noir_stdlib/src/field/bn254.nr b/noir_stdlib/src/field/bn254.nr index 9e1445fd3ba..0931ef3ca25 100644 --- a/noir_stdlib/src/field/bn254.nr +++ b/noir_stdlib/src/field/bn254.nr @@ -24,15 +24,21 @@ unconstrained fn decompose_unsafe(x: Field) -> (Field, Field) { /// Decompose a single field into two 16 byte fields. pub fn decompose(x: Field) -> (Field, Field) { // Take hints of the decomposition - let (xlo, xhi) = decompose_unsafe(x); - let borrow = lt_unsafe(PLO, xlo, 16); + let (xlo, xhi) = unsafe { + let (xlo, xhi) = decompose_unsafe(x); + + // Range check the limbs + xlo.assert_max_bit_size(128); + xhi.assert_max_bit_size(128); - // Range check the limbs - xlo.assert_max_bit_size(128); - xhi.assert_max_bit_size(128); + // Check that the decomposition is correct + assert_eq(x, xlo + TWO_POW_128 * xhi); - // Check that the decomposition is correct - assert_eq(x, xlo + TWO_POW_128 * xhi); + (xlo, xhi) + }; + + + let borrow = unsafe {lt_unsafe(PLO, xlo, 16)}; // Check that (xlo < plo && xhi <= phi) || (xlo >= plo && xhi < phi) let rlo = PLO - xlo + (borrow as Field) * TWO_POW_128; @@ -72,7 +78,7 @@ pub fn assert_gt(a: Field, b: Field) { let (alo, ahi) = decompose(a); let (blo, bhi) = decompose(b); - let borrow = lte_unsafe(alo, blo, 16); + let borrow = unsafe { lte_unsafe(alo, blo, 16) }; // Assert that (alo > blo && ahi >= bhi) || (alo <= blo && ahi > bhi) let rlo = alo - blo - 1 + (borrow as Field) * TWO_POW_128; @@ -89,13 +95,18 @@ pub fn assert_lt(a: Field, b: Field) { pub fn gt(a: Field, b: Field) -> bool { if a == b { false - } else if lt_unsafe(a, b, 32) { - assert_gt(b, a); - false } else { - assert_gt(a, b); - true + let a_lt_b = unsafe {lt_unsafe(a, b, 32) }; + if a_lt_b { + assert_gt(b, a); + false + } else { + assert_gt(a, b); + true + } } + + } pub fn lt(a: Field, b: Field) -> bool { diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index ebde4b88858..6586d10ca66 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -13,7 +13,7 @@ mod sha256; mod sha512; mod field; mod ec; -mod unsafe; +mod unsafe_func; mod collections; mod compat; mod convert; @@ -32,12 +32,20 @@ mod bigint; #[oracle(print)] unconstrained fn print_oracle(with_newline: bool, input: T) {} -unconstrained pub fn print(input: T) { - print_oracle(false, input); +unconstrained fn print_unconstrained(with_newline: bool, input: T) { + print_oracle(with_newline, input); } -unconstrained pub fn println(input: T) { - print_oracle(true, input); +pub fn println(input: T) { + unsafe { + print_unconstrained(true, input); + } +} + +pub fn print(input: T) { + unsafe { + print_unconstrained(false, input); + } } #[foreign(recursive_aggregation)] diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index cab95731d05..8029e4f68d8 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -6,7 +6,7 @@ struct Option { impl Option { /// Constructs a None value pub fn none() -> Self { - Self { _is_some: false, _value: crate::unsafe::zeroed() } + Self { _is_some: false, _value: crate::unsafe_func::zeroed() } } /// Constructs a Some wrapper around the given value @@ -39,11 +39,7 @@ impl Option { /// Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. pub fn unwrap_or(self, default: T) -> T { - if self._is_some { - self._value - } else { - default - } + if self._is_some { self._value } else { default } } /// Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return @@ -112,31 +108,19 @@ impl Option { /// If self is Some, return self. Otherwise, return `other`. pub fn or(self, other: Self) -> Self { - if self._is_some { - self - } else { - other - } + if self._is_some { self } else { other } } /// If self is Some, return self. Otherwise, return `default()`. pub fn or_else(self, default: fn[Env]() -> Self) -> Self { - if self._is_some { - self - } else { - default() - } + if self._is_some { self } else { default() } } // If only one of the two Options is Some, return that option. // Otherwise, if both options are Some or both are None, None is returned. pub fn xor(self, other: Self) -> Self { if self._is_some { - if other._is_some { - Option::none() - } else { - self - } + if other._is_some { Option::none() } else { self } } else if other._is_some { other } else { diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index c8c6217de90..d5b6b37b19e 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -210,21 +210,26 @@ impl Mul for U128 { impl Div for U128 { fn div(self: Self, b: U128) -> U128 { - let (q,r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - q + unsafe { + let (q,r) = self.unconstrained_div(b); + let a = b * q + r; + assert_eq(self, a); + assert(r < b); + q + } } } impl Rem for U128 { fn rem(self: Self, b: U128) -> U128 { - let (q,r) = self.unconstrained_div(b); - let a = b * q + r; - assert_eq(self, a); - assert(r < b); - r + unsafe { + let (q,r) = self.unconstrained_div(b); + let a = b * q + r; + assert_eq(self, a); + assert(r < b); + + r + } } } diff --git a/noir_stdlib/src/unsafe.nr b/noir_stdlib/src/unsafe_func.nr similarity index 100% rename from noir_stdlib/src/unsafe.nr rename to noir_stdlib/src/unsafe_func.nr diff --git a/tooling/nargo_fmt/src/visitor/expr.rs b/tooling/nargo_fmt/src/visitor/expr.rs index 2cd0e881e84..f9836adda18 100644 --- a/tooling/nargo_fmt/src/visitor/expr.rs +++ b/tooling/nargo_fmt/src/visitor/expr.rs @@ -119,11 +119,11 @@ impl FmtVisitor<'_> { self.last_position = block_span.start() + 1; // `{` self.push_str("{"); - self.trim_spaces_after_opening_brace(&block.0); + self.trim_spaces_after_opening_brace(&block.statements); self.indent.block_indent(self.config); - self.visit_stmts(block.0); + self.visit_stmts(block.statements); let span = (self.last_position..block_span.end() - 1).into(); self.close_block(span);