diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..158729d3edf 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +5730e678d3b5aedacf327b1a9c2b69cc6916c176 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index c07d2d8a4c1..16fb26e55db 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index dd3ec7f6a75..7a8a7187857 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -324,101 +324,105 @@ pub fn update_fn_signatures_in_contract_interface( }); if let Some(interface_struct) = maybe_interface_struct { - let methods = context.def_interner.get_struct_methods(interface_struct.borrow().id); - - for func_id in methods.iter().flat_map(|methods| methods.direct.iter()) { - let name = context.def_interner.function_name(func_id); - let fn_parameters = &context.def_interner.function_meta(func_id).parameters.clone(); - - if name == "at" || name == "interface" || name == "storage" { - continue; - } - - let fn_signature_hash = compute_fn_signature_hash( - name, - &fn_parameters - .iter() - .skip(1) - .map(|(_, typ, _)| typ.clone()) - .collect::>(), - ); - let hir_func = context.def_interner.function(func_id).block(&context.def_interner); - - let function_selector_statement = context.def_interner.statement( - hir_func.statements().get(hir_func.statements().len() - 2).ok_or(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "Function signature statement not found, invalid body length" - .to_string(), - ), - }, - file_id, - ))?, - ); - let function_selector_expression_id = match function_selector_statement { - HirStatement::Let(let_statement) => Ok(let_statement.expression), - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "Function selector statement must be an expression".to_string(), - ), - }, - file_id, - )), - }?; - let function_selector_expression = - context.def_interner.expression(&function_selector_expression_id); - - let current_fn_signature_expression_id = match function_selector_expression { - HirExpression::Call(call_expr) => Ok(call_expr.arguments[0]), - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "Function selector argument expression must be call expression" - .to_string(), - ), - }, - file_id, - )), - }?; - - let current_fn_signature_expression = - context.def_interner.expression(¤t_fn_signature_expression_id); + if let Some(methods) = + context.def_interner.get_struct_methods(interface_struct.borrow().id).cloned() + { + for func_id in methods.iter().flat_map(|(_name, methods)| methods.direct.iter()) { + let name = context.def_interner.function_name(func_id); + let fn_parameters = + &context.def_interner.function_meta(func_id).parameters.clone(); + + if name == "at" || name == "interface" || name == "storage" { + continue; + } - match current_fn_signature_expression { - HirExpression::Literal(HirLiteral::Integer(value, _)) => { - if !value.is_zero() { - Err(( + let fn_signature_hash = compute_fn_signature_hash( + name, + &fn_parameters + .iter() + .skip(1) + .map(|(_, typ, _)| typ.clone()) + .collect::>(), + ); + let hir_func = + context.def_interner.function(func_id).block(&context.def_interner); + + let function_selector_statement = context.def_interner.statement( + hir_func.statements().get(hir_func.statements().len() - 2).ok_or(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function signature statement not found, invalid body length" + .to_string(), + ), + }, + file_id, + ))?, + ); + let function_selector_expression_id = match function_selector_statement { + HirStatement::Let(let_statement) => Ok(let_statement.expression), + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function selector statement must be an expression".to_string(), + ), + }, + file_id, + )), + }?; + let function_selector_expression = + context.def_interner.expression(&function_selector_expression_id); + + let current_fn_signature_expression_id = match function_selector_expression { + HirExpression::Call(call_expr) => Ok(call_expr.arguments[0]), + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function selector argument expression must be call expression" + .to_string(), + ), + }, + file_id, + )), + }?; + + let current_fn_signature_expression = + context.def_interner.expression(¤t_fn_signature_expression_id); + + match current_fn_signature_expression { + HirExpression::Literal(HirLiteral::Integer(value, _)) => { + if !value.is_zero() { + Err(( AztecMacroError::CouldNotGenerateContractInterface { secondary_message: Some( "Function signature argument must be a placeholder with value 0".to_string()), }, file_id, )) - } else { - Ok(()) + } else { + Ok(()) + } } - } - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "Function signature argument must be a literal field element" - .to_string(), - ), + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "Function signature argument must be a literal field element" + .to_string(), + ), + }, + file_id, + )), + }?; + + context.def_interner.update_expression( + current_fn_signature_expression_id, + |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer( + FieldElement::from(fn_signature_hash as u128), + false, + )) }, - file_id, - )), - }?; - - context.def_interner.update_expression( - current_fn_signature_expression_id, - |expr| { - *expr = HirExpression::Literal(HirLiteral::Integer( - FieldElement::from(fn_signature_hash as u128), - false, - )) - }, - ); + ); + } } } } diff --git a/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs index 3b3813da6ee..6b5db103c0b 100644 --- a/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs @@ -267,6 +267,9 @@ fn empty_expression(expression: &mut Expression) { ExpressionKind::Comptime(block_expression, _span) => { empty_block_expression(block_expression); } + ExpressionKind::Unsafe(block_expression, _span) => { + empty_block_expression(block_expression); + } ExpressionKind::Quote(..) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), ExpressionKind::AsTraitPath(path) => { empty_unresolved_type(&mut path.typ); @@ -325,7 +328,7 @@ fn empty_unresolved_type(unresolved_type: &mut UnresolvedType) { empty_unresolved_type(unresolved_type) } UnresolvedTypeData::Tuple(unresolved_types) => empty_unresolved_types(unresolved_types), - UnresolvedTypeData::Function(args, ret, _env) => { + UnresolvedTypeData::Function(args, ret, _env, _) => { empty_unresolved_types(args); empty_unresolved_type(ret); } diff --git a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs index 87181b285de..e2692349baa 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -107,7 +107,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::Forall(..) | Type::Quoted(_) | Type::Slice(_) - | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), + | Type::Function(_, _, _, _) => unreachable!("{typ} cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"), } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs index 24519d530ee..1aa0c2efbd0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -2,16 +2,20 @@ //! which the results are unused. use std::collections::HashSet; +use im::Vector; +use noirc_errors::Location; + use crate::ssa::{ ir::{ basic_block::{BasicBlock, BasicBlockId}, dfg::DataFlowGraph, function::Function, - instruction::{Instruction, InstructionId, Intrinsic}, + instruction::{BinaryOp, Instruction, InstructionId, Intrinsic}, post_order::PostOrder, + types::Type, value::{Value, ValueId}, }, - ssa_gen::Ssa, + ssa_gen::{Ssa, SSA_WORD_SIZE}, }; impl Ssa { @@ -20,7 +24,7 @@ impl Ssa { #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn dead_instruction_elimination(mut self) -> Ssa { for function in self.functions.values_mut() { - dead_instruction_elimination(function); + dead_instruction_elimination(function, true); } self } @@ -32,16 +36,29 @@ impl Ssa { /// instructions that reference results from an instruction in another block are evaluated first. /// If we did not iterate blocks in this order we could not safely say whether or not the results /// of its instructions are needed elsewhere. -fn dead_instruction_elimination(function: &mut Function) { +fn dead_instruction_elimination(function: &mut Function, insert_out_of_bounds_checks: bool) { let mut context = Context::default(); for call_data in &function.dfg.data_bus.call_data { context.mark_used_instruction_results(&function.dfg, call_data.array_id); } - let blocks = PostOrder::with_function(function); + let mut inserted_out_of_bounds_checks = false; + let blocks = PostOrder::with_function(function); for block in blocks.as_slice() { - context.remove_unused_instructions_in_block(function, *block); + inserted_out_of_bounds_checks |= context.remove_unused_instructions_in_block( + function, + *block, + insert_out_of_bounds_checks, + ); + } + + // If we inserted out of bounds check, let's run the pass again with those new + // instructions (we don't want to remove those checks, or instructions that are + // dependencies of those checks) + if inserted_out_of_bounds_checks { + dead_instruction_elimination(function, false); + return; } context.remove_rc_instructions(&mut function.dfg); @@ -71,20 +88,40 @@ impl Context { /// values set. This allows DIE to identify whole chains of unused instructions. (If the /// values referenced by an unused instruction were considered to be used, only the head of /// such chains would be removed.) + /// + /// If `insert_out_of_bounds_checks` is true and there are unused ArrayGet/ArraySet that + /// might be out of bounds, this method will insert out of bounds checks instead of + /// removing unused instructions and return `true`. The idea then is to later call this + /// function again with `insert_out_of_bounds_checks` set to false to effectively remove + /// unused instructions but leave the out of bounds checks. fn remove_unused_instructions_in_block( &mut self, function: &mut Function, block_id: BasicBlockId, - ) { + insert_out_of_bounds_checks: bool, + ) -> bool { let block = &function.dfg[block_id]; self.mark_terminator_values_as_used(function, block); - for instruction_id in block.instructions().iter().rev() { + let instructions_len = block.instructions().len(); + + // Indexes of instructions that might be out of bounds. + // We'll remove those, but before that we'll insert bounds checks for them. + let mut possible_index_out_of_bounds_indexes = Vec::new(); + + for (instruction_index, instruction_id) in block.instructions().iter().rev().enumerate() { + let instruction = &function.dfg[*instruction_id]; + if self.is_unused(*instruction_id, function) { self.instructions_to_remove.insert(*instruction_id); - } else { - let instruction = &function.dfg[*instruction_id]; + if insert_out_of_bounds_checks + && instruction_might_result_in_out_of_bounds(function, instruction) + { + possible_index_out_of_bounds_indexes + .push(instructions_len - instruction_index - 1); + } + } else { use Instruction::*; if matches!(instruction, IncrementRc { .. } | DecrementRc { .. }) { self.rc_instructions.push((*instruction_id, block_id)); @@ -96,9 +133,26 @@ impl Context { } } + // If there are some instructions that might trigger an out of bounds error, + // first add constrain checks. Then run the DIE pass again, which will remove those + // but leave the constrains (any any value needed by those constrains) + if !possible_index_out_of_bounds_indexes.is_empty() { + let inserted_check = self.replace_array_instructions_with_out_of_bounds_checks( + function, + block_id, + &mut possible_index_out_of_bounds_indexes, + ); + // There's a slight chance we didn't insert any checks, so we could proceed with DIE. + if inserted_check { + return true; + } + } + function.dfg[block_id] .instructions_mut() .retain(|instruction| !self.instructions_to_remove.contains(instruction)); + + false } /// Returns true if an instruction can be removed. @@ -166,6 +220,288 @@ impl Context { } } } + + /// Replaces unused ArrayGet/ArraySet instructions with out of bounds checks. + /// Returns `true` if at least one check was inserted. + /// Because some ArrayGet might happen in groups (for composite types), if just + /// some of the instructions in a group are used but not all of them, no check + /// is inserted, so this method might return `false`. + fn replace_array_instructions_with_out_of_bounds_checks( + &mut self, + function: &mut Function, + block_id: BasicBlockId, + possible_index_out_of_bounds_indexes: &mut Vec, + ) -> bool { + let mut inserted_check = false; + + // Keep track of the current side effects condition + let mut side_effects_condition = None; + + // Keep track of the next index we need to handle + let mut next_out_of_bounds_index = possible_index_out_of_bounds_indexes.pop(); + + let instructions = function.dfg[block_id].take_instructions(); + for (index, instruction_id) in instructions.iter().enumerate() { + let instruction_id = *instruction_id; + let instruction = &function.dfg[instruction_id]; + + if let Instruction::EnableSideEffects { condition } = instruction { + side_effects_condition = Some(*condition); + + // We still need to keep the EnableSideEffects instruction + function.dfg[block_id].instructions_mut().push(instruction_id); + continue; + }; + + // If it's an ArrayGet we'll deal with groups of it in case the array type is a composite type, + // and adjust `next_out_of_bounds_index` and `possible_index_out_of_bounds_indexes` accordingly + if let Instruction::ArrayGet { array, .. } = instruction { + handle_array_get_group( + function, + array, + index, + &mut next_out_of_bounds_index, + possible_index_out_of_bounds_indexes, + ); + } + + let Some(out_of_bounds_index) = next_out_of_bounds_index else { + // No more out of bounds instructions to insert, just push the current instruction + function.dfg[block_id].instructions_mut().push(instruction_id); + continue; + }; + + if index != out_of_bounds_index { + // This instruction is not out of bounds: let's just push it + function.dfg[block_id].instructions_mut().push(instruction_id); + continue; + } + + // This is an instruction that might be out of bounds: let's add a constrain. + let (array, index) = match instruction { + Instruction::ArrayGet { array, index } + | Instruction::ArraySet { array, index, .. } => (array, index), + _ => panic!("Expected an ArrayGet or ArraySet instruction here"), + }; + + let call_stack = function.dfg.get_call_stack(instruction_id); + + let (lhs, rhs) = if function.dfg.get_numeric_constant(*index).is_some() { + // If we are here it means the index is known but out of bounds. That's always an error! + let false_const = function.dfg.make_constant(false.into(), Type::bool()); + let true_const = function.dfg.make_constant(true.into(), Type::bool()); + (false_const, true_const) + } else { + // `index` will be relative to the flattened array length, so we need to take that into account + let array_length = function.dfg.type_of_value(*array).flattened_size(); + + // If we are here it means the index is dynamic, so let's add a check that it's less than length + let index = function.dfg.insert_instruction_and_results( + Instruction::Cast(*index, Type::unsigned(SSA_WORD_SIZE)), + block_id, + None, + call_stack.clone(), + ); + let index = index.first(); + + let array_typ = Type::unsigned(SSA_WORD_SIZE); + let array_length = + function.dfg.make_constant((array_length as u128).into(), array_typ); + let is_index_out_of_bounds = function.dfg.insert_instruction_and_results( + Instruction::binary(BinaryOp::Lt, index, array_length), + block_id, + None, + call_stack.clone(), + ); + let is_index_out_of_bounds = is_index_out_of_bounds.first(); + let true_const = function.dfg.make_constant(true.into(), Type::bool()); + (is_index_out_of_bounds, true_const) + }; + + let (lhs, rhs) = apply_side_effects( + side_effects_condition, + lhs, + rhs, + function, + block_id, + call_stack.clone(), + ); + + let message = Some("Index out of bounds".to_owned().into()); + function.dfg.insert_instruction_and_results( + Instruction::Constrain(lhs, rhs, message), + block_id, + None, + call_stack, + ); + inserted_check = true; + + next_out_of_bounds_index = possible_index_out_of_bounds_indexes.pop(); + } + + inserted_check + } +} + +fn instruction_might_result_in_out_of_bounds( + function: &Function, + instruction: &Instruction, +) -> bool { + use Instruction::*; + match instruction { + ArrayGet { array, index } | ArraySet { array, index, .. } => { + if function.dfg.try_get_array_length(*array).is_some() { + if let Some(known_index) = function.dfg.get_numeric_constant(*index) { + // `index` will be relative to the flattened array length, so we need to take that into account + let typ = function.dfg.type_of_value(*array); + let array_length = typ.flattened_size(); + known_index >= array_length.into() + } else { + // A dynamic index might always be out of bounds + true + } + } else { + // Slice operations might be out of bounds, but there's no way we + // can insert a check because we don't know a slice's length + false + } + } + _ => false, + } +} + +fn handle_array_get_group( + function: &Function, + array: &ValueId, + index: usize, + next_out_of_bounds_index: &mut Option, + possible_index_out_of_bounds_indexes: &mut Vec, +) { + let Some(array_length) = function.dfg.try_get_array_length(*array) else { + // Nothing to do for slices + return; + }; + + let flattened_size = function.dfg.type_of_value(*array).flattened_size(); + let element_size = flattened_size / array_length; + if element_size <= 1 { + // Not a composite type + return; + }; + + // It's a composite type. + // When doing ArrayGet on a composite type, this **always** results in instructions like these + // (assuming element_size == 3): + // + // 1. v27 = array_get v1, index v26 + // 2. v28 = add v26, u32 1 + // 3. v29 = array_get v1, index v28 + // 4. v30 = add v26, u32 2 + // 5. v31 = array_get v1, index v30 + // + // That means that after this instructions, (element_size - 1) instructions will be + // part of this composite array get, and they'll be two instructions apart. + // + // Now three things can happen: + // a) none of the array_get instructions are unused: in this case they won't be in + // `possible_index_out_of_bounds_indexes` and they won't be removed, nothing to do here + // b) all of the array_get instructions are unused: in this case we can replace **all** + // of them with just one constrain: no need to do one per array_get + // c) some of the array_get instructions are unused, but not all: in this case + // we don't need to insert any constrain, because on a later stage array bound checks + // will be performed anyway. We'll let DIE remove the unused ones, without replacing + // them with bounds checks, and leave the used ones. + // + // To check in which scenario we are we can get from `possible_index_out_of_bounds_indexes` + // (starting from `next_out_of_bounds_index`) while we are in the group ranges + // (1..=5 in the example above) + + let Some(out_of_bounds_index) = *next_out_of_bounds_index else { + // No next unused instruction, so this is case a) and nothing needs to be done here + return; + }; + + if index != out_of_bounds_index { + // The next index is not the one for the current instructions, + // so we are in case a), and nothing needs to be done here + return; + } + + // What's the last instruction that's part of the group? (5 in the example above) + let last_instruction_index = index + 2 * (element_size - 1); + // How many unused instructions are in this group? + let mut unused_count = 1; + loop { + *next_out_of_bounds_index = possible_index_out_of_bounds_indexes.pop(); + if let Some(out_of_bounds_index) = *next_out_of_bounds_index { + if out_of_bounds_index <= last_instruction_index { + unused_count += 1; + if unused_count == element_size { + // We are in case b): we need to insert just one constrain. + // Since we popped all of the group indexes, and given that we + // are analyzing the first instruction in the group, we can + // set `next_out_of_bounds_index` to the current index: + // then a check will be inserted, and no other check will be + // inserted for the rest of the group. + *next_out_of_bounds_index = Some(index); + break; + } else { + continue; + } + } + } + + // We are in case c): some of the instructions are unused. + // We don't need to insert any checks, and given that we already popped + // all of the indexes in the group, there's nothing else to do here. + break; + } +} + +// Given `lhs` and `rhs` values, if there's a side effects condition this will +// return (`lhs * condition`, `rhs * condition`), otherwise just (`lhs`, `rhs`) +fn apply_side_effects( + side_effects_condition: Option, + lhs: ValueId, + rhs: ValueId, + function: &mut Function, + block_id: BasicBlockId, + call_stack: Vector, +) -> (ValueId, ValueId) { + // See if there's an active "enable side effects" condition + let Some(condition) = side_effects_condition else { + return (lhs, rhs); + }; + + let dfg = &mut function.dfg; + + // Condition needs to be cast to argument type in order to multiply them together. + // In our case, lhs is always a boolean. + let casted_condition = dfg.insert_instruction_and_results( + Instruction::Cast(condition, Type::bool()), + block_id, + None, + call_stack.clone(), + ); + let casted_condition = casted_condition.first(); + + let lhs = dfg.insert_instruction_and_results( + Instruction::binary(BinaryOp::Mul, lhs, casted_condition), + block_id, + None, + call_stack.clone(), + ); + let lhs = lhs.first(); + + let rhs = dfg.insert_instruction_and_results( + Instruction::binary(BinaryOp::Mul, rhs, casted_condition), + block_id, + None, + call_stack, + ); + let rhs = rhs.first(); + + (lhs, rhs) } #[cfg(test)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index e16f6697c70..084584beab3 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -248,7 +248,7 @@ impl<'a> FunctionContext<'a> { } ast::Type::Unit => panic!("convert_non_tuple_type called on a unit type"), ast::Type::Tuple(_) => panic!("convert_non_tuple_type called on a tuple: {typ}"), - ast::Type::Function(_, _, _) => Type::Function, + ast::Type::Function(_, _, _, _) => Type::Function, ast::Type::Slice(_) => panic!("convert_non_tuple_type called on a slice: {typ}"), ast::Type::MutableReference(element) => { // Recursive call to panic if element is a tuple diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs index aab995c49a1..a3ce069b204 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -36,6 +36,7 @@ pub enum ExpressionKind { Quote(Tokens), Unquote(Box), Comptime(BlockExpression, Span), + Unsafe(BlockExpression, Span), AsTraitPath(AsTraitPath), // This variant is only emitted when inlining the result of comptime @@ -587,6 +588,7 @@ impl Display for ExpressionKind { Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), Comptime(block, _) => write!(f, "comptime {block}"), + Unsafe(block, _) => write!(f, "unsafe {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), Unquote(expr) => write!(f, "$({expr})"), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs index 8e27f0bdda9..7b5b64e7089 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -127,6 +127,7 @@ pub enum UnresolvedTypeData { /*args:*/ Vec, /*ret:*/ Box, /*env:*/ Box, + /*unconstrained:*/ bool, ), /// The type of quoted code for metaprogramming @@ -222,7 +223,11 @@ impl std::fmt::Display for UnresolvedTypeData { Bool => write!(f, "bool"), String(len) => write!(f, "str<{len}>"), FormatString(len, elements) => write!(f, "fmt<{len}, {elements}"), - Function(args, ret, env) => { + Function(args, ret, env, unconstrained) => { + if *unconstrained { + write!(f, "unconstrained ")?; + } + let args = vecmap(args, ToString::to_string).join(", "); match &env.as_ref().typ { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs index 5d9a97fa6cf..10ae7a1e62f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -100,7 +100,9 @@ impl StatementKind { StatementKind::Expression(expr) => { match (&expr.kind, semi, last_statement_in_block) { // Semicolons are optional for these expressions - (ExpressionKind::Block(_), semi, _) | (ExpressionKind::If(_), semi, _) => { + (ExpressionKind::Block(_), semi, _) + | (ExpressionKind::Unsafe(..), semi, _) + | (ExpressionKind::If(_), semi, _) => { if semi.is_some() { StatementKind::Semi(expr) } else { @@ -745,8 +747,10 @@ impl ForRange { let block = ExpressionKind::Block(BlockExpression { statements: vec![let_array, for_loop], }); - let kind = StatementKind::Expression(Expression::new(block, for_loop_span)); - Statement { kind, span: for_loop_span } + Statement { + kind: StatementKind::Expression(Expression::new(block, for_loop_span)), + span: for_loop_span, + } } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs index 598ffed1433..2856bb55276 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -493,7 +493,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)] @@ -502,7 +504,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_fn_enter)] @@ -511,7 +515,9 @@ pub fn build_debug_crate_file() -> String { __debug_fn_enter_oracle(fn_id); } pub fn __debug_fn_enter(fn_id: u32) { - __debug_fn_enter_inner(fn_id); + unsafe {{ + __debug_fn_enter_inner(fn_id); + }} } #[oracle(__debug_fn_exit)] @@ -520,7 +526,9 @@ pub fn build_debug_crate_file() -> String { __debug_fn_exit_oracle(fn_id); } pub fn __debug_fn_exit(fn_id: u32) { - __debug_fn_exit_inner(fn_id); + unsafe {{ + __debug_fn_exit_inner(fn_id); + }} } #[oracle(__debug_dereference_assign)] @@ -529,7 +537,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(), @@ -553,7 +563,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/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index 5ba448f890e..b31b8f36031 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -58,6 +58,9 @@ impl<'context> Elaborator<'context> { ExpressionKind::Comptime(comptime, _) => { return self.elaborate_comptime_block(comptime, expr.span) } + ExpressionKind::Unsafe(block_expression, _) => { + self.elaborate_unsafe_block(block_expression) + } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), ExpressionKind::Error => (HirExpression::Error, Type::Error), ExpressionKind::Unquote(_) => { @@ -105,6 +108,19 @@ impl<'context> Elaborator<'context> { (HirBlockExpression { statements }, block_type) } + fn elaborate_unsafe_block(&mut self, block: BlockExpression) -> (HirExpression, Type) { + // Before entering the block we cache the old value of `in_unsafe_block` so it can be restored. + let old_in_unsafe_block = self.in_unsafe_block; + self.in_unsafe_block = true; + + let (hir_block_expression, typ) = self.elaborate_block_expression(block); + + // Finally, we restore the original value of `self.in_unsafe_block`. + self.in_unsafe_block = old_in_unsafe_block; + + (HirExpression::Unsafe(hir_block_expression), typ) + } + fn elaborate_literal(&mut self, literal: Literal, span: Span) -> (HirExpression, Type) { use HirExpression::Literal as Lit; match literal { @@ -369,7 +385,8 @@ impl<'context> Elaborator<'context> { function_args.push((typ, arg, span)); } - let location = Location::new(span, self.file); + let call_span = Span::from(object_span.start()..method_name_span.end()); + let location = Location::new(call_span, self.file); let method = method_call.method_name; let turbofish_generics = generics.clone(); let is_macro_call = method_call.is_macro_call; @@ -710,7 +727,7 @@ impl<'context> Elaborator<'context> { let captures = lambda_context.captures; let expr = HirExpression::Lambda(HirLambda { parameters, return_type, body, captures }); - (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type))) + (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type), false)) } fn elaborate_quote(&mut self, mut tokens: Tokens) -> (HirExpression, Type) { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index e60308aaddd..da35ca77316 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -97,6 +97,7 @@ pub struct Elaborator<'context> { file: FileId, + in_unsafe_block: bool, nested_loops: usize, /// Contains a mapping of the current struct or functions's generics to @@ -194,6 +195,7 @@ impl<'context> Elaborator<'context> { interner, def_maps, file: FileId::dummy(), + in_unsafe_block: false, nested_loops: 0, generics: Vec::new(), lambda_stack: Vec::new(), @@ -802,7 +804,12 @@ impl<'context> Elaborator<'context> { let return_type = Box::new(self.resolve_type(func.return_type())); - let mut typ = Type::Function(parameter_types, return_type, Box::new(Type::Unit)); + let mut typ = Type::Function( + parameter_types, + return_type, + Box::new(Type::Unit), + func.def.is_unconstrained, + ); if !generics.is_empty() { typ = Type::Forall(generics, Box::new(typ)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index 1e48fdd07e7..52407746258 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -145,8 +145,9 @@ impl<'context> Elaborator<'context> { }; let no_environment = Box::new(Type::Unit); + // TODO: unconstrained let function_type = - Type::Function(arguments, Box::new(return_type), no_environment); + Type::Function(arguments, Box::new(return_type), no_environment, false); functions.push(TraitFunction { name: name.clone(), @@ -345,9 +346,15 @@ fn check_function_type_matches_expected_type( ) { let mut bindings = TypeBindings::new(); // Shouldn't need to unify envs, they should always be equal since they're both free functions - if let (Type::Function(params_a, ret_a, _env_a), Type::Function(params_b, ret_b, _env_b)) = - (expected, actual) + if let ( + Type::Function(params_a, ret_a, _env_a, _unconstrained_a), + Type::Function(params_b, ret_b, _env_b, _unconstrained_b), + ) = (expected, actual) { + // TODO: we don't yet allow marking a trait function or a trait impl function as unconstrained, + // so both values will always be false here. Once we support that, we should check that both + // match (adding a test for it). + if params_a.len() == params_b.len() { for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() { if a.try_unify(b, &mut bindings).is_err() { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index f8ba994f66b..58d019c86aa 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -125,7 +125,7 @@ impl<'context> Elaborator<'context> { Tuple(fields) => { Type::Tuple(vecmap(fields, |field| self.resolve_type_inner(field, kind))) } - Function(args, ret, env) => { + Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); let ret = Box::new(self.resolve_type_inner(*ret, kind)); @@ -139,7 +139,7 @@ impl<'context> Elaborator<'context> { match *env { Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _, _) => { - Type::Function(args, ret, env) + Type::Function(args, ret, env, unconstrained) } _ => { self.push_err(ResolverError::InvalidClosureEnvironment { @@ -735,8 +735,8 @@ impl<'context> Elaborator<'context> { return Type::Error; } - for (param, (arg, _, arg_span)) in fn_params.iter().zip(callsite_args) { - self.unify(arg, param, || TypeCheckError::TypeMismatch { + for (param, (arg, arg_expr_id, arg_span)) in fn_params.iter().zip(callsite_args) { + self.unify_with_coercions(arg, param, *arg_expr_id, || TypeCheckError::TypeMismatch { expected_typ: param.to_string(), expr_typ: arg.to_string(), expr_span: *arg_span, @@ -763,7 +763,8 @@ impl<'context> Elaborator<'context> { let ret = self.interner.next_type_variable(); let args = vecmap(args, |(arg, _, _)| arg); let env_type = self.interner.next_type_variable(); - let expected = Type::Function(args, Box::new(ret.clone()), Box::new(env_type)); + let expected = + Type::Function(args, Box::new(ret.clone()), Box::new(env_type), false); if let Err(error) = binding.try_bind(expected, span) { self.push_err(error); @@ -772,7 +773,7 @@ impl<'context> Elaborator<'context> { } // The closure env is ignored on purpose: call arguments never place // constraints on closure environments. - Type::Function(parameters, ret, _env) => { + Type::Function(parameters, ret, _env, _unconstrained) => { self.bind_function_type_impl(¶meters, &ret, &args, span) } Type::Error => Type::Error, @@ -1128,7 +1129,7 @@ impl<'context> Elaborator<'context> { let (method_type, mut bindings) = method.typ.clone().instantiate(self.interner); match method_type { - Type::Function(args, _, _) => { + Type::Function(args, _, _, _) => { // We can cheat a bit and match against only the object type here since no operator // overload uses other generic parameters or return types aside from the object type. let expected_object_type = &args[0]; @@ -1314,22 +1315,33 @@ impl<'context> Elaborator<'context> { let is_current_func_constrained = self.in_constrained_function(); - let is_unconstrained_call = self.is_unconstrained_call(call.func); + let func_type_is_unconstrained = + if let Type::Function(_args, _ret, _env, unconstrained) = &func_type { + *unconstrained + } else { + false + }; + + let is_unconstrained_call = + func_type_is_unconstrained || self.is_unconstrained_call(call.func); let crossing_runtime_boundary = is_current_func_constrained && is_unconstrained_call; if crossing_runtime_boundary { - let called_func_id = self - .interner - .lookup_function_from_expr(&call.func) - .expect("Called function should exist"); - self.run_lint(|elaborator| { - lints::oracle_called_from_constrained_function( - elaborator.interner, - &called_func_id, - is_current_func_constrained, - span, - ) - .map(Into::into) - }); + if !self.in_unsafe_block { + self.push_err(TypeCheckError::Unsafe { span }); + } + + if let Some(called_func_id) = self.interner.lookup_function_from_expr(&call.func) { + self.run_lint(|elaborator| { + lints::oracle_called_from_constrained_function( + elaborator.interner, + &called_func_id, + is_current_func_constrained, + span, + ) + .map(Into::into) + }); + } + let errors = lints::unconstrained_function_args(&args); for error in errors { self.push_err(error); @@ -1373,9 +1385,9 @@ impl<'context> Elaborator<'context> { object: &mut ExprId, ) { let expected_object_type = match function_type { - Type::Function(args, _, _) => args.first(), + Type::Function(args, _, _, _) => args.first(), Type::Forall(_, typ) => match typ.as_ref() { - Type::Function(args, _, _) => args.first(), + Type::Function(args, _, _, _) => args.first(), typ => unreachable!("Unexpected type for function: {typ}"), }, typ => unreachable!("Unexpected type for function: {typ}"), @@ -1577,7 +1589,7 @@ impl<'context> Elaborator<'context> { } } - Type::Function(parameters, return_type, _env) => { + Type::Function(parameters, return_type, _env, _unconstrained) => { for parameter in parameters { Self::find_numeric_generics_in_type(parameter, found); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index bc48b2875c8..853697220b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -202,6 +202,9 @@ impl HirExpression { HirExpression::Comptime(block) => { ExpressionKind::Comptime(block.to_display_ast(interner), span) } + HirExpression::Unsafe(block) => { + ExpressionKind::Unsafe(block.to_display_ast(interner), span) + } HirExpression::Quote(block) => ExpressionKind::Quote(block.clone()), // A macro was evaluated here: return the quoted result @@ -340,11 +343,11 @@ impl Type { let name = Path::from_single(name.as_ref().clone(), Span::default()); UnresolvedTypeData::TraitAsType(name, Vec::new()) } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| arg.to_display_ast()); let ret = Box::new(ret.to_display_ast()); let env = Box::new(env.to_display_ast()); - UnresolvedTypeData::Function(args, ret, env) + UnresolvedTypeData::Function(args, ret, env, *unconstrained) } Type::MutableReference(element) => { let element = Box::new(element.to_display_ast()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 72b92e288c7..093dc370eb6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -472,6 +472,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), HirExpression::Quote(tokens) => self.evaluate_quote(tokens, id), HirExpression::Comptime(block) => self.evaluate_block(block), + HirExpression::Unsafe(block) => self.evaluate_block(block), HirExpression::Unquote(tokens) => { // An Unquote expression being found is indicative of a macro being // expanded within another comptime fn which we don't currently support. diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index ef7b9f2be55..5698cc8d3bd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -7,8 +7,8 @@ use acvm::{AcirField, FieldElement}; use builtin_helpers::{ check_argument_count, check_function_not_yet_resolved, check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_function_def, get_module, get_quoted, - get_slice, get_struct, get_trait_constraint, get_trait_def, get_tuple, get_type, get_u32, - hir_pattern_to_tokens, mutate_func_meta_type, parse, parse_tokens, + get_slice, get_struct, get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, + get_type, get_u32, hir_pattern_to_tokens, mutate_func_meta_type, parse, parse_tokens, replace_func_meta_parameters, replace_func_meta_return_type, }; use iter_extended::{try_vecmap, vecmap}; @@ -23,7 +23,7 @@ use crate::{ hir::comptime::{errors::IResult, value::add_token_spans, InterpreterError, Value}, hir_def::function::FunctionBody, macros_api::{ModuleDefId, NodeInterner, Signedness}, - node_interner::DefinitionKind, + node_interner::{DefinitionKind, TraitImplKind}, parser::{self}, token::{SpannedToken, Token}, QuotedType, Shared, Type, @@ -86,6 +86,10 @@ impl<'local, 'context> Interpreter<'local, 'context> { } "trait_def_eq" => trait_def_eq(interner, arguments, location), "trait_def_hash" => trait_def_hash(interner, arguments, location), + "trait_impl_methods" => trait_impl_methods(interner, arguments, location), + "trait_impl_trait_generic_args" => { + trait_impl_trait_generic_args(interner, arguments, location) + } "type_as_array" => type_as_array(arguments, return_type, location), "type_as_constant" => type_as_constant(arguments, return_type, location), "type_as_integer" => type_as_integer(arguments, return_type, location), @@ -93,6 +97,9 @@ impl<'local, 'context> Interpreter<'local, 'context> { "type_as_struct" => type_as_struct(arguments, return_type, location), "type_as_tuple" => type_as_tuple(arguments, return_type, location), "type_eq" => type_eq(arguments, location), + "type_get_trait_impl" => { + type_get_trait_impl(interner, arguments, return_type, location) + } "type_implements" => type_implements(interner, arguments, location), "type_is_bool" => type_is_bool(arguments, location), "type_is_field" => type_is_field(arguments, location), @@ -507,6 +514,26 @@ fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult Option +fn type_get_trait_impl( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let (typ, constraint) = check_two_arguments(arguments, location)?; + + let typ = get_type(typ)?; + let (trait_id, generics) = get_trait_constraint(constraint)?; + + let option_value = match interner.try_lookup_trait_implementation(&typ, trait_id, &generics) { + Ok((TraitImplKind::Normal(trait_impl_id), _)) => Some(Value::TraitImpl(trait_impl_id)), + _ => None, + }; + + option(return_type, option_value) +} + // fn implements(self, constraint: TraitConstraint) -> bool fn type_implements( interner: &NodeInterner, @@ -607,6 +634,41 @@ fn trait_def_eq( Ok(Value::Bool(id_a == id_b)) } +// fn methods(self) -> [FunctionDefinition] +fn trait_impl_methods( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let argument = check_one_argument(arguments, location)?; + + let trait_impl_id = get_trait_impl(argument)?; + let trait_impl = interner.get_trait_implementation(trait_impl_id); + let trait_impl = trait_impl.borrow(); + let methods = + trait_impl.methods.iter().map(|func_id| Value::FunctionDefinition(*func_id)).collect(); + let slice_type = Type::Slice(Box::new(Type::Quoted(QuotedType::FunctionDefinition))); + + Ok(Value::Slice(methods, slice_type)) +} + +// fn trait_generic_args(self) -> [Type] +fn trait_impl_trait_generic_args( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let argument = check_one_argument(arguments, location)?; + + let trait_impl_id = get_trait_impl(argument)?; + let trait_impl = interner.get_trait_implementation(trait_impl_id); + let trait_impl = trait_impl.borrow(); + let trait_generics = trait_impl.trait_generics.iter().map(|t| Value::Type(t.clone())).collect(); + let slice_type = Type::Slice(Box::new(Type::Quoted(QuotedType::Type))); + + Ok(Value::Slice(trait_generics, slice_type)) +} + // fn zeroed() -> T fn zeroed(return_type: Type) -> IResult { match return_type { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 56f6c11974f..8d2f55b9c20 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -14,7 +14,7 @@ use crate::{ stmt::HirPattern, }, macros_api::{NodeInterner, StructId}, - node_interner::{FuncId, TraitId}, + node_interner::{FuncId, TraitId, TraitImplId}, parser::NoirParser, token::{Token, Tokens}, QuotedType, Type, @@ -77,8 +77,7 @@ pub(crate) fn get_array( value => { let type_var = Box::new(interner.next_type_variable()); let expected = Type::Array(type_var.clone(), type_var); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) + type_mismatch(value, expected, location) } } } @@ -92,8 +91,7 @@ pub(crate) fn get_slice( value => { let type_var = Box::new(interner.next_type_variable()); let expected = Type::Slice(type_var); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) + type_mismatch(value, expected, location) } } } @@ -107,8 +105,7 @@ pub(crate) fn get_tuple( value => { let type_var = interner.next_type_variable(); let expected = Type::Tuple(vec![type_var]); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) + type_mismatch(value, expected, location) } } } @@ -116,10 +113,7 @@ pub(crate) fn get_tuple( pub(crate) fn get_field((value, location): (Value, Location)) -> IResult { match value { Value::Field(value) => Ok(value), - value => { - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected: Type::FieldElement, actual, location }) - } + value => type_mismatch(value, Type::FieldElement, location), } } @@ -128,8 +122,7 @@ pub(crate) fn get_u8((value, location): (Value, Location)) -> IResult { Value::U8(value) => Ok(value), value => { let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) + type_mismatch(value, expected, location) } } } @@ -139,8 +132,7 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { Value::U32(value) => Ok(value), value => { let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) + type_mismatch(value, expected, location) } } } @@ -148,44 +140,28 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult { match value { Value::Expr(expr) => Ok(expr), - value => { - let expected = Type::Quoted(QuotedType::Expr); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::Expr), location), } } pub(crate) fn get_function_def((value, location): (Value, Location)) -> IResult { match value { Value::FunctionDefinition(id) => Ok(id), - value => { - let expected = Type::Quoted(QuotedType::FunctionDefinition); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::FunctionDefinition), location), } } pub(crate) fn get_module((value, location): (Value, Location)) -> IResult { match value { Value::ModuleDefinition(module_id) => Ok(module_id), - value => { - let expected = Type::Quoted(QuotedType::Module); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::Module), location), } } pub(crate) fn get_struct((value, location): (Value, Location)) -> IResult { match value { Value::StructDefinition(id) => Ok(id), - _ => { - let expected = Type::Quoted(QuotedType::StructDefinition); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, location, actual }) - } + _ => type_mismatch(value, Type::Quoted(QuotedType::StructDefinition), location), } } @@ -194,47 +170,43 @@ pub(crate) fn get_trait_constraint( ) -> IResult<(TraitId, Vec)> { match value { Value::TraitConstraint(trait_id, generics) => Ok((trait_id, generics)), - value => { - let expected = Type::Quoted(QuotedType::TraitConstraint); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::TraitConstraint), location), } } pub(crate) fn get_trait_def((value, location): (Value, Location)) -> IResult { match value { Value::TraitDefinition(id) => Ok(id), - value => { - let expected = Type::Quoted(QuotedType::TraitDefinition); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::TraitDefinition), location), + } +} + +pub(crate) fn get_trait_impl((value, location): (Value, Location)) -> IResult { + match value { + Value::TraitImpl(id) => Ok(id), + value => type_mismatch(value, Type::Quoted(QuotedType::TraitImpl), location), } } pub(crate) fn get_type((value, location): (Value, Location)) -> IResult { match value { Value::Type(typ) => Ok(typ), - value => { - let expected = Type::Quoted(QuotedType::Type); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::Type), location), } } pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult>> { match value { Value::Quoted(tokens) => Ok(tokens), - value => { - let expected = Type::Quoted(QuotedType::Quoted); - let actual = value.get_type().into_owned(); - Err(InterpreterError::TypeMismatch { expected, actual, location }) - } + value => type_mismatch(value, Type::Quoted(QuotedType::Quoted), location), } } +fn type_mismatch(value: Value, expected: Type, location: Location) -> IResult { + let actual = value.get_type().into_owned(); + Err(InterpreterError::TypeMismatch { expected, actual, location }) +} + pub(crate) fn hir_pattern_to_tokens( interner: &NodeInterner, hir_pattern: &HirPattern, @@ -357,7 +329,7 @@ where pub(super) fn replace_func_meta_parameters(typ: &mut Type, parameter_types: Vec) { match typ { - Type::Function(parameters, _, _) => { + Type::Function(parameters, _, _, _) => { *parameters = parameter_types; } Type::Forall(_, typ) => replace_func_meta_parameters(typ, parameter_types), @@ -367,7 +339,7 @@ pub(super) fn replace_func_meta_parameters(typ: &mut Type, parameter_types: Vec< pub(super) fn replace_func_meta_return_type(typ: &mut Type, return_type: Type) { match typ { - Type::Function(_, ret, _) => { + Type::Function(_, ret, _, _) => { *ret = Box::new(return_type); } Type::Forall(_, typ) => replace_func_meta_return_type(typ, return_type), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs index d5408309e55..d65c2bb7dcc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -9,12 +9,15 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness}, hir::def_map::ModuleId, - hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, + hir_def::{ + expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, + traits::TraitConstraint, + }, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, - node_interner::{ExprId, FuncId, TraitId}, + node_interner::{ExprId, FuncId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, TypeBindings, @@ -53,6 +56,7 @@ pub enum Value { StructDefinition(StructId), TraitConstraint(TraitId, /* trait generics */ Vec), TraitDefinition(TraitId), + TraitImpl(TraitImplId), FunctionDefinition(FuncId), ModuleDefinition(ModuleId), Type(Type), @@ -100,6 +104,7 @@ impl Value { } Value::TraitConstraint { .. } => Type::Quoted(QuotedType::TraitConstraint), Value::TraitDefinition(_) => Type::Quoted(QuotedType::TraitDefinition), + Value::TraitImpl(_) => Type::Quoted(QuotedType::TraitImpl), Value::FunctionDefinition(_) => Type::Quoted(QuotedType::FunctionDefinition), Value::ModuleDefinition(_) => Type::Quoted(QuotedType::Module), Value::Type(_) => Type::Quoted(QuotedType::Type), @@ -230,6 +235,7 @@ impl Value { | Value::StructDefinition(_) | Value::TraitConstraint(..) | Value::TraitDefinition(_) + | Value::TraitImpl(_) | Value::FunctionDefinition(_) | Value::Zeroed(_) | Value::Type(_) @@ -353,6 +359,7 @@ impl Value { | Value::StructDefinition(_) | Value::TraitConstraint(..) | Value::TraitDefinition(_) + | Value::TraitImpl(_) | Value::FunctionDefinition(_) | Value::Zeroed(_) | Value::Type(_) @@ -516,18 +523,40 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { write!(f, "{}", def.name) } Value::TraitConstraint(trait_id, generics) => { - let trait_ = self.interner.get_trait(*trait_id); - let generic_string = vecmap(generics, ToString::to_string).join(", "); - if generics.is_empty() { - write!(f, "{}", trait_.name) - } else { - write!(f, "{}<{generic_string}>", trait_.name) - } + write!(f, "{}", display_trait_id_and_generics(self.interner, trait_id, generics)) } Value::TraitDefinition(trait_id) => { let trait_ = self.interner.get_trait(*trait_id); write!(f, "{}", trait_.name) } + Value::TraitImpl(trait_impl_id) => { + let trait_impl = self.interner.get_trait_implementation(*trait_impl_id); + let trait_impl = trait_impl.borrow(); + + let generic_string = + vecmap(&trait_impl.trait_generics, ToString::to_string).join(", "); + let generic_string = if generic_string.is_empty() { + generic_string + } else { + format!("<{}>", generic_string) + }; + + let where_clause = vecmap(&trait_impl.where_clause, |trait_constraint| { + display_trait_constraint(self.interner, trait_constraint) + }); + let where_clause = where_clause.join(", "); + let where_clause = if where_clause.is_empty() { + where_clause + } else { + format!(" where {}", where_clause) + }; + + write!( + f, + "impl {}{} for {}{}", + trait_impl.ident, generic_string, trait_impl.typ, where_clause + ) + } Value::FunctionDefinition(function_id) => { write!(f, "{}", self.interner.function_name(function_id)) } @@ -538,3 +567,26 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { } } } + +fn display_trait_id_and_generics( + interner: &NodeInterner, + trait_id: &TraitId, + generics: &Vec, +) -> String { + let trait_ = interner.get_trait(*trait_id); + let generic_string = vecmap(generics, ToString::to_string).join(", "); + if generics.is_empty() { + format!("{}", trait_.name) + } else { + format!("{}<{generic_string}>", trait_.name) + } +} + +fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitConstraint) -> String { + let trait_constraint_string = display_trait_id_and_generics( + interner, + &trait_constraint.trait_id, + &trait_constraint.trait_generics, + ); + format!("{}: {}", trait_constraint.typ, trait_constraint_string) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs index 8eba8215f84..380753d8198 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -136,6 +136,8 @@ pub enum TypeCheckError { UnconstrainedReferenceToConstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, + #[error("Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block")] + Unsafe { span: Span }, #[error("Slices must have constant length")] NonConstantSliceLength { span: Span }, #[error("Only sized types may be used in the entry point to a program")] @@ -356,6 +358,9 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { let msg = "turbofish (`::<_>`) usage at this position isn't supported yet"; Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) }, + TypeCheckError::Unsafe { span } => { + Diagnostic::simple_warning(error.to_string(), String::new(), *span) + } } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs index e85d30f0c32..8137e74da30 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs @@ -37,6 +37,7 @@ pub enum HirExpression { Quote(Tokens), Unquote(Tokens), Comptime(HirBlockExpression), + Unsafe(HirBlockExpression), Error, } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs index 6b66cf1ab4a..29b0cb7b8af 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs @@ -194,9 +194,9 @@ impl FuncMeta { /// Gives the (uninstantiated) return type of this function. pub fn return_type(&self) -> &Type { match &self.typ { - Type::Function(_, ret, _env) => ret, + Type::Function(_, ret, _env, _unconstrained) => ret, Type::Forall(_, typ) => match typ.as_ref() { - Type::Function(_, ret, _env) => ret, + Type::Function(_, ret, _env, _unconstrained) => ret, _ => unreachable!(), }, _ => unreachable!(), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs index 099c9ea78f7..df6ccc0492c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/traits.rs @@ -142,9 +142,9 @@ impl std::fmt::Display for Trait { impl TraitFunction { pub fn arguments(&self) -> &[Type] { match &self.typ { - Type::Function(args, _, _) => args, + Type::Function(args, _, _, _) => args, Type::Forall(_, typ) => match typ.as_ref() { - Type::Function(args, _, _) => args, + Type::Function(args, _, _, _) => args, _ => unreachable!("Trait function does not have a function type"), }, _ => unreachable!("Trait function does not have a function type"), @@ -161,9 +161,9 @@ impl TraitFunction { pub fn return_type(&self) -> &Type { match &self.typ { - Type::Function(_, return_type, _) => return_type, + Type::Function(_, return_type, _, _) => return_type, Type::Forall(_, typ) => match typ.as_ref() { - Type::Function(_, return_type, _) => return_type, + Type::Function(_, return_type, _, _) => return_type, _ => unreachable!("Trait function does not have a function type"), }, _ => unreachable!("Trait function does not have a function type"), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 177d23c74dd..d2f499950f1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -88,7 +88,12 @@ pub enum Type { /// the environment should be `Unit` by default, /// for closures it should contain a `Tuple` type with the captured /// variable types. - Function(Vec, /*return_type:*/ Box, /*environment:*/ Box), + Function( + Vec, + /*return_type:*/ Box, + /*environment:*/ Box, + /*unconstrained*/ bool, + ), /// &mut T MutableReference(Box), @@ -146,6 +151,7 @@ pub enum QuotedType { StructDefinition, TraitConstraint, TraitDefinition, + TraitImpl, FunctionDefinition, Module, } @@ -662,15 +668,19 @@ impl std::fmt::Display for Type { let typevars = vecmap(typevars, |var| var.id().to_string()); write!(f, "forall {}. {}", typevars.join(" "), typ) } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, unconstrained) => { + if *unconstrained { + write!(f, "unconstrained ")?; + } + let closure_env_text = match **env { Type::Unit => "".to_string(), - _ => format!(" with env {env}"), + _ => format!("[{env}]"), }; let args = vecmap(args.iter(), ToString::to_string); - write!(f, "fn({}) -> {ret}{closure_env_text}", args.join(", ")) + write!(f, "fn{closure_env_text}({}) -> {ret}", args.join(", ")) } Type::MutableReference(element) => { write!(f, "&mut {element}") @@ -727,6 +737,7 @@ impl std::fmt::Display for QuotedType { QuotedType::StructDefinition => write!(f, "StructDefinition"), QuotedType::TraitDefinition => write!(f, "TraitDefinition"), QuotedType::TraitConstraint => write!(f, "TraitConstraint"), + QuotedType::TraitImpl => write!(f, "TraitImpl"), QuotedType::FunctionDefinition => write!(f, "FunctionDefinition"), QuotedType::Module => write!(f, "Module"), } @@ -853,7 +864,7 @@ impl Type { Type::Tuple(fields) => { fields.iter().any(|field| field.contains_numeric_typevar(target_id)) } - Type::Function(parameters, return_type, env) => { + Type::Function(parameters, return_type, env, _unconstrained) => { parameters.iter().any(|parameter| parameter.contains_numeric_typevar(target_id)) || return_type.contains_numeric_typevar(target_id) || env.contains_numeric_typevar(target_id) @@ -933,7 +944,7 @@ impl Type { field.find_numeric_type_vars(found_names); } } - Type::Function(parameters, return_type, env) => { + Type::Function(parameters, return_type, env, _unconstrained) => { for parameter in parameters.iter() { parameter.find_numeric_type_vars(found_names); } @@ -990,7 +1001,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) | Type::Quoted(_) @@ -1039,7 +1050,7 @@ impl Type { Type::FmtString(_, _) // To enable this we would need to determine the size of the closure outputs at compile-time. // This is possible as long as the output size is not dependent upon a witness condition. - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::Slice(_) | Type::MutableReference(_) | Type::Forall(_, _) @@ -1077,7 +1088,7 @@ impl Type { | Type::Slice(_) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::FmtString(_, _) | Type::InfixExpr(..) | Type::Error => true, @@ -1208,7 +1219,7 @@ impl Type { | Type::TypeVariable(_, _) | Type::TraitAsType(..) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) @@ -1576,8 +1587,11 @@ impl Type { } } - (Function(params_a, ret_a, env_a), Function(params_b, ret_b, env_b)) => { - if params_a.len() == params_b.len() { + ( + Function(params_a, ret_a, env_a, unconstrained_a), + Function(params_b, ret_b, env_b, unconstrained_b), + ) => { + if unconstrained_a == unconstrained_b && params_a.len() == params_b.len() { for (a, b) in params_a.iter().zip(params_b.iter()) { a.try_unify(b, bindings)?; } @@ -1768,12 +1782,38 @@ impl Type { ) { let mut bindings = TypeBindings::new(); - if let Err(UnificationError) = self.try_unify(expected, &mut bindings) { - if !self.try_array_to_slice_coercion(expected, expression, interner) { - errors.push(make_error()); - } - } else { + if let Ok(()) = self.try_unify(expected, &mut bindings) { Type::apply_type_bindings(bindings); + return; + } + + if self.try_array_to_slice_coercion(expected, expression, interner) { + return; + } + + // Try to coerce `fn (..) -> T` to `unconstrained fn (..) -> T` + if let Some(coerced_self) = self.try_fn_to_unconstrained_fn_coercion(expected) { + coerced_self.unify_with_coercions(expected, expression, interner, errors, make_error); + return; + } + + errors.push(make_error()); + } + + // If `self` and `expected` are function types, tries to coerce `self` to `expected`. + // Returns None if no coercion can be applied, otherwise returns `self` coerced to `expected`. + fn try_fn_to_unconstrained_fn_coercion(&self, expected: &Type) -> Option { + // If `self` and `expected` are function types, `self` can be coerced to `expected` + // if `self` is unconstrained and `expected` is not. The other way around is an error, though. + if let ( + Type::Function(params, ret, env, unconstrained_self), + Type::Function(_, _, _, unconstrained_expected), + ) = (self.follow_bindings(), expected.follow_bindings()) + { + (!unconstrained_self && unconstrained_expected) + .then(|| Type::Function(params, ret, env, unconstrained_expected)) + } else { + None } } @@ -1789,13 +1829,17 @@ impl Type { let target = target.follow_bindings(); if let (Type::Array(_size, element1), Type::Slice(element2)) = (&this, &target) { - // Still have to ensure the element types match. - // Don't need to issue an error here if not, it will be done in unify_with_coercions - let mut bindings = TypeBindings::new(); - if element1.try_unify(element2, &mut bindings).is_ok() { - convert_array_expression_to_slice(expression, this, target, interner); - Self::apply_type_bindings(bindings); - return true; + // We can only do the coercion if the `as_slice` method exists. + // This is usually true, but some tests don't have access to the standard library. + if let Some(as_slice) = interner.lookup_primitive_method(&this, "as_slice") { + // Still have to ensure the element types match. + // Don't need to issue an error here if not, it will be done in unify_with_coercions + let mut bindings = TypeBindings::new(); + if element1.try_unify(element2, &mut bindings).is_ok() { + convert_array_expression_to_slice(expression, this, target, as_slice, interner); + Self::apply_type_bindings(bindings); + return true; + } } } false @@ -2070,13 +2114,13 @@ impl Type { let typ = Box::new(typ.substitute_helper(type_bindings, substitute_bound_typevars)); Type::Forall(typevars.clone(), typ) } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| { arg.substitute_helper(type_bindings, substitute_bound_typevars) }); let ret = Box::new(ret.substitute_helper(type_bindings, substitute_bound_typevars)); let env = Box::new(env.substitute_helper(type_bindings, substitute_bound_typevars)); - Type::Function(args, ret, env) + Type::Function(args, ret, env, *unconstrained) } Type::MutableReference(element) => Type::MutableReference(Box::new( element.substitute_helper(type_bindings, substitute_bound_typevars), @@ -2132,7 +2176,7 @@ impl Type { Type::Forall(typevars, typ) => { !typevars.iter().any(|var| var.id() == target_id) && typ.occurs(target_id) } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, _unconstrained) => { args.iter().any(|arg| arg.occurs(target_id)) || ret.occurs(target_id) || env.occurs(target_id) @@ -2186,11 +2230,11 @@ impl Type { self.clone() } - Function(args, ret, env) => { + Function(args, ret, env, unconstrained) => { let args = vecmap(args, |arg| arg.follow_bindings()); let ret = Box::new(ret.follow_bindings()); let env = Box::new(env.follow_bindings()); - Function(args, ret, env) + Function(args, ret, env, *unconstrained) } MutableReference(element) => MutableReference(Box::new(element.follow_bindings())), @@ -2282,7 +2326,7 @@ impl Type { *self = Type::TypeVariable(var.clone(), TypeVariableKind::Normal); } } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, _unconstrained) => { for arg in args { arg.replace_named_generics_with_type_variables(); } @@ -2311,12 +2355,9 @@ fn convert_array_expression_to_slice( expression: ExprId, array_type: Type, target_type: Type, + as_slice_method: crate::node_interner::FuncId, interner: &mut NodeInterner, ) { - let as_slice_method = interner - .lookup_primitive_method(&array_type, "as_slice") - .expect("Expected 'as_slice' method to be present in Noir's stdlib"); - let as_slice_id = interner.function_definition_id(as_slice_method); let location = interner.expr_location(&expression); let as_slice = HirExpression::Ident(HirIdent::non_trait_method(as_slice_id, location), None); @@ -2337,7 +2378,8 @@ fn convert_array_expression_to_slice( interner.push_expr_location(func, location.span, location.file); interner.push_expr_type(expression, target_type.clone()); - let func_type = Type::Function(vec![array_type], Box::new(target_type), Box::new(Type::Unit)); + let func_type = + Type::Function(vec![array_type], Box::new(target_type), Box::new(Type::Unit), false); interner.push_expr_type(func, func_type); } @@ -2429,10 +2471,11 @@ impl From<&Type> for PrintableType { Type::TypeVariable(_, _) => unreachable!(), Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), - Type::Function(arguments, return_type, env) => PrintableType::Function { + Type::Function(arguments, return_type, env, unconstrained) => PrintableType::Function { arguments: arguments.iter().map(|arg| arg.into()).collect(), return_type: Box::new(return_type.as_ref().into()), env: Box::new(env.as_ref().into()), + unconstrained: *unconstrained, }, Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } @@ -2517,7 +2560,11 @@ impl std::fmt::Debug for Type { let typevars = vecmap(typevars, |var| format!("{:?}", var)); write!(f, "forall {}. {:?}", typevars.join(" "), typ) } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, unconstrained) => { + if *unconstrained { + write!(f, "unconstrained ")?; + } + let closure_env_text = match **env { Type::Unit => "".to_string(), _ => format!(" with env {env:?}"), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index 4222d2b585f..b9b4bdef9aa 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -925,10 +925,12 @@ pub enum Keyword { Trait, TraitConstraint, TraitDefinition, + TraitImpl, Type, TypeType, Unchecked, Unconstrained, + Unsafe, Use, Where, While, @@ -977,10 +979,12 @@ impl fmt::Display for Keyword { Keyword::Trait => write!(f, "trait"), Keyword::TraitConstraint => write!(f, "TraitConstraint"), Keyword::TraitDefinition => write!(f, "TraitDefinition"), + Keyword::TraitImpl => write!(f, "TraitImpl"), Keyword::Type => write!(f, "type"), Keyword::TypeType => write!(f, "Type"), Keyword::Unchecked => write!(f, "unchecked"), 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"), @@ -1031,11 +1035,13 @@ impl Keyword { "trait" => Keyword::Trait, "TraitConstraint" => Keyword::TraitConstraint, "TraitDefinition" => Keyword::TraitDefinition, + "TraitImpl" => Keyword::TraitImpl, "type" => Keyword::Type, "Type" => Keyword::TypeType, "StructDefinition" => Keyword::StructDefinition, "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, + "unsafe" => Keyword::Unsafe, "use" => Keyword::Use, "where" => Keyword::Where, "while" => Keyword::While, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs index f2ed9433e61..f7bcfd58b72 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -288,7 +288,12 @@ pub enum Type { Tuple(Vec), Slice(Box), MutableReference(Box), - Function(/*args:*/ Vec, /*ret:*/ Box, /*env:*/ Box), + Function( + /*args:*/ Vec, + /*ret:*/ Box, + /*env:*/ Box, + /*unconstrained:*/ bool, + ), } impl Type { @@ -419,7 +424,11 @@ impl std::fmt::Display for Type { let elements = vecmap(elements, ToString::to_string); write!(f, "({})", elements.join(", ")) } - Type::Function(args, ret, env) => { + Type::Function(args, ret, env, unconstrained) => { + if *unconstrained { + write!(f, "unconstrained ")?; + } + let args = vecmap(args, ToString::to_string); let closure_env_text = match **env { Type::Unit => "".to_string(), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index 5ac730db400..510b81d9acb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -442,6 +442,7 @@ impl<'interner> Monomorphizer<'interner> { }, HirExpression::Literal(HirLiteral::Unit) => ast::Expression::Block(vec![]), HirExpression::Block(block) => self.block(block.statements)?, + HirExpression::Unsafe(block) => self.block(block.statements)?, HirExpression::Prefix(prefix) => { let rhs = self.expr(prefix.rhs)?; @@ -1003,15 +1004,17 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Tuple(fields) } - HirType::Function(args, ret, env) => { + HirType::Function(args, ret, env, unconstrained) => { let args = try_vecmap(args, |x| Self::convert_type(x, location))?; let ret = Box::new(Self::convert_type(ret, location)?); let env = Self::convert_type(env, location)?; match &env { - ast::Type::Unit => ast::Type::Function(args, ret, Box::new(env)), + ast::Type::Unit => { + ast::Type::Function(args, ret, Box::new(env), *unconstrained) + } ast::Type::Tuple(_elements) => ast::Type::Tuple(vec![ env.clone(), - ast::Type::Function(args, ret, Box::new(env)), + ast::Type::Function(args, ret, Box::new(env), *unconstrained), ]), _ => { unreachable!( @@ -1100,7 +1103,7 @@ impl<'interner> Monomorphizer<'interner> { Ok(()) } - HirType::Function(args, ret, env) => { + HirType::Function(args, ret, env, _) => { for arg in args { Self::check_type(arg, location)?; } @@ -1122,7 +1125,7 @@ impl<'interner> Monomorphizer<'interner> { true } else if let ast::Type::Tuple(elements) = t { if elements.len() == 2 { - matches!(elements[1], ast::Type::Function(_, _, _)) + matches!(elements[1], ast::Type::Function(_, _, _, _)) } else { false } @@ -1132,7 +1135,7 @@ impl<'interner> Monomorphizer<'interner> { } fn is_function_closure_type(&self, t: &ast::Type) -> bool { - if let ast::Type::Function(_, _, env) = t { + if let ast::Type::Function(_, _, env, _) = t { let e = (*env).clone(); matches!(*e, ast::Type::Tuple(_captures)) } else { @@ -1499,8 +1502,12 @@ impl<'interner> Monomorphizer<'interner> { }; self.push_function(id, function); - let typ = - ast::Type::Function(parameter_types, Box::new(ret_type), Box::new(ast::Type::Unit)); + let typ = ast::Type::Function( + parameter_types, + Box::new(ret_type), + Box::new(ast::Type::Unit), + false, + ); let name = lambda_name.to_owned(); Ok(ast::Expression::Ident(ast::Ident { @@ -1568,7 +1575,7 @@ impl<'interner> Monomorphizer<'interner> { })?); let expr_type = self.interner.id_type(expr); - let env_typ = if let types::Type::Function(_, _, function_env_type) = expr_type { + let env_typ = if let types::Type::Function(_, _, function_env_type, _) = expr_type { Self::convert_type(&function_env_type, location)? } else { unreachable!("expected a Function type for a Lambda node") @@ -1598,8 +1605,12 @@ impl<'interner> Monomorphizer<'interner> { let body = self.expr(lambda.body)?; self.lambda_envs_stack.pop(); - let lambda_fn_typ: ast::Type = - ast::Type::Function(parameter_types, Box::new(ret_type), Box::new(env_typ.clone())); + let lambda_fn_typ: ast::Type = ast::Type::Function( + parameter_types, + Box::new(ret_type), + Box::new(env_typ.clone()), + false, + ); let lambda_fn = ast::Expression::Ident(ast::Ident { definition: Definition::Function(id), mutable: false, @@ -1649,7 +1660,7 @@ impl<'interner> Monomorphizer<'interner> { Ok((block_let_stmt, closure_ident)) } - /// Implements std::unsafe::zeroed by returning an appropriate zeroed + /// Implements std::unsafe_func::zeroed by returning an appropriate zeroed /// ast literal or collection node for the given type. Note that for functions /// there is no obvious zeroed value so this should be considered unsafe to use. fn zeroed_value_of_type( @@ -1689,9 +1700,8 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Tuple(fields) => ast::Expression::Tuple(vecmap(fields, |field| { self.zeroed_value_of_type(field, location) })), - ast::Type::Function(parameter_types, ret_type, env) => { - self.create_zeroed_function(parameter_types, ret_type, env, location) - } + ast::Type::Function(parameter_types, ret_type, env, unconstrained) => self + .create_zeroed_function(parameter_types, ret_type, env, *unconstrained, location), ast::Type::Slice(element_type) => { ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents: vec![], @@ -1713,7 +1723,7 @@ impl<'interner> Monomorphizer<'interner> { } // Creating a zeroed function value is almost always an error if it is used later, - // Hence why std::unsafe::zeroed is unsafe. + // Hence why std::unsafe_func::zeroed is unsafe. // // To avoid confusing later passes, we arbitrarily choose to construct a function // that satisfies the input type by discarding all its parameters and returning a @@ -1723,6 +1733,7 @@ impl<'interner> Monomorphizer<'interner> { parameter_types: &[ast::Type], ret_type: &ast::Type, env_type: &ast::Type, + unconstrained: bool, location: noirc_errors::Location, ) -> ast::Expression { let lambda_name = "zeroed_lambda"; @@ -1737,7 +1748,6 @@ impl<'interner> Monomorphizer<'interner> { let return_type = ret_type.clone(); let name = lambda_name.to_owned(); - let unconstrained = false; let function = ast::Function { id, name, @@ -1759,6 +1769,7 @@ impl<'interner> Monomorphizer<'interner> { parameter_types.to_owned(), Box::new(ret_type.clone()), Box::new(env_type.clone()), + unconstrained, ), }) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index 43e742b940e..e9d1dbb67f8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -179,10 +179,10 @@ pub struct NodeInterner { /// may have both `impl Struct { fn foo(){} }` and `impl Struct { fn foo(){} }`. /// If this happens, the returned Vec will have 2 entries and we'll need to further /// disambiguate them by checking the type of each function. - struct_methods: HashMap<(StructId, String), Methods>, + struct_methods: HashMap>, /// Methods on primitive types defined in the stdlib. - primitive_methods: HashMap<(TypeMethodKey, String), Methods>, + primitive_methods: HashMap>, // For trait implementation functions, this is their self type and trait they belong to func_id_to_trait: HashMap, @@ -951,12 +951,12 @@ impl NodeInterner { /// Returns the [`FuncId`] corresponding to the function referred to by `expr_id` pub fn lookup_function_from_expr(&self, expr: &ExprId) -> Option { if let HirExpression::Ident(HirIdent { id, .. }, _) = self.expression(expr) { - if let Some(DefinitionKind::Function(func_id)) = - self.try_definition(id).map(|def| &def.kind) - { - Some(*func_id) - } else { - None + match self.try_definition(id).map(|def| &def.kind) { + Some(DefinitionKind::Function(func_id)) => Some(*func_id), + Some(DefinitionKind::Local(Some(expr_id))) => { + self.lookup_function_from_expr(expr_id) + } + _ => None, } } else { None @@ -1131,22 +1131,27 @@ impl NodeInterner { self.structs[&id].clone() } - pub fn get_struct_methods(&self, id: StructId) -> Vec { - self.struct_methods - .keys() - .filter_map(|(key_id, name)| { - if key_id == &id { - Some( - self.struct_methods - .get(&(*key_id, name.clone())) - .expect("get_struct_methods given invalid StructId") - .clone(), - ) - } else { - None - } - }) - .collect() + pub fn get_struct_methods(&self, id: StructId) -> Option<&HashMap> { + self.struct_methods.get(&id) + } + + fn get_primitive_methods(&self, key: TypeMethodKey) -> Option<&HashMap> { + self.primitive_methods.get(&key) + } + + pub fn get_type_methods(&self, typ: &Type) -> Option<&HashMap> { + match typ { + Type::Struct(struct_type, _) => { + let struct_type = struct_type.borrow(); + self.get_struct_methods(struct_type.id) + } + Type::Alias(type_alias, generics) => { + let type_alias = type_alias.borrow(); + let typ = type_alias.get_type(generics); + self.get_type_methods(&typ) + } + _ => get_type_method_key(typ).and_then(|key| self.get_primitive_methods(key)), + } } pub fn get_trait(&self, id: TraitId) -> &Trait { @@ -1199,14 +1204,19 @@ impl NodeInterner { pub fn id_type_substitute_trait_as_type(&self, def_id: DefinitionId) -> Type { let typ = self.definition_type(def_id); - if let Type::Function(args, ret, env) = &typ { + if let Type::Function(args, ret, env, unconstrained) = &typ { let def = self.definition(def_id); if let Type::TraitAsType(..) = ret.as_ref() { if let DefinitionKind::Function(func_id) = def.kind { let f = self.function(&func_id); let func_body = f.as_expr(); let ret_type = self.id_type(func_body); - let new_type = Type::Function(args.clone(), Box::new(ret_type), env.clone()); + let new_type = Type::Function( + args.clone(), + Box::new(ret_type), + env.clone(), + *unconstrained, + ); return new_type; } } @@ -1300,8 +1310,12 @@ impl NodeInterner { return Some(existing); } - let key = (id, method_name); - self.struct_methods.entry(key).or_default().add_method(method_id, is_trait_method); + self.struct_methods + .entry(id) + .or_default() + .entry(method_name) + .or_default() + .add_method(method_id, is_trait_method); None } Type::Error => None, @@ -1314,7 +1328,9 @@ impl NodeInterner { unreachable!("Cannot add a method to the unsupported type '{}'", other) }); self.primitive_methods - .entry((key, method_name)) + .entry(key) + .or_default() + .entry(method_name) .or_default() .add_method(method_id, is_trait_method); None @@ -1648,7 +1664,7 @@ impl NodeInterner { method_name: &str, force_type_check: bool, ) -> Option { - let methods = self.struct_methods.get(&(id, method_name.to_owned())); + let methods = self.struct_methods.get(&id).and_then(|h| h.get(method_name)); // If there is only one method, just return it immediately. // It will still be typechecked later. @@ -1673,8 +1689,8 @@ impl NodeInterner { } else { // Failed to find a match for the type in question, switch to looking at impls // for all types `T`, e.g. `impl Foo for T` - let key = &(TypeMethodKey::Generic, method_name.to_owned()); - let global_methods = self.primitive_methods.get(key)?; + let global_methods = + self.primitive_methods.get(&TypeMethodKey::Generic)?.get(method_name)?; global_methods.find_matching_method(typ, self) } } @@ -1682,7 +1698,7 @@ impl NodeInterner { /// Looks up a given method name on the given primitive type. pub fn lookup_primitive_method(&self, typ: &Type, method_name: &str) -> Option { let key = get_type_method_key(typ)?; - let methods = self.primitive_methods.get(&(key, method_name.to_owned()))?; + let methods = self.primitive_methods.get(&key)?.get(method_name)?; self.find_matching_method(typ, Some(methods), method_name) } @@ -1779,7 +1795,7 @@ impl NodeInterner { let the_trait = self.get_trait(trait_id); self.ordering_type = match &the_trait.methods[0].typ { Type::Forall(_, typ) => match typ.as_ref() { - Type::Function(_, return_type, _) => Some(return_type.as_ref().clone()), + Type::Function(_, return_type, _, _) => Some(return_type.as_ref().clone()), other => unreachable!("Expected function type for `cmp`, found {}", other), }, other => unreachable!("Expected Forall type for `cmp`, found {}", other), @@ -1803,6 +1819,11 @@ impl NodeInterner { self.prefix_operator_traits.insert(operator, trait_id); } + pub fn is_operator_trait(&self, trait_id: TraitId) -> bool { + self.infix_operator_traits.values().any(|id| *id == trait_id) + || self.prefix_operator_traits.values().any(|id| *id == trait_id) + } + /// This function is needed when creating a NodeInterner for testing so that calls /// to `get_operator_trait` do not panic when the stdlib isn't present. #[cfg(test)] @@ -1977,7 +1998,7 @@ impl NodeInterner { }; let env = Box::new(Type::Unit); - (Type::Function(args, Box::new(ret.clone()), env), ret) + (Type::Function(args, Box::new(ret.clone()), env, false), ret) } /// Returns the type of a prefix operator (which is always a function), along with its return type. @@ -1986,7 +2007,7 @@ impl NodeInterner { let args = vec![rhs_type]; let ret = self.id_type(operator_expr); let env = Box::new(Type::Unit); - (Type::Function(args, Box::new(ret.clone()), env), ret) + (Type::Function(args, Box::new(ret.clone()), env, false), ret) } pub fn is_in_lsp_mode(&self) -> bool { @@ -2017,7 +2038,7 @@ impl Methods { } /// Iterate through each method, starting with the direct methods - fn iter(&self) -> impl Iterator + '_ { + pub fn iter(&self) -> impl Iterator + '_ { self.direct.iter().copied().chain(self.trait_impl_methods.iter().copied()) } @@ -2027,7 +2048,7 @@ impl Methods { // at most 1 matching method in this list. for method in self.iter() { match interner.function_meta(&method).typ.instantiate(interner).0 { - Type::Function(args, _, _) => { + Type::Function(args, _, _, _) => { if let Some(object) = args.first() { let mut bindings = TypeBindings::new(); @@ -2078,7 +2099,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::FmtString(_, _) => Some(FmtString), Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), - Type::Function(_, _, _) => Some(Function), + Type::Function(_, _, _, _) => Some(Function), Type::NamedGeneric(_, _, _) => Some(Generic), Type::Quoted(quoted) => Some(Quoted(*quoted)), Type::MutableReference(element) => get_type_method_key(element), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs index 36d3ce5898c..72719adb4f8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -16,8 +16,12 @@ pub enum ParserErrorReason { ExpectedFieldName(Token), #[error("expected a pattern but found a type - {0}")] ExpectedPatternButFoundType(Token), + #[error("expected an identifier after .")] + ExpectedIdentifierAfterDot, #[error("expected an identifier after ::")] ExpectedIdentifierAfterColons, + #[error("expected {{ after if condition")] + ExpectedLeftBraceAfterIfCondition, #[error("Expected a ; separating these two statements")] MissingSeparatingSemi, #[error("constrain keyword is deprecated")] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs index 5a97d66df9a..ed92143756c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -377,6 +377,7 @@ pub fn block<'a>( statement: impl NoirParser + 'a, ) -> impl NoirParser + 'a { use Token::*; + statement .recover_via(statement_recovery()) .then(just(Semicolon).or_not().map_with_span(|s, span| (s, span))) @@ -519,6 +520,15 @@ where .map(|(block, span)| ExpressionKind::Comptime(block, span)) } +fn unsafe_expr<'a, S>(statement: S) -> impl NoirParser + 'a +where + S: NoirParser + 'a, +{ + keyword(Keyword::Unsafe) + .ignore_then(spanned(block(statement))) + .map(|(block, span)| ExpressionKind::Unsafe(block, span)) +} + fn let_statement<'a, P>( expr_parser: P, ) -> impl NoirParser<((Pattern, UnresolvedType), Expression)> + 'a @@ -847,6 +857,9 @@ where ArrayIndex(Expression), Cast(UnresolvedType), MemberAccess(UnaryRhsMemberAccess), + /// This is to allow `foo.` (no identifier afterwards) to be parsed as `foo` + /// and produce an error, rather than just erroring (for LSP). + JustADot, } // `(arg1, ..., argN)` in `my_func(arg1, ..., argN)` @@ -890,7 +903,16 @@ where }) .labelled(ParsingRuleLabel::FieldAccess); - let rhs = choice((call_rhs, array_rhs, cast_rhs, member_rhs)); + let just_a_dot = + just(Token::Dot).map(|_| UnaryRhs::JustADot).validate(|value, span, emit_error| { + emit_error(ParserError::with_reason( + ParserErrorReason::ExpectedIdentifierAfterDot, + span, + )); + value + }); + + let rhs = choice((call_rhs, array_rhs, cast_rhs, member_rhs, just_a_dot)); foldl_with_span( atom(expr_parser, expr_no_constructors, statement, allow_constructors), @@ -904,6 +926,7 @@ where UnaryRhs::MemberAccess(field) => { Expression::member_access_or_method_call(lhs, field, span) } + UnaryRhs::JustADot => lhs, }, ) } @@ -931,10 +954,32 @@ where keyword(Keyword::If) .ignore_then(expr_no_constructors) - .then(if_block) - .then(keyword(Keyword::Else).ignore_then(else_block).or_not()) - .map(|((condition, consequence), alternative)| { - ExpressionKind::If(Box::new(IfExpression { condition, consequence, alternative })) + .then(if_block.then(keyword(Keyword::Else).ignore_then(else_block).or_not()).or_not()) + .validate(|(condition, consequence_and_alternative), span, emit_error| { + if let Some((consequence, alternative)) = consequence_and_alternative { + ExpressionKind::If(Box::new(IfExpression { + condition, + consequence, + alternative, + })) + } else { + // We allow `if cond` without a block mainly for LSP, so that it parses right + // and autocompletion works there. + emit_error(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBraceAfterIfCondition, + span, + )); + + let span_end = condition.span.end(); + ExpressionKind::If(Box::new(IfExpression { + condition, + consequence: Expression::new( + ExpressionKind::Error, + Span::from(span_end..span_end), + ), + alternative: None, + })) + } }) }) } @@ -1080,7 +1125,8 @@ where }, lambdas::lambda(expr_parser.clone()), block(statement.clone()).map(ExpressionKind::Block), - comptime_expr(statement), + comptime_expr(statement.clone()), + unsafe_expr(statement), quote(), unquote(expr_parser.clone()), variable(), @@ -1407,6 +1453,24 @@ mod test { ); } + #[test] + fn parse_if_without_block() { + let src = "if foo"; + let parser = if_expr(expression_no_constructors(expression()), fresh_statement()); + let (expression_kind, errors) = parse_recover(parser, src); + + let expression_kind = expression_kind.unwrap(); + let ExpressionKind::If(if_expression) = expression_kind else { + panic!("Expected an if expression, got {:?}", expression_kind); + }; + + assert_eq!(if_expression.consequence.kind, ExpressionKind::Error); + assert_eq!(if_expression.alternative, None); + + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected { after if condition"); + } + #[test] fn parse_module_declaration() { parse_with(module_declaration(), "mod foo").unwrap(); @@ -1673,4 +1737,27 @@ mod test { let block_expr = block_expr.expect("Failed to parse module"); assert_eq!(block_expr.statements.len(), 2); } + + #[test] + fn test_parses_member_access_without_member_name() { + let src = "{ foo. }"; + + let (Some(block_expression), errors) = parse_recover(block(fresh_statement()), src) else { + panic!("Expected to be able to parse a block expression"); + }; + + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected an identifier after ."); + + let statement = &block_expression.statements[0]; + let StatementKind::Expression(expr) = &statement.kind else { + panic!("Expected an expression statement"); + }; + + let ExpressionKind::Variable(var) = &expr.kind else { + panic!("Expected a variable expression"); + }; + + assert_eq!(var.to_string(), "foo"); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs index 7c551ca96d1..3db2f24aa58 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -31,6 +31,7 @@ pub(super) fn parse_type_inner<'a>( struct_definition_type(), trait_constraint_type(), trait_definition_type(), + trait_impl_type(), function_definition_type(), module_type(), top_level_item_type(), @@ -107,6 +108,11 @@ pub(super) fn trait_definition_type() -> impl NoirParser { }) } +pub(super) fn trait_impl_type() -> impl NoirParser { + keyword(Keyword::TraitImpl) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TraitImpl).with_span(span)) +} + pub(super) fn function_definition_type() -> impl NoirParser { keyword(Keyword::FunctionDefinition).map_with_span(|_, span| { UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition).with_span(span) @@ -312,13 +318,17 @@ where t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(Span::empty(span.end()))) }); - keyword(Keyword::Fn) - .ignore_then(env) + keyword(Keyword::Unconstrained) + .or_not() + .then(keyword(Keyword::Fn)) + .map(|(unconstrained_token, _fn_token)| unconstrained_token.is_some()) + .then(env) .then(args) .then_ignore(just(Token::Arrow)) .then(type_parser) - .map_with_span(|((env, args), ret), span| { - UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env)).with_span(span) + .map_with_span(|(((unconstrained, env), args), ret), span| { + UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env), unconstrained) + .with_span(span) }) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs index efb430b75eb..61ff6baf919 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs @@ -31,6 +31,12 @@ impl NodeInterner { location_candidate.map(|(index, _location)| *index) } + /// Returns the Type of the expression that exists at the given location. + pub fn type_at_location(&self, location: Location) -> Option { + let index = self.find_location_index(location)?; + Some(self.id_type(index)) + } + /// Returns the [Location] of the definition of the given Ident found at [Span] of the given [FileId]. /// Returns [None] when definition is not found. pub fn get_definition_location_from( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 9124567b4e5..26a82216eee 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -2460,6 +2460,209 @@ fn no_super() { assert_eq!(span.end(), 9); } +#[test] +fn cannot_call_unconstrained_function_outside_of_unsafe() { + let src = r#" + fn main() { + foo(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { + let src = r#" + fn main() { + let func = foo; + // Warning should trigger here + func(); + inner(func); + } + + fn inner(x: unconstrained fn() -> ()) { + // Warning should trigger here + x(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + for error in &errors { + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; + } +} + +#[test] +fn missing_unsafe_block_when_needing_type_annotations() { + // This test is a regression check that even when an unsafe block is missing + // that we still appropriately continue type checking and infer type annotations. + let src = r#" + fn main() { + let z = BigNum { limbs: [2, 0, 0] }; + assert(z.__is_zero() == false); + } + + struct BigNum { + limbs: [u64; N], + } + + impl BigNum { + unconstrained fn __is_zero_impl(self) -> bool { + let mut result: bool = true; + for i in 0..N { + result = result & (self.limbs[i] == 0); + } + result + } + } + + trait BigNumTrait { + fn __is_zero(self) -> bool; + } + + impl BigNumTrait for BigNum { + fn __is_zero(self) -> bool { + self.__is_zero_impl() + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_pass_unconstrained_function_to_regular_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) { + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + .. + }) = &errors[0].0 + { + assert_eq!(expected_typ, "fn() -> ()"); + assert_eq!(expr_typ, "unconstrained fn() -> ()"); + } else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_assign_unconstrained_and_regular_fn_to_variable() { + let src = r#" + fn main() { + let _func = if true { foo } else { bar }; + } + + fn foo() {} + unconstrained fn bar() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { + panic!("Expected a context error, got {:?}", errors[0].0); + }; + + if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { + assert_eq!(expected_typ, "fn() -> ()"); + assert_eq!(expr_typ, "unconstrained fn() -> ()"); + } else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_pass_regular_function_to_unconstrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_unconstrained(func); + } + + fn foo() {} + + fn expect_unconstrained(_func: unconstrained fn() -> ()) {} + "#; + assert_no_errors(src); +} + +#[test] +fn cannot_pass_unconstrained_function_to_constrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0].0 else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { + let src = r#" + fn main() { + let _func: unconstrained fn() -> () = foo; + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { + let src = r#" + fn main() { + let _ = Foo { func: foo }; + } + + fn foo() {} + + struct Foo { + func: unconstrained fn() -> (), + } + "#; + assert_no_errors(src); +} + #[test] fn trait_impl_generics_count_mismatch() { let src = r#" diff --git a/noir/noir-repo/compiler/noirc_printable_type/src/lib.rs b/noir/noir-repo/compiler/noirc_printable_type/src/lib.rs index dfecd301b35..5ab04c6f576 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_printable_type/src/lib.rs @@ -40,6 +40,7 @@ pub enum PrintableType { arguments: Vec, return_type: Box, env: Box, + unconstrained: bool, }, MutableReference { typ: Box, diff --git a/noir/noir-repo/docs/docs/noir/concepts/unconstrained.md b/noir/noir-repo/docs/docs/noir/concepts/unconstrained.md index 96f824c5e42..b5221b8d2dd 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/unconstrained.md +++ b/noir/noir-repo/docs/docs/noir/concepts/unconstrained.md @@ -62,11 +62,13 @@ Those are some nice savings already but we can do better. This code is all const It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. -We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: +We can then run `u72_to_u8` as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: ```rust fn main(num: u72) -> pub [u8; 8] { - let out = u72_to_u8(num); + let out = unsafe { + u72_to_u8(num) + }; let mut reconstructed_num: u72 = 0; for i in 0..8 { @@ -92,6 +94,9 @@ Backend circuit size: 2902 This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). +Note that in order to invoke unconstrained functions we need to wrap them in an `unsafe` block, +to make it clear that the call is unconstrained. + Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. ## Break and Continue diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json index f0768339ea9..27869205ad3 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json +++ b/noir/noir-repo/docs/versioned_docs/version-v0.33.0/reference/debugger/_category_.json @@ -3,4 +3,4 @@ "position": 1, "collapsible": true, "collapsed": true -} \ No newline at end of file +} diff --git a/noir/noir-repo/noir_stdlib/src/array.nr b/noir/noir-repo/noir_stdlib/src/array.nr index af2bea12c60..cef79e7c7f6 100644 --- a/noir/noir-repo/noir_stdlib/src/array.nr +++ b/noir/noir-repo/noir_stdlib/src/array.nr @@ -11,14 +11,20 @@ impl [T; N] { } pub fn sort_via(self, ordering: fn[Env](T, T) -> bool) -> Self { - let sorted_index = self.get_sorting_index(ordering); - 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); - } + let sorted_index = unsafe { + // Safety: These indices are asserted to be the sorted element indices via `find_index` + let sorted_index: [u32; N] = self.get_sorting_index(ordering); + + 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; for i in 0..N { result[i] = self[sorted_index[i]]; } diff --git a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr index a56d6f60390..a4c0f642a82 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr @@ -7,7 +7,7 @@ struct BoundedVec { impl BoundedVec { pub fn new() -> Self { - let zeroed = crate::unsafe::zeroed(); + let zeroed = crate::mem::zeroed(); BoundedVec { storage: [zeroed; MaxLen], len: 0 } } @@ -106,7 +106,7 @@ impl BoundedVec { self.len -= 1; let elem = self.storage[self.len]; - self.storage[self.len] = crate::unsafe::zeroed(); + self.storage[self.len] = crate::mem::zeroed(); elem } diff --git a/noir/noir-repo/noir_stdlib/src/collections/map.nr b/noir/noir-repo/noir_stdlib/src/collections/map.nr index 8324583632f..bd50f345356 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/map.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/map.nr @@ -168,7 +168,9 @@ 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.push(value); } } diff --git a/noir/noir-repo/noir_stdlib/src/collections/umap.nr b/noir/noir-repo/noir_stdlib/src/collections/umap.nr index fe16ef6bca2..86ae79ea644 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/umap.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/umap.nr @@ -119,7 +119,9 @@ impl UHashMap { B: BuildHasher, H: Hasher { // docs:end:contains_key - self.get(key).is_some() + unsafe { + self.get(key) + }.is_some() } // Returns true if the map contains no elements. @@ -438,7 +440,7 @@ where // Not marked as deleted and has key-value. if equal & slot.is_valid(){ let (key, value) = slot.key_value_unchecked(); - let other_value = other.get(key); + let other_value = unsafe { other.get(key) }; if other_value.is_none(){ equal = false; diff --git a/noir/noir-repo/noir_stdlib/src/field/bn254.nr b/noir/noir-repo/noir_stdlib/src/field/bn254.nr index e8db0a30c38..ed0053694c7 100644 --- a/noir/noir-repo/noir_stdlib/src/field/bn254.nr +++ b/noir/noir-repo/noir_stdlib/src/field/bn254.nr @@ -66,13 +66,15 @@ unconstrained fn lte_16_hint(x: Field, y: Field) -> bool { fn assert_gt_limbs(a: (Field, Field), b: (Field, Field)) { let (alo, ahi) = a; let (blo, bhi) = b; - let borrow = lte_16_hint(alo, blo); + unsafe { + let borrow = lte_16_hint(alo, blo); - let rlo = alo - blo - 1 + (borrow as Field) * TWO_POW_128; - let rhi = ahi - bhi - (borrow as Field); + let rlo = alo - blo - 1 + (borrow as Field) * TWO_POW_128; + let rhi = ahi - bhi - (borrow as Field); - rlo.assert_max_bit_size(128); - rhi.assert_max_bit_size(128); + rlo.assert_max_bit_size(128); + rhi.assert_max_bit_size(128); + } } /// Decompose a single field into two 16 byte fields. @@ -80,19 +82,21 @@ pub fn decompose(x: Field) -> (Field, Field) { if is_unconstrained() { compute_decomposition(x) } else { - // Take hints of the decomposition - let (xlo, xhi) = decompose_hint(x); + unsafe { + // Take hints of the decomposition + let (xlo, xhi) = decompose_hint(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); - // Assert that the decomposition of P is greater than the decomposition of x - assert_gt_limbs((PLO, PHI), (xlo, xhi)); - (xlo, xhi) + // Assert that the decomposition of P is greater than the decomposition of x + assert_gt_limbs((PLO, PHI), (xlo, xhi)); + (xlo, xhi) + } } } @@ -118,14 +122,16 @@ pub fn gt(a: Field, b: Field) -> bool { compute_lt(b, a, 32) } else if a == b { false - } else { + } else { // Take a hint of the comparison and verify it - if lt_32_hint(a, b) { - assert_gt(b, a); - false - } else { - assert_gt(a, b); - true + unsafe { + if lt_32_hint(a, b) { + assert_gt(b, a); + false + } else { + assert_gt(a, b); + true + } } } } diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index 88dbf83e5bd..8a546e64309 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -108,7 +108,9 @@ fn __derive_generators( // does not assert the limbs are 128 bits // does not assert the decomposition does not overflow the EmbeddedCurveScalar fn from_field_unsafe(scalar: Field) -> EmbeddedCurveScalar { - let (xlo, xhi) = crate::field::bn254::decompose_hint(scalar); + let (xlo, xhi) = unsafe { + crate::field::bn254::decompose_hint(scalar) + }; // Check that the decomposition is correct assert_eq(scalar, xlo + crate::field::bn254::TWO_POW_128 * xhi); EmbeddedCurveScalar { lo: xlo, hi: xhi } diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index 2d559c43162..5c4587b69ac 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -12,7 +12,6 @@ mod sha256; mod sha512; mod field; mod ec; -mod unsafe; mod collections; mod compat; mod convert; @@ -28,18 +27,27 @@ mod bigint; mod runtime; mod meta; mod append; +mod mem; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[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/noir-repo/noir_stdlib/src/unsafe.nr b/noir/noir-repo/noir_stdlib/src/mem.nr similarity index 99% rename from noir/noir-repo/noir_stdlib/src/unsafe.nr rename to noir/noir-repo/noir_stdlib/src/mem.nr index 542bd31fa84..88d17e20ee3 100644 --- a/noir/noir-repo/noir_stdlib/src/unsafe.nr +++ b/noir/noir-repo/noir_stdlib/src/mem.nr @@ -3,3 +3,4 @@ /// is no guarantee that all zeroes is a valid bit pattern for every type. #[builtin(zeroed)] pub fn zeroed() -> T {} + diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..e081d4ecd4b 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -8,6 +8,7 @@ mod module; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +32,9 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted let mut result = quote {}; for trait_to_derive in traits { - let handler = HANDLERS.get(trait_to_derive); + let handler = unsafe { + HANDLERS.get(trait_to_derive) + }; assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); let trait_impl = handler.unwrap()(s); diff --git a/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr b/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr new file mode 100644 index 00000000000..2f82ee5f434 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr @@ -0,0 +1,7 @@ +impl TraitImpl { + #[builtin(trait_impl_trait_generic_args)] + fn trait_generic_args(self) -> [Type] {} + + #[builtin(trait_impl_methods)] + fn methods(self) -> [FunctionDefinition] {} +} diff --git a/noir/noir-repo/noir_stdlib/src/meta/typ.nr b/noir/noir-repo/noir_stdlib/src/meta/typ.nr index ad669e93c0a..67ad2a96739 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/typ.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/typ.nr @@ -20,6 +20,9 @@ impl Type { #[builtin(type_as_tuple)] fn as_tuple(self) -> Option<[Type]> {} + #[builtin(type_get_trait_impl)] + fn get_trait_impl(self, constraint: TraitConstraint) -> Option {} + #[builtin(type_implements)] fn implements(self, constraint: TraitConstraint) -> bool {} diff --git a/noir/noir-repo/noir_stdlib/src/option.nr b/noir/noir-repo/noir_stdlib/src/option.nr index df020e75615..8d6d9ef970d 100644 --- a/noir/noir-repo/noir_stdlib/src/option.nr +++ b/noir/noir-repo/noir_stdlib/src/option.nr @@ -10,7 +10,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::mem::zeroed() } } /// Constructs a Some wrapper around the given value diff --git a/noir/noir-repo/noir_stdlib/src/slice.nr b/noir/noir-repo/noir_stdlib/src/slice.nr index 8d3f395f080..f9aa98a9ecd 100644 --- a/noir/noir-repo/noir_stdlib/src/slice.nr +++ b/noir/noir-repo/noir_stdlib/src/slice.nr @@ -49,7 +49,7 @@ impl [T] { pub fn as_array(self) -> [T; N] { assert(self.len() == N); - let mut array = [crate::unsafe::zeroed(); N]; + let mut array = [crate::mem::zeroed(); N]; for i in 0..N { array[i] = self[i]; } diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr index 7b75cf4cae4..a6d980c7f33 100644 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ b/noir/noir-repo/noir_stdlib/src/uint128.nr @@ -103,7 +103,9 @@ impl U128 { (if ascii < 58 { ascii - 48 } else { - let ascii = ascii + 32 * (U128::uconstrained_check_is_upper_ascii(ascii) as u8); + let ascii = ascii + 32 * (unsafe { + U128::uconstrained_check_is_upper_ascii(ascii) as u8 + }); assert(ascii >= 97); // enforce >= 'a' assert(ascii <= 102); // enforce <= 'f' ascii - 87 @@ -212,21 +214,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 + } } } @@ -439,31 +446,40 @@ mod tests { let b= U128::from_u64s_le(0x0, 0xfffffffffffffffe); let c= U128::one(); let d= U128::from_u64s_le(0x0, 0x1); - let (q,r) = a.unconstrained_div(b); - assert_eq(q, c); - assert_eq(r, d); + unsafe { + let (q,r) = a.unconstrained_div(b); + assert_eq(q, c); + assert_eq(r, d); + } let a = U128::from_u64s_le(2, 0); let b = U128::one(); // Check the case where a is a multiple of b - let (c,d ) = a.unconstrained_div(b); - assert_eq((c, d), (a, U128::zero())); + unsafe { + let (c, d) = a.unconstrained_div(b); + assert_eq((c, d), (a, U128::zero())); + } // Check where b is a multiple of a - let (c,d) = b.unconstrained_div(a); - assert_eq((c, d), (U128::zero(), b)); + unsafe { + let (c,d) = b.unconstrained_div(a); + assert_eq((c, d), (U128::zero(), b)); + } // Dividing by zero returns 0,0 let a = U128::from_u64s_le(0x1, 0x0); let b = U128::zero(); - let (c,d)= a.unconstrained_div(b); - assert_eq((c, d), (U128::zero(), U128::zero())); - + unsafe { + let (c, d)= a.unconstrained_div(b); + assert_eq((c, d), (U128::zero(), U128::zero())); + } // Dividing 1<<127 by 1<<127 (special case) let a = U128::from_u64s_le(0x0, pow63 as u64); let b = U128::from_u64s_le(0x0, pow63 as u64); - let (c,d )= a.unconstrained_div(b); - assert_eq((c, d), (U128::one(), U128::zero())); + unsafe { + let (c, d) = a.unconstrained_div(b); + assert_eq((c, d), (U128::one(), U128::zero())); + } } #[test] diff --git a/noir/noir-repo/scripts/install_bb.sh b/noir/noir-repo/scripts/install_bb.sh index 65a449be543..91b9180f138 100755 --- a/noir/noir-repo/scripts/install_bb.sh +++ b/noir/noir-repo/scripts/install_bb.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION="0.47.1" +VERSION="0.48.0" BBUP_PATH=~/.bb/bbup diff --git a/noir/noir-repo/test_programs/compile_failure/array_length_defaulting/src/main.nr b/noir/noir-repo/test_programs/compile_failure/array_length_defaulting/src/main.nr index 40543db2870..16902860a16 100644 --- a/noir/noir-repo/test_programs/compile_failure/array_length_defaulting/src/main.nr +++ b/noir/noir-repo/test_programs/compile_failure/array_length_defaulting/src/main.nr @@ -1,5 +1,5 @@ fn main() { - let x = std::unsafe::zeroed(); + let x = std::mem::zeroed(); foo(x); } diff --git a/noir/noir-repo/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr b/noir/noir-repo/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr index 473ad8e8d6a..f262635e508 100644 --- a/noir/noir-repo/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr +++ b/noir/noir-repo/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr @@ -3,6 +3,8 @@ unconstrained fn mut_ref_identity(value: &mut Field) -> Field { } fn main(mut x: Field, y: pub Field) { - let returned_x = mut_ref_identity(&mut x); + let returned_x = unsafe { + mut_ref_identity(&mut x) + }; assert(returned_x == x); } diff --git a/noir/noir-repo/test_programs/compile_failure/regression_5008/src/main.nr b/noir/noir-repo/test_programs/compile_failure/regression_5008/src/main.nr index 6d9645ee6eb..cf79c22683f 100644 --- a/noir/noir-repo/test_programs/compile_failure/regression_5008/src/main.nr +++ b/noir/noir-repo/test_programs/compile_failure/regression_5008/src/main.nr @@ -13,5 +13,7 @@ impl Foo { fn main() { let foo = Foo { bar: &mut Bar { value: 0 } }; - foo.crash_fn(); + unsafe { + foo.crash_fn() + }; } diff --git a/noir/noir-repo/test_programs/compile_failure/turbofish_generic_count/src/main.nr b/noir/noir-repo/test_programs/compile_failure/turbofish_generic_count/src/main.nr index 4091b2f0581..5dc05a3a5c3 100644 --- a/noir/noir-repo/test_programs/compile_failure/turbofish_generic_count/src/main.nr +++ b/noir/noir-repo/test_programs/compile_failure/turbofish_generic_count/src/main.nr @@ -6,7 +6,7 @@ struct Bar { impl Bar { fn zeroed(_self: Self) -> A { - std::unsafe::zeroed() + std::mem::zeroed() } } diff --git a/noir/noir-repo/test_programs/compile_failure/unconstrained_ref/src/main.nr b/noir/noir-repo/test_programs/compile_failure/unconstrained_ref/src/main.nr index 1491badaa49..9a4f42469b5 100644 --- a/noir/noir-repo/test_programs/compile_failure/unconstrained_ref/src/main.nr +++ b/noir/noir-repo/test_programs/compile_failure/unconstrained_ref/src/main.nr @@ -4,5 +4,7 @@ unconstrained fn uncon_ref() -> &mut Field { } fn main() { - let e = uncon_ref(); + let e = unsafe { + uncon_ref() + }; } diff --git a/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index d4f71d38413..6cd13ab0e2f 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -11,7 +11,7 @@ fn main() { fn split_first(array: [T; N]) -> (T, [T; N - 1]) { std::static_assert(N != 0, "split_first called on empty array"); - let mut new_array: [T; N - 1] = std::unsafe::zeroed(); + let mut new_array: [T; N - 1] = std::mem::zeroed(); for i in 0..N - 1 { new_array[i] = array[i + 1]; @@ -21,7 +21,7 @@ fn split_first(array: [T; N]) -> (T, [T; N - 1]) { } fn push(array: [Field; N], element: Field) -> [Field; N + 1] { - let mut result: [_; N + 1] = std::unsafe::zeroed(); + let mut result: [_; N + 1] = std::mem::zeroed(); result[array.len()] = element; for i in 0..array.len() { diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_cast/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_cast/src/main.nr index ecb832468ba..f2bede99d4c 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_cast/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_cast/src/main.nr @@ -2,11 +2,13 @@ // // The features being tested are cast operations on brillig fn main() { - bool_casts(); - field_casts(); - uint_casts(); - int_casts(); - mixed_casts(); + unsafe { + bool_casts(); + field_casts(); + uint_casts(); + int_casts(); + mixed_casts(); + } } unconstrained fn bool_casts() { diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr index 54f06858846..fedfe48cb0d 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_field_binary_operations/src/main.nr @@ -1,11 +1,13 @@ // Tests arithmetic operations on fields fn main() { - let x = 4; - let y = 2; - assert((x + y) == add(x, y)); - assert((x - y) == sub(x, y)); - assert((x * y) == mul(x, y)); - assert((x / y) == div(x, y)); + unsafe { + let x = 4; + let y = 2; + assert((x + y) == add(x, y)); + assert((x - y) == sub(x, y)); + assert((x * y) == mul(x, y)); + assert((x / y) == div(x, y)); + } } unconstrained fn add(x: Field, y: Field) -> Field { diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr index 7d3f9459598..7ecc21dbd2f 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_integer_binary_operations/src/main.nr @@ -3,28 +3,30 @@ fn main() { let x: u32 = 6; let y: u32 = 2; - assert((x + y) == add(x, y)); + unsafe { + assert((x + y) == add(x, y)); - assert((x - y) == sub(x, y)); + assert((x - y) == sub(x, y)); - assert((x * y) == mul(x, y)); + assert((x * y) == mul(x, y)); - assert((x / y) == div(x, y)); - // TODO SSA => ACIR has some issues with i32 ops - assert(check_signed_div(6, 2, 3)); + assert((x / y) == div(x, y)); + // TODO SSA => ACIR has some issues with i32 ops + assert(check_signed_div(6, 2, 3)); - assert(eq(1, 2) == false); - assert(eq(1, 1)); + assert(eq(1, 2) == false); + assert(eq(1, 1)); - assert(lt(x, y) == false); - assert(lt(y, x)); + assert(lt(x, y) == false); + assert(lt(y, x)); - assert((x & y) == and(x, y)); - assert((x | y) == or(x, y)); - // TODO SSA => ACIR has some issues with xor ops - assert(check_xor(x, y, 4)); - assert((x >> y as u8) == shr(x, y as u8)); - assert((x << y as u8) == shl(x, y as u8)); + assert((x & y) == and(x, y)); + assert((x | y) == or(x, y)); + // TODO SSA => ACIR has some issues with xor ops + assert(check_xor(x, y, 4)); + assert((x >> y as u8) == shr(x, y as u8)); + assert((x << y as u8) == shl(x, y as u8)); + } } unconstrained fn add(x: u32, y: u32) -> u32 { diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_modulo/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_modulo/src/main.nr index 195ed31fb08..dff5eadcb55 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_modulo/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_modulo/src/main.nr @@ -2,20 +2,22 @@ // // The features being tested is modulo operations on brillig fn main() { - assert(modulo(47, 3) == 2); - assert(modulo(2, 3) == 2); - assert(signed_modulo(5, 3) == 2); - assert(signed_modulo(2, 3) == 2); + unsafe { + assert(modulo(47, 3) == 2); + assert(modulo(2, 3) == 2); + assert(signed_modulo(5, 3) == 2); + assert(signed_modulo(2, 3) == 2); - let minus_two: i8 = -2; // 254 - let minus_three: i8 = -3; // 253 - let minus_five: i8 = -5; // 251 - // (5 / -3) * -3 + 2 = -1 * -3 + 2 = 3 + 2 = 5 - assert(signed_modulo(5, minus_three) == 2); - // (-5 / 3) * 3 - 2 = -1 * 3 - 2 = -3 - 2 = -5 - assert(signed_modulo(minus_five, 3) == minus_two); - // (-5 / -3) * -3 - 2 = 1 * -3 - 2 = -3 - 2 = -5 - assert(signed_modulo(minus_five, minus_three) == minus_two); + let minus_two: i8 = -2; // 254 + let minus_three: i8 = -3; // 253 + let minus_five: i8 = -5; // 251 + // (5 / -3) * -3 + 2 = -1 * -3 + 2 = 3 + 2 = 5 + assert(signed_modulo(5, minus_three) == 2); + // (-5 / 3) * 3 - 2 = -1 * 3 - 2 = -3 - 2 = -5 + assert(signed_modulo(minus_five, 3) == minus_two); + // (-5 / -3) * -3 - 2 = 1 * -3 - 2 = -3 - 2 = -5 + assert(signed_modulo(minus_five, minus_three) == minus_two); + } } unconstrained fn modulo(x: u32, y: u32) -> u32 { diff --git a/noir/noir-repo/test_programs/compile_success_empty/brillig_slice_input/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/brillig_slice_input/src/main.nr index 8403cb7d4a0..596f364b49f 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/brillig_slice_input/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/brillig_slice_input/src/main.nr @@ -25,8 +25,10 @@ fn main() { y: 8, } ]); - let brillig_sum = sum_slice(slice); - assert_eq(brillig_sum, 55); + unsafe { + let brillig_sum = sum_slice(slice); + assert_eq(brillig_sum, 55); + } slice = slice.push_back([ Point { @@ -38,6 +40,8 @@ fn main() { y: 13, } ]); - let brillig_sum = sum_slice(slice); - assert_eq(brillig_sum, 100); + unsafe { + let brillig_sum = sum_slice(slice); + assert_eq(brillig_sum, 100); + } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr index 19572fd15a1..705a1b2ab4e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr @@ -7,7 +7,7 @@ fn main() { // Can't print these at compile-time here since printing to stdout while // compiling breaks the test runner. let s1 = f"x is {x}, fake interpolation: \{y}, y is {y}"; - let s2 = std::unsafe::zeroed::>(); + let s2 = std::mem::zeroed::>(); (s1, s2) }; assert_eq(s1, "x is 4, fake interpolation: {y}, y is 5"); diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml new file mode 100644 index 00000000000..c9a6f1e150e --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_trait_impl" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr new file mode 100644 index 00000000000..87b48e7a357 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr @@ -0,0 +1,32 @@ +use std::meta::type_of; + +trait SomeTrait { + fn foo(); +} +struct SomeStruct { + +} + +impl SomeTrait for SomeStruct { + fn foo() {} +} + +fn main() { + comptime + { + let some_struct = quote { SomeStruct }.as_type(); + let some_trait = quote { SomeTrait }.as_trait_constraint(); + let trait_impl = some_struct.get_trait_impl(some_trait).unwrap(); + + // Check TraitImpl::trait_generic_args + let trait_generic_args = trait_impl.trait_generic_args(); + assert_eq(trait_generic_args.len(), 2); + assert_eq(trait_generic_args[0], quote { i32 }.as_type()); + assert_eq(trait_generic_args[1], quote { i64 }.as_type()); + + // Check TraitImpl::methods + let methods = trait_impl.methods(); + assert_eq(methods.len(), 1); + assert_eq(methods[0].name(), quote { foo }); + } +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr index 170292b0e37..f0b53a392ed 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -108,6 +108,8 @@ fn main() { let some_trait_field = quote { SomeTrait }.as_trait_constraint(); assert(!struct_implements_some_trait.implements(some_trait_field)); assert(!struct_does_not_implement_some_trait.implements(some_trait_field)); + + let _trait_impl = struct_implements_some_trait.get_trait_impl(some_trait_i32).unwrap(); } } @@ -117,5 +119,7 @@ fn function_with_where(_x: T) where T: SomeTrait { let t = quote { T }.as_type(); let some_trait_i32 = quote { SomeTrait }.as_trait_constraint(); assert(t.implements(some_trait_i32)); + + assert(t.get_trait_impl(some_trait_i32).is_none()); } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr index 52567025e23..31d08d2762d 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr @@ -4,7 +4,9 @@ use std::meta::unquote; fn main() { comptime { - foo::<3>(5); + unsafe { + foo::<3>(5) + }; submodule::bar(); } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr index 44ccb2bd595..b35afdbc6c6 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr @@ -1,3 +1,3 @@ fn main() { - let _: [u8] = std::unsafe::zeroed(); + let _: [u8] = std::mem::zeroed(); } diff --git a/noir/noir-repo/test_programs/compile_success_no_bug/check_uncostrained_regression/src/main.nr b/noir/noir-repo/test_programs/compile_success_no_bug/check_uncostrained_regression/src/main.nr index e93e068f432..33b84c2b702 100644 --- a/noir/noir-repo/test_programs/compile_success_no_bug/check_uncostrained_regression/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_no_bug/check_uncostrained_regression/src/main.nr @@ -15,7 +15,9 @@ unconstrained fn convert(trigger: Trigger) -> ResultType { } impl Trigger { fn execute(self) -> ResultType { - let result = convert(self); + let result = unsafe { + convert(self) + }; assert(result.a == self.x + 1); assert(result.b == self.y - 1 + self.z[2]); assert(result.c[1] == 0); diff --git a/noir/noir-repo/test_programs/execution_failure/brillig_assert_fail/src/main.nr b/noir/noir-repo/test_programs/execution_failure/brillig_assert_fail/src/main.nr index da9d4ec1ac8..07256f0c398 100644 --- a/noir/noir-repo/test_programs/execution_failure/brillig_assert_fail/src/main.nr +++ b/noir/noir-repo/test_programs/execution_failure/brillig_assert_fail/src/main.nr @@ -2,7 +2,11 @@ // // The features being tested is using assert on brillig fn main(x: Field) { - assert(1 == conditional(x as bool)); + assert( + 1 == unsafe { + conditional(x as bool) + } + ); } unconstrained fn conditional(x: bool) -> Field { diff --git a/noir/noir-repo/test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr b/noir/noir-repo/test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr index bd77551e304..4dd06ceb743 100644 --- a/noir/noir-repo/test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr +++ b/noir/noir-repo/test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr @@ -1,5 +1,9 @@ fn main(x: Field) { - assert(1 == conditional(x)); + assert( + 1 == unsafe { + conditional(x) + } + ); } unconstrained fn conditional(x: Field) -> Field { diff --git a/noir/noir-repo/test_programs/execution_failure/fold_nested_brillig_assert_fail/src/main.nr b/noir/noir-repo/test_programs/execution_failure/fold_nested_brillig_assert_fail/src/main.nr index 0a5038c179b..21fd4006c2a 100644 --- a/noir/noir-repo/test_programs/execution_failure/fold_nested_brillig_assert_fail/src/main.nr +++ b/noir/noir-repo/test_programs/execution_failure/fold_nested_brillig_assert_fail/src/main.nr @@ -13,7 +13,9 @@ fn fold_conditional_wrapper(x: bool) -> Field { #[fold] fn fold_conditional(x: bool) -> Field { - conditional_wrapper(x) + unsafe { + conditional_wrapper(x) + } } unconstrained fn conditional_wrapper(x: bool) -> Field { diff --git a/noir/noir-repo/test_programs/execution_failure/regression_5202/src/main.nr b/noir/noir-repo/test_programs/execution_failure/regression_5202/src/main.nr index 45ffafca4c7..fbcdba43355 100644 --- a/noir/noir-repo/test_programs/execution_failure/regression_5202/src/main.nr +++ b/noir/noir-repo/test_programs/execution_failure/regression_5202/src/main.nr @@ -13,11 +13,15 @@ unconstrained fn should_i_assert() -> bool { } fn get_magical_boolean() -> bool { - let option = get_unconstrained_option(); + let option = unsafe { + get_unconstrained_option() + }; let pre_assert = option.is_some().to_field(); - if should_i_assert() { + if unsafe { + should_i_assert() + } { // Note that `should_i_assert` is unconstrained, so Noir should not be able to infer // any behavior from the contents of this block. In this case it is actually false, so the // assertion below is not even executed (if it did it'd fail since the values are not equal). diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml new file mode 100644 index 00000000000..4123215e2b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unused_array_get_known_index_out_of_bounds" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr b/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr new file mode 100644 index 00000000000..bdc645ec483 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + let array = [1, 2, 3]; + let _ = array[10]; // Index out of bounds +} diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml new file mode 100644 index 00000000000..04d9146b881 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unused_array_get_unknown_index_out_of_bounds" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml new file mode 100644 index 00000000000..1ec81884d61 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml @@ -0,0 +1 @@ +x = "10" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr b/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr new file mode 100644 index 00000000000..15c2d1f1f23 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr @@ -0,0 +1,4 @@ +fn main(x: Field) { + let array = [1, 2, 3]; + let _ = array[x]; // Index out of bounds +} diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml new file mode 100644 index 00000000000..b8fe7e955a1 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unused_array_set_known_index_out_of_bounds" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr b/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr new file mode 100644 index 00000000000..9c447aee08f --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + let mut array = [1, 2, 3]; + array[10] = 1; // Index out of bounds +} diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml new file mode 100644 index 00000000000..ccc00956e80 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unused_array_set_unknown_index_out_of_bounds" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml b/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml new file mode 100644 index 00000000000..1ec81884d61 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml @@ -0,0 +1 @@ +x = "10" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr b/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr new file mode 100644 index 00000000000..dbde898f7a9 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr @@ -0,0 +1,4 @@ +fn main(x: Field) { + let mut array = [1, 2, 3]; + array[x] = 1; // Index out of bounds +} diff --git a/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml new file mode 100644 index 00000000000..f2acfa4d4cf --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unused_slice_get_known_index_out_of_bounds" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr new file mode 100644 index 00000000000..59e57664bbe --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + let slice = &[1, 2, 3]; + let _ = slice[10]; // Index out of bounds +} diff --git a/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml new file mode 100644 index 00000000000..3c8ae8fe07a --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unused_slice_get_unknown_index_out_of_bounds" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml new file mode 100644 index 00000000000..1ec81884d61 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml @@ -0,0 +1 @@ +x = "10" \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr new file mode 100644 index 00000000000..5a62e0e9843 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr @@ -0,0 +1,4 @@ +fn main(x: Field) { + let slice = &[1, 2, 3]; + let _ = slice[x]; // Index out of bounds +} diff --git a/noir/noir-repo/test_programs/execution_success/acir_inside_brillig_recursion/src/main.nr b/noir/noir-repo/test_programs/execution_success/acir_inside_brillig_recursion/src/main.nr index 92f8524a771..49b7c00b6b9 100644 --- a/noir/noir-repo/test_programs/execution_success/acir_inside_brillig_recursion/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/acir_inside_brillig_recursion/src/main.nr @@ -1,5 +1,7 @@ fn main() { - assert_eq(fibonacci(3), fibonacci_hint(3)); + unsafe { + assert_eq(fibonacci(3), fibonacci_hint(3)); + } } unconstrained fn fibonacci_hint(x: u32) -> u32 { diff --git a/noir/noir-repo/test_programs/execution_success/aes128_encrypt/src/main.nr b/noir/noir-repo/test_programs/execution_success/aes128_encrypt/src/main.nr index cd7fb4772e2..9cf07841b9e 100644 --- a/noir/noir-repo/test_programs/execution_success/aes128_encrypt/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/aes128_encrypt/src/main.nr @@ -31,12 +31,19 @@ unconstrained fn cipher(plaintext: [u8; 12], iv: [u8; 16], key: [u8; 16]) -> [u8 fn main(inputs: str<12>, iv: str<16>, key: str<16>, output: str<32>) { let result = std::aes128::aes128_encrypt(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); - let output_bytes: [u8; 16] = decode_hex(output); - for i in 0..16 { - assert(result[i] == output_bytes[i]); - } - let unconstrained_result = cipher(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); - for i in 0..16 { - assert(unconstrained_result[i] == output_bytes[i]); + + let output_bytes: [u8; 16] = unsafe { + let output_bytes: [u8; 16] = decode_hex(output); + for i in 0..16 { + assert(result[i] == output_bytes[i]); + } + output_bytes + }; + + unsafe { + let unconstrained_result = cipher(inputs.as_bytes(), iv.as_bytes(), key.as_bytes()); + for i in 0..16 { + assert(unconstrained_result[i] == output_bytes[i]); + } } } diff --git a/noir/noir-repo/test_programs/execution_success/array_to_slice_constant_length/src/main.nr b/noir/noir-repo/test_programs/execution_success/array_to_slice_constant_length/src/main.nr index e81dd4a0c5f..5668a9ff388 100644 --- a/noir/noir-repo/test_programs/execution_success/array_to_slice_constant_length/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/array_to_slice_constant_length/src/main.nr @@ -5,6 +5,8 @@ unconstrained fn return_array(val: Field) -> [Field; 1] { } fn main(val: Field) { - let array = return_array(val); - assert_constant(array.as_slice().len()); + unsafe { + let array = return_array(val); + assert_constant(array.as_slice().len()); + } } diff --git a/noir/noir-repo/test_programs/execution_success/bigint/src/main.nr b/noir/noir-repo/test_programs/execution_success/bigint/src/main.nr index 9385b39e847..188ccd01131 100644 --- a/noir/noir-repo/test_programs/execution_success/bigint/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/bigint/src/main.nr @@ -17,7 +17,9 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { let c = if x[0] != 0 { test_unconstrained1(a, b) } else { - test_unconstrained2(a, b) + unsafe { + test_unconstrained2(a, b) + } }; assert(c.array[0] == std::wrapping_mul(x[0], y[0])); diff --git a/noir/noir-repo/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr index 5bd6ce0adb2..1a595ecfb38 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_acir_as_brillig/src/main.nr @@ -1,7 +1,9 @@ fn main(x: u32) { - assert(entry_point(x) == 2); - swap_entry_point(x, x + 1); - assert(deep_entry_point(x) == 4); + unsafe { + assert(entry_point(x) == 2); + swap_entry_point(x, x + 1); + assert(deep_entry_point(x) == 4); + } } fn inner(x: u32) -> u32 { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_array_to_slice/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_array_to_slice/src/main.nr index 262d3b5d86a..f54adb39963 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_array_to_slice/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_array_to_slice/src/main.nr @@ -10,9 +10,11 @@ unconstrained fn brillig_as_slice(x: Field) -> (u32, Field, Field) { } fn main(x: Field) { - let (slice_len, dynamic_0, slice_0) = brillig_as_slice(x); - assert(slice_len == 1); - assert(dynamic_0 == 2); - assert(slice_0 == 2); + unsafe { + let (slice_len, dynamic_0, slice_0) = brillig_as_slice(x); + assert(slice_len == 1); + assert(dynamic_0 == 2); + assert(slice_0 == 2); + } } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_arrays/src/main.nr index e535b6001a4..c7f0757f31e 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_arrays/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_arrays/src/main.nr @@ -2,8 +2,10 @@ // // The features being tested are array reads and writes fn main(x: [Field; 3]) { - read_array(x); - read_write_array(x); + unsafe { + read_array(x); + read_write_array(x); + } } unconstrained fn read_array(x: [Field; 3]) { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_assert/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_assert/src/main.nr index 16fe7b29061..c6c39b61bc9 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_assert/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_assert/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is using assert on brillig fn main(x: Field) { - assert(1 == conditional(x as bool)); + unsafe { + assert(1 == conditional(x as bool)); + } } unconstrained fn conditional(x: bool) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_blake2s/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_blake2s/src/main.nr index 2743e02e920..122142a9c80 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_blake2s/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_blake2s/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is blake2s in brillig fn main(x: [u8; 5], result: [u8; 32]) { - assert(blake2s(x) == result); + unsafe { + assert(blake2s(x) == result); + } } unconstrained fn blake2s(x: [u8; 5]) -> [u8; 32] { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr index 2809668c574..6ddfc03622a 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_block_parameter_liveness/src/main.nr @@ -38,11 +38,11 @@ struct Outer { // If we don't take into account block parameter liveness, this function will need 5*500=2500 stack items unconstrained fn main(conditions: [bool; 5]) -> pub Outer { let out0 = if conditions[0] { - let mut outer: Outer = std::unsafe::zeroed(); + let mut outer: Outer = std::mem::zeroed(); outer.middle_a.inner_a.a = 1; outer } else { - let mut outer: Outer= std::unsafe::zeroed(); + let mut outer: Outer = std::mem::zeroed(); outer.middle_f.inner_c.d = 2; outer }; diff --git a/noir/noir-repo/test_programs/execution_success/brillig_calls/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_calls/src/main.nr index 5c39713f5bb..3e23da53b18 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_calls/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_calls/src/main.nr @@ -2,10 +2,12 @@ // // The features being tested is brillig calls fn main(x: u32) { - assert(entry_point(x) == 2); - swap_entry_point(x, x + 1); - assert(deep_entry_point(x) == 4); - multiple_values_entry_point(x); + unsafe { + assert(entry_point(x) == 2); + swap_entry_point(x, x + 1); + assert(deep_entry_point(x) == 4); + multiple_values_entry_point(x); + } } unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_calls_array/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_calls_array/src/main.nr index 1b1d89f6366..8b27a9bb202 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_calls_array/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_calls_array/src/main.nr @@ -2,8 +2,10 @@ // // The features being tested is brillig calls passing arrays around fn main(x: [u32; 3]) { - assert(entry_point(x) == 9); - another_entry_point(x); + unsafe { + assert(entry_point(x) == 9); + another_entry_point(x); + } } unconstrained fn inner(x: [u32; 3]) -> [u32; 3] { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr index 0a1718d0171..318da4caf72 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_calls_conditionals/src/main.nr @@ -2,10 +2,12 @@ // // The features being tested is brillig calls with conditionals fn main(x: [u32; 3]) { - assert(entry_point(x[0]) == 7); - assert(entry_point(x[1]) == 8); - assert(entry_point(x[2]) == 9); - assert(entry_point(42) == 0); + unsafe { + assert(entry_point(x[0]) == 7); + assert(entry_point(x[1]) == 8); + assert(entry_point(x[2]) == 9); + assert(entry_point(42) == 0); + } } unconstrained fn inner_1() -> u32 { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_conditional/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_conditional/src/main.nr index a59336a877b..8ababf82319 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_conditional/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_conditional/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is basic conditonal on brillig fn main(x: Field) { - assert(4 == conditional(x == 1)); + unsafe { + assert(4 == conditional(x == 1)); + } } unconstrained fn conditional(x: bool) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr index 78343dcd26c..45f13c79637 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256k1/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is ecdsa in brillig fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { - assert(ecdsa(hashed_message, pub_key_x, pub_key_y, signature)); + unsafe { + assert(ecdsa(hashed_message, pub_key_x, pub_key_y, signature)); + } } unconstrained fn ecdsa( diff --git a/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr index 48debadb012..32b562ec50c 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_ecdsa_secp256r1/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is ecdsa in brillig fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { - assert(ecdsa(hashed_message, pub_key_x, pub_key_y, signature)); + unsafe { + assert(ecdsa(hashed_message, pub_key_x, pub_key_y, signature)); + } } unconstrained fn ecdsa( diff --git a/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr index 1476c447431..55b9d307905 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_fns_as_values/src/main.nr @@ -1,18 +1,20 @@ struct MyStruct { - operation: fn (u32) -> u32, + operation: unconstrained fn (u32) -> u32, } fn main(x: u32) { - assert(wrapper(increment, x) == x + 1); - assert(wrapper(increment_acir, x) == x + 1); - assert(wrapper(decrement, x) == x - 1); - assert(wrapper_with_struct(MyStruct { operation: increment }, x) == x + 1); - assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == x - 1); - // https://github.com/noir-lang/noir/issues/1975 - assert(increment(x) == x + 1); + unsafe { + assert(wrapper(increment, x) == x + 1); + assert(wrapper(increment_acir, x) == x + 1); + assert(wrapper(decrement, x) == x - 1); + assert(wrapper_with_struct(MyStruct { operation: increment }, x) == x + 1); + assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == x - 1); + // https://github.com/noir-lang/noir/issues/1975 + assert(increment(x) == x + 1); + } } -unconstrained fn wrapper(func: fn(u32) -> u32, param: u32) -> u32 { +unconstrained fn wrapper(func: unconstrained fn(u32) -> u32, param: u32) -> u32 { func(param) } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_hash_to_field/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_hash_to_field/src/main.nr index 78759bd84c6..d1ea635d49a 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_hash_to_field/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_hash_to_field/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is hash_to_field in brillig fn main(input: Field) -> pub Field { - hash_to_field(input) + unsafe { + hash_to_field(input) + } } unconstrained fn hash_to_field(input: Field) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_identity_function/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_identity_function/src/main.nr index f41188b1f0d..c2759fe054f 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_identity_function/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_identity_function/src/main.nr @@ -6,17 +6,19 @@ struct myStruct { // // The features being tested is the identity function in Brillig fn main(x: Field) { - assert(x == identity(x)); - // TODO: add support for array comparison - let arr = identity_array([x, x]); - assert(x == arr[0]); - assert(x == arr[1]); + unsafe { + assert(x == identity(x)); + // TODO: add support for array comparison + let arr = identity_array([x, x]); + assert(x == arr[0]); + assert(x == arr[1]); - let s = myStruct { foo: x, foo_arr: [x, x] }; - let identity_struct = identity_struct(s); - assert(x == identity_struct.foo); - assert(x == identity_struct.foo_arr[0]); - assert(x == identity_struct.foo_arr[1]); + let s = myStruct { foo: x, foo_arr: [x, x] }; + let identity_struct = identity_struct(s); + assert(x == identity_struct.foo); + assert(x == identity_struct.foo_arr[0]); + assert(x == identity_struct.foo_arr[1]); + } } unconstrained fn identity(x: Field) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_keccak/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_keccak/src/main.nr index 9150e38f208..dd339208659 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_keccak/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_keccak/src/main.nr @@ -2,21 +2,23 @@ // // The features being tested is keccak256 in brillig fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = keccak256([x as u8], 1); - assert(digest == result); - //#1399: variable message size - let message_size = 4; - let hash_a = keccak256([1, 2, 3, 4], message_size); - let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + unsafe { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = keccak256([x as u8], 1); + assert(digest == result); + //#1399: variable message size + let message_size = 4; + let hash_a = keccak256([1, 2, 3, 4], message_size); + let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - assert(hash_a == hash_b); + assert(hash_a == hash_b); - let message_size_big = 8; - let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + let message_size_big = 8; + let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - assert(hash_a != hash_c); + assert(hash_a != hash_c); + } } unconstrained fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr index 05d35469342..770660bb2a1 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_loop/src/main.nr @@ -2,8 +2,10 @@ // // The features being tested is basic looping on brillig fn main(sum: u32) { - assert(loop(4) == sum); - assert(plain_loop() == sum); + unsafe { + assert(loop(4) == sum); + assert(plain_loop() == sum); + } } unconstrained fn loop(x: u32) -> u32 { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr index 5a5657246a8..77ab4ea19a6 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -28,14 +28,16 @@ unconstrained fn create_and_assert_inside_brillig(x: Field, y: Field) { } fn main(x: Field, y: Field) { - let header = Header { params: [1, 2, 3] }; - let note0 = MyNote { array: [1, 2], plain: 3, header }; - let note1 = MyNote { array: [4, 5], plain: 6, header }; + unsafe { + let header = Header { params: [1, 2, 3] }; + let note0 = MyNote { array: [1, 2], plain: 3, header }; + let note1 = MyNote { array: [4, 5], plain: 6, header }; - assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); - let notes = create_inside_brillig(); - assert_inside_brillig(notes, x, y); - create_and_assert_inside_brillig(x, y); + let notes = create_inside_brillig(); + assert_inside_brillig(notes, x, y); + create_and_assert_inside_brillig(x, y); + } } diff --git a/noir/noir-repo/test_programs/execution_success/brillig_not/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_not/src/main.nr index d34b3edb4b6..557d1e2e31f 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_not/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_not/src/main.nr @@ -2,8 +2,10 @@ // // The features being tested is not instruction on brillig fn main(x: Field, y: Field) { - assert(false == not_operator(x as bool)); - assert(true == not_operator(y as bool)); + unsafe { + assert(false == not_operator(x as bool)); + assert(true == not_operator(y as bool)); + } } unconstrained fn not_operator(x: bool) -> bool { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_oracle/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_oracle/src/main.nr index 0305cb06978..93465b87389 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_oracle/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_oracle/src/main.nr @@ -3,21 +3,23 @@ use std::test::OracleMock; // Tests oracle usage in brillig/unconstrained functions fn main(_x: Field) { - let size = 20; - // TODO: Add a method along the lines of `(0..size).to_array()`. - let mut mock_oracle_response = [0; 20]; - // TODO: Add an `array.reverse()` method. - let mut reversed_mock_oracle_response = [0; 20]; - for i in 0..size { - mock_oracle_response[i] = i; - reversed_mock_oracle_response[19 - i] = i; + unsafe { + let size = 20; + // TODO: Add a method along the lines of `(0..size).to_array()`. + let mut mock_oracle_response = [0; 20]; + // TODO: Add an `array.reverse()` method. + let mut reversed_mock_oracle_response = [0; 20]; + for i in 0..size { + mock_oracle_response[i] = i; + reversed_mock_oracle_response[19 - i] = i; + } + + // TODO: this method of returning a slice feels hacky. + let _ = OracleMock::mock("get_number_sequence").with_params(size).returns((20, mock_oracle_response)); + let _ = OracleMock::mock("get_reverse_number_sequence").with_params(size).returns((20, reversed_mock_oracle_response)); + + get_number_sequence_wrapper(size as Field); } - - // TODO: this method of returning a slice feels hacky. - let _ = OracleMock::mock("get_number_sequence").with_params(size).returns((20, mock_oracle_response)); - let _ = OracleMock::mock("get_reverse_number_sequence").with_params(size).returns((20, reversed_mock_oracle_response)); - - get_number_sequence_wrapper(size as Field); } // Define oracle functions which we have mocked above diff --git a/noir/noir-repo/test_programs/execution_success/brillig_recursion/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_recursion/src/main.nr index a87ef28bc56..c69468013b1 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_recursion/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_recursion/src/main.nr @@ -2,7 +2,9 @@ // // The feature being tested is brillig recursion fn main(x: u32) { - assert(fibonacci(x) == 55); + unsafe { + assert(fibonacci(x) == 55); + } } unconstrained fn fibonacci(x: u32) -> u32 { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_sha256/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_sha256/src/main.nr index fcc01978a0a..5519fb2da64 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_sha256/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_sha256/src/main.nr @@ -2,7 +2,9 @@ // // The features being tested is sha256 in brillig fn main(x: Field, result: [u8; 32]) { - assert(result == sha256(x)); + unsafe { + assert(result == sha256(x)); + } } unconstrained fn sha256(x: Field) -> [u8; 32] { diff --git a/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr index 5ec657b0d35..d4b74162cfb 100644 --- a/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/brillig_unitialised_arrays/src/main.nr @@ -1,6 +1,8 @@ fn main(x: Field, y: Field) -> pub Field { - let notes = create_notes(x, y); - sum_x(notes, x, y) + unsafe { + let notes = create_notes(x, y); + sum_x(notes, x, y) + } } fn sum_x(notes: [Field; 2], x: Field, y: Field) -> Field { diff --git a/noir/noir-repo/test_programs/execution_success/databus/src/main.nr b/noir/noir-repo/test_programs/execution_success/databus/src/main.nr index 1e4aa141eea..ecc7794cf9e 100644 --- a/noir/noir-repo/test_programs/execution_success/databus/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/databus/src/main.nr @@ -1,6 +1,8 @@ fn main(mut x: u32, y: call_data(0) u32, z: call_data(0) [u32; 4]) -> return_data u32 { let a = z[x]; - a + foo(y) + unsafe { + a + foo(y) + } } // Use an unconstrained function to force the compiler to avoid inlining diff --git a/noir/noir-repo/test_programs/execution_success/generics/src/main.nr b/noir/noir-repo/test_programs/execution_success/generics/src/main.nr index f754fb96292..75a7f8a3154 100644 --- a/noir/noir-repo/test_programs/execution_success/generics/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/generics/src/main.nr @@ -34,7 +34,7 @@ impl Bar { impl Bar { // This is to test that we can use turbofish on methods as well fn zeroed(_self: Self) -> A { - std::unsafe::zeroed() + std::mem::zeroed() } } diff --git a/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr b/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr index 52ffe3e823b..966be2741d6 100644 --- a/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/global_consts/src/main.nr @@ -25,7 +25,7 @@ unconstrained fn calculate_global_value() -> Field { } // Regression test for https://github.com/noir-lang/noir/issues/4318 -global CALCULATED_GLOBAL: Field = calculate_global_value(); +global CALCULATED_GLOBAL: Field = unsafe { calculate_global_value() }; fn main( a: [Field; M + N - N], diff --git a/noir/noir-repo/test_programs/execution_success/is_unconstrained/src/main.nr b/noir/noir-repo/test_programs/execution_success/is_unconstrained/src/main.nr index ddafc73c598..d06366cf642 100644 --- a/noir/noir-repo/test_programs/execution_success/is_unconstrained/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/is_unconstrained/src/main.nr @@ -9,6 +9,8 @@ unconstrained fn unconstrained_intermediate() { } fn main() { - unconstrained_intermediate(); + unsafe { + unconstrained_intermediate(); + } check(false); } diff --git a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr index 1bcbd7d5421..9664b4d1ce6 100644 --- a/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/nested_arrays_from_brillig/src/main.nr @@ -20,7 +20,9 @@ unconstrained fn create_inside_brillig(values: [Field; 6]) -> [MyNote; 2] { } fn main(values: [Field; 6]) { - let notes = create_inside_brillig(values); + let notes = unsafe { + create_inside_brillig(values) + }; assert(access_nested(notes) == (2 + 4 + 3 + 1)); } diff --git a/noir/noir-repo/test_programs/execution_success/regression_4124/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_4124/src/main.nr index 6caea017798..7b5060062da 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_4124/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_4124/src/main.nr @@ -11,7 +11,7 @@ impl MyDeserialize<1> for Field { } pub fn storage_read() -> [Field; N] { - std::unsafe::zeroed() + std::mem::zeroed() } struct PublicMutable { diff --git a/noir/noir-repo/test_programs/execution_success/regression_5435/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_5435/src/main.nr index 65f13c5b201..d8aeb76356b 100644 --- a/noir/noir-repo/test_programs/execution_success/regression_5435/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/regression_5435/src/main.nr @@ -3,7 +3,9 @@ fn main(input: Field, enable: bool) { let hash = no_predicate_function(input); // `EnableSideEffects` instruction from above instruction leaks out and removes the predicate from this call, // resulting in execution failure. - fail(hash); + unsafe { + fail(hash) + }; } } diff --git a/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr b/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr index 43bd4433c69..2382f5b0f2f 100644 --- a/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr @@ -327,7 +327,7 @@ fn main() { // // impl Bvec { // fn empty() -> Self { -// Self { inner: [std::unsafe::zeroed(); N], offset: 0, len: 0 } +// Self { inner: [std::mem::zeroed(); N], offset: 0, len: 0 } // } // // fn new(array: [T; N]) -> Self { @@ -559,7 +559,7 @@ fn main() { // } // // fn reverse_array(input: [T; N]) -> [T; N] { -// let mut output = [std::unsafe::zeroed(); N]; +// let mut output = [std::mem::zeroed(); N]; // for i in 0..N { // output[i] = input[N - (i + 1)]; // } @@ -587,7 +587,7 @@ fn main() { // assert_eq(xs, ys); // // // test that pop_front gives all contents, in order, -// // followed by std::unsafe::zeroed() +// // followed by std::mem::zeroed() // println(xs); // let (x, new_xs) = xs.pop_front(); // assert_eq(x, 0); @@ -606,7 +606,7 @@ fn main() { // println(xs); // if xs.len != 0 { // let (x, _new_xs) = xs.pop_front(); -// assert_eq(x, std::unsafe::zeroed()); +// assert_eq(x, std::mem::zeroed()); // } // // assert_eq(new_xs, Bvec { inner: [0, 1, 2], offset: 3, len: 0 }); diff --git a/noir/noir-repo/test_programs/execution_success/u16_support/src/main.nr b/noir/noir-repo/test_programs/execution_success/u16_support/src/main.nr index e8b418f16da..ca41c708077 100644 --- a/noir/noir-repo/test_programs/execution_success/u16_support/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/u16_support/src/main.nr @@ -1,6 +1,8 @@ fn main(x: u16) { test_u16(x); - test_u16_unconstrained(x); + unsafe { + test_u16_unconstrained(x); + } } unconstrained fn test_u16_unconstrained(x: u16) { diff --git a/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr b/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr index 395ed21b6b0..f2ca6813713 100644 --- a/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/uhashmap/src/main.nr @@ -294,7 +294,9 @@ unconstrained fn doc_tests() { // docs:start:get_example fn get_example(map: UHashMap>) { - let x = map.get(12); + let x = unsafe { + map.get(12) + }; if x.is_some() { assert(x.unwrap() == 42); @@ -320,7 +322,9 @@ fn entries_examples(map: UHashMap {value}"); } // docs:end:keys_example diff --git a/noir/noir-repo/test_programs/execution_success/unit_value/src/main.nr b/noir/noir-repo/test_programs/execution_success/unit_value/src/main.nr index a488f267b4c..18c7e5f4f7d 100644 --- a/noir/noir-repo/test_programs/execution_success/unit_value/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/unit_value/src/main.nr @@ -1,5 +1,5 @@ fn get_transaction() { - std::unsafe::zeroed() + std::mem::zeroed() } fn main() { diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml deleted file mode 100644 index 8fce1bf44b6..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "verify_honk_proof" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml deleted file mode 100644 index f2e6bbed8ef..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml +++ /dev/null @@ -1,537 +0,0 @@ -key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" -proof = [ - "0x0000000000000000000000000000000000000000000000000000000000000020", - "0x0000000000000000000000000000000000000000000000000000000000000011", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf", - "0x00000000000000000000000000000000000000000000000b75c020998797da78", - "0x0000000000000000000000000000000000000000000000005a107acb64952eca", - "0x000000000000000000000000000000000000000000000000000031e97a575e9d", - "0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4", - "0x00000000000000000000000000000000000000000000000c410db10a01750aeb", - "0x00000000000000000000000000000000000000000000000d722669117f9758a4", - "0x000000000000000000000000000000000000000000000000000178cbf4206471", - "0x000000000000000000000000000000000000000000000000e91b8a11e7842c38", - "0x000000000000000000000000000000000000000000000007fd51009034b3357f", - "0x000000000000000000000000000000000000000000000009889939f81e9c7402", - "0x0000000000000000000000000000000000000000000000000000f94656a2ca48", - "0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f", - "0x0000000000000000000000000000000000000000000000093fe27776f50224bd", - "0x000000000000000000000000000000000000000000000004a0c80c0da527a081", - "0x0000000000000000000000000000000000000000000000000001b52c2020d746", - "0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632", - "0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc", - "0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62", - "0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c", - "0x000000000000000000000000000000b0804efd6573805f991458295f510a2004", - "0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e", - "0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47", - "0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15", - "0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd", - "0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383", - "0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4", - "0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98", - "0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f", - "0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49", - "0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157", - "0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678", - "0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f", - "0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49", - "0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157", - "0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678", - "0x000000000000000000000000000000f968b227a358a305607f3efc933823d288", - "0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08", - "0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f", - "0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1", - "0x0000000000000000000000000000005b739ed2075f2b046062b8fc6a2d1e9863", - "0x00000000000000000000000000000000001285cd1030d338c0e1603b4da2c838", - "0x00000000000000000000000000000027447d6c281eb38b2b937af4a516d60c04", - "0x000000000000000000000000000000000019bc3d980465fbb4a656a74296fc58", - "0x000000000000000000000000000000b484788ace8f7df86dd5e325d2e9b12599", - "0x00000000000000000000000000000000000a2ca0d10eb7b767114ae230b728d3", - "0x000000000000000000000000000000c6dfc7092f16f95795e437664498b88d53", - "0x0000000000000000000000000000000000131067b4e4d95a4f6f8cf5c9b5450a", - "0x0f413f22eec51f2a02800e0cafaeec1d92d744fbbaef213c687b9edabd6985f5", - "0x21230f4ff26c80ffb5d037a9d1d26c3f955ca34cbeca4f54db6656b932967a0c", - "0x0521f877fe35535767f99597cc50effbd283dcae6812ee0a7620d796ccbfd642", - "0x202b01350a9cc5c20ec0f3eaada338c0a3b793811bd539418ffa3cc4302615e2", - "0x2d1214d9b0d41058ad4a172d9c0aecc5bdabe95e687c3465050c6b5396509be4", - "0x1113b344a151b0af091cb28d728b752ebb4865da6cd7ee68471b961ca5cf69b9", - "0x2aa66d0954bb83e17bd5c9928d3aa7a7df75d741d409f7c15ba596804ba643fb", - "0x2e26bc7a530771ef7a95d5360d537e41cf94d8a0942764ff09881c107f91a106", - "0x0f14f32b921bb63ad1df00adab7c82af58ea8aa7f353f14b281208d8c5fab504", - "0x13429515c0c53b6502bbcdf545defb3cb69a986c9263e070fcbb397391aae1a3", - "0x1f21cac5e2f262afc1006a21454cc6bcb018c44e53ad8ab61cebbac99e539176", - "0x2a9886a6ddc8a61b097c668cd362fc8acdee8dde74f7b1af192c3e060bb2948f", - "0x2d718181e408ead2e9bcd30a84ad1fccbaf8d48ab6d1820bad4933d284b503c4", - "0x2634c1aafc902f14508f34d3d7e9d485f42d1a4c95b5a1ef73711ed0d3c68d77", - "0x092ede9777e6472ce5ffd8c963d466006189e960e2c591d338dc8d4af1a057fb", - "0x1cba45b17fd24f1cb1b4ab7b83eee741f6c77ba70a497dc4de259eceb7d5ea26", - "0x246e887c7bf2e17f919b2393b6e9b00b33e8822d862544a775aac05cb7bff710", - "0x04c3f539fe8689971948afcb437f1ecbd444a5bddaca1c8a450348dcd8480047", - "0x20c6a423ae4fd58e8951aa378d02d77baf90508ceb48856db2319d70938b186e", - "0x1bcf8786b554b3316d8ebdbc9d006a4e5d4865aad512ffd404b7f83550d3d030", - "0x09ab038260518f0970564afcd6bf22e2abf6b1fa5e12a327bbf195b6ca5edd78", - "0x1024e32554746f89c195286ba6ccfc9765e5d14bbe8064bc6fdf22d16ec6b495", - "0x17706656f8dbd7e47bb257a6428f0cb7278ea02fa9e6ce431d7bcc9133fba9c7", - "0x25a3e8a33c15ef2a4dd16313a6049bf1d468b4cdc141f238f2d51a1e8e1c22b3", - "0x1198863f08006edb27aee23164fb117a4ddec1bf1ed89807aa907e5cd24bf068", - "0x1862b4856b5b4d4a064f873e221703e4e2cd1ebfca1337dedca56485c38ed5a0", - "0x062214af1ea6dd6bf8895b92d394571c43970b6f967e1c794624d96071b25ad3", - "0x1e5be9428ddcf1f9b0cbafc28101e792ec5cf73852b0cd0b84fbff71b4490e09", - "0x2d4189bea5b1e30f63c64bd26df82f18bcaf885ec8887b54634b2557869ce87f", - "0x0f2e5d9a908850e9d44925e17d8b12d1adb1ed029799c9b5858598504242bbc0", - "0x3050dc85746a57931d99f3f35e77c2ba561fba0baa018b79ff1fd544026833ae", - "0x2a591a32437e5e0b875a137fd868bd1b6dbc003ff1b661f26e00627cc7c5cf47", - "0x27946841e1670ad9c65717016d0cedf524724217236e81b9fd0a264a36ebfb0e", - "0x0fc396e9d19d6e68e289602e292ee345542d0d28bf6de34fa62cc577cbdfb1df", - "0x08e7433a07a44c0c9c4dd4b273a2685bbd1a91fd5cf2b43409458fab42a23e1b", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x12bd9bfb029c3503a5c6deea87b0a0f11bb9f7ea584af2d48f3e48d7e09247ae", - "0x2ccc4810748c0a82dfc0f063d0b8c7999ffe9474653080e6ef92b3cb7a428784", - "0x08eb574d7fecadadb508c8bd35fdad06b99110609d679763c2e3645229b1b95a", - "0x0f1a65e747c8021ed7c454a4be1e89b1bce66ead9ed980fa98a7a050eafe98a1", - "0x1c8ff9e36684ec71614dee4c17859b06c742089f6029d3694a16e00dac9b57f1", - "0x0303101a8ba712aeca4da85b767ab8d3ecf489ec7d746f8ee20041717cc000e9", - "0x0aaf64c65e7088e5596108c9601467911fea809ca6540d79af77e6e66e36cd99", - "0x17caf164ce74ea7edfb1390e07763d2197797ec26661b92cde18a98d61d2fddc", - "0x18cb055c7ad6d01437725bb457681d81f3ecadc4f35d838a3c13daf25a44456a", - "0x2d78602b8bbcd32b36a99a6e2d248e7fe044ef1b50813133370412f9ef5299f0", - "0x2b139276ea86d426a115479e4154f72a6bd83a6253bf13e9670dc6b4664378f0", - "0x127c7837b384902c39a104036c09546728571c46c8166b1b9b13b3a615ebb781", - "0x05faa4816f83cf0189a482ad943c94b9ec6474002f2b327f8698763ad0ea0985", - "0x2f90359cc30ee693fb3aced96523cf7aebd152c22329eee56a398d9a4ac0628e", - "0x0a71beaf17a59c5a238f04c1f203848d87502c5057a78c13f0cfb0f9876e7714", - "0x2696c1e6d089556adaeb95c8a5e3065b00a393a38c2d69e9bd6ce8cdc49d87da", - "0x1f3d165a7dc6564a036e451eb9cb7f1e1cb1e6d29daa75e3f135ea3e58a79ccd", - "0x1473a660819bdd838d56122b72b32b267211e9f1103239480ec50fa85c9e1035", - "0x0a8ccaeb22451f391b3fc3467c8e6e900270a7afb7b510e8acf5a4f06f1c0888", - "0x03b3080afc0658cc87e307758cebc171921f43eca159b9dedf7f72aa8dd926bd", - "0x2dd7d6663fa0e1755dfafac352c361fcd64c7f4d53627e3646870ac169cc4a07", - "0x1ec54b883f5f35ccad0e75695af20790d9860104095bab34c9bf01628dd40cb9", - "0x193dff50f83c241f7a9e087a29ce72ecf3f6d8563593f786dcd04c32bcfd4ced", - "0x135122c0dae26cda8ca1c09de8225064ad86d10423ab0aaa53b481aa4626e1d6", - "0x08d5a56cbfab5aeed56d3cdd7fb6b30fc26b0c1a5b63fccd7fa44c53ba6fd35a", - "0x0d12f126dfa2daad3726d00ca339284cc22e36c6d81bb7a4b95c6f9598b60e7c", - "0x2e8b24bbdf2fd839d3c7cae1f0eeb96bfcfaeef30b27476f2fafcb17da78cd5e", - "0x2364acfe0cea39b7f749c5f303b99504977357925f810f684c60f35d16315211", - "0x06ca062eb70b8c51cfac35345e7b6b51f33a8ec9ebe204fb9b4911200bf508b7", - "0x266c0aa1ccb97186815bf69084f600d06ddd934e59a38dfe602ee5d6b9487f22", - "0x1d817537a49c6d0e3b4b65c6665334b91d7593142e60065048be9e55ceb5e7ab", - "0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49", - "0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49", - "0x25b77026673a1e613e50df0e88fb510973739d5f9064bd364079a9f884209632", - "0x25c9bc7a3f6aae3d43ff68b5614b34b5eaceff37157b37347995d231784ac1fd", - "0x085f69baef22680ae15f4801ef4361ebe9c7fc24a94b5bc2527dce8fb705439e", - "0x0d7c6b9ce31bfc32238a205455baf5ffe99cd30eb0f7bb5b504e1d4501e01382", - "0x1001a8cc4bc1221c814fba0eddcf3c40619b133373640c600de5bed0a0a05b10", - "0x20f5894be90e52977cb70f4f4cbd5101693db0360848939750db7e91109d54b6", - "0x22c09cb26db43f0599408b4daed0f4f496c66424e6affa41c14387d8e0af851b", - "0x24e5f41357798432426a9549d71e8cc681eaebacbe87f6e3bf38e85de5aa2f3d", - "0x06eb90100c736fbf2b87432d7821ecdc0b365024739bc36363d48b905973f5b9", - "0x000000000000000000000000000000ece6d09ed58e9f5661c01140b10558a8c2", - "0x000000000000000000000000000000000012b6e4f37adcb34b8e88ff8b6eebce", - "0x000000000000000000000000000000b226a2bb93593fa1fab19a44767828a3f5", - "0x00000000000000000000000000000000002b5b518342030543092e1428a7e33c", - "0x00000000000000000000000000000022ba33857034a0574c216eb3c1ddff3025", - "0x00000000000000000000000000000000001918e58df857985a7cf9eae7802165", - "0x00000000000000000000000000000045c2d840b96fb6106cc14dcad89dd5f675", - "0x00000000000000000000000000000000000afdfac1e3a1febdd0208867d44f98", - "0x00000000000000000000000000000042ebed6c5ec45d794f119aef24c192af0f", - "0x00000000000000000000000000000000002d05ef250900bbcc5751bbeb210d6a", - "0x00000000000000000000000000000060d604bdda48eecc90ed065bd9770e1323", - "0x00000000000000000000000000000000001fed91c63d0041660c1cbc84c2ffbb", - "0x00000000000000000000000000000054196b549cde36092e8184c7f4f7d878de", - "0x00000000000000000000000000000000000153f26a01294329922b492485cc31", - "0x00000000000000000000000000000056ebea579d10dbb440f0222931df2c0059", - "0x00000000000000000000000000000000000d2cbc61ce5b7cdd7fce398da4637b", - "0x000000000000000000000000000000e2b9512360b9797d96675d8a2fd2f7aa5d", - "0x000000000000000000000000000000000025742905f105ff895f74e7c3daa34a", - "0x000000000000000000000000000000a2dd7df55db59bd41b83518d4403fbc382", - "0x00000000000000000000000000000000002c1d9c3cbb9371d4cc4e9f900b9a46", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000bcf12ae40c9425c3e67654b84181f90502", - "0x00000000000000000000000000000000000b6d3faa8a71ff6ef1aa887b7307cf", - "0x0000000000000000000000000000001f6f719acc23b8f84808c0275d61cfb456", - "0x0000000000000000000000000000000000296030933ed0c134457ae71c393dfe", - "0x000000000000000000000000000000ebe1a57cdd7d3d763289b40ef5ed9a7ae0", - "0x000000000000000000000000000000000010f30483e7df51fca2316d3367603c", - "0x0000000000000000000000000000000149b7b283ab18060618c8e051864c03cd", - "0x00000000000000000000000000000000001ef7763235a3a25e241a5f06704dc3", -] -public_inputs = [ - "0x0000000000000000000000000000000000000000000000000000000000000003", -] -verification_key = [ - "0x0000000000000000000000000000000000000000000000000000000000000020", - "0x0000000000000000000000000000000000000000000000000000000000000011", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000003", - "0x0000000000000000000000000000000000000000000000000000000000000004", - "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x0000000000000000000000000000000000000000000000000000000000000006", - "0x0000000000000000000000000000000000000000000000000000000000000007", - "0x0000000000000000000000000000000000000000000000000000000000000008", - "0x0000000000000000000000000000000000000000000000000000000000000009", - "0x000000000000000000000000000000000000000000000000000000000000000a", - "0x000000000000000000000000000000000000000000000000000000000000000b", - "0x000000000000000000000000000000000000000000000000000000000000000c", - "0x000000000000000000000000000000000000000000000000000000000000000d", - "0x000000000000000000000000000000000000000000000000000000000000000e", - "0x000000000000000000000000000000000000000000000000000000000000000f", - "0x0000000000000000000000000000000000000000000000000000000000000010", - "0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84", - "0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae", - "0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16", - "0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1", - "0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c", - "0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7", - "0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8", - "0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c", - "0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5", - "0x00000000000000000000000000000000002002681bb417184b2df070a16a3858", - "0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511", - "0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223", - "0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7", - "0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c", - "0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130", - "0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f", - "0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3", - "0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592", - "0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3", - "0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1", - "0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0", - "0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c", - "0x0000000000000000000000000000009f825dde88092070747180d581c342444a", - "0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01", - "0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff", - "0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9", - "0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1", - "0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b", - "0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2", - "0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f", - "0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0", - "0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349", - "0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8", - "0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2", - "0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556", - "0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d", - "0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb", - "0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d", - "0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8", - "0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862", - "0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e", - "0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830", - "0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f", - "0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe", - "0x000000000000000000000000000000231147211b3c75e1f47d150e4bbd2fb22e", - "0x00000000000000000000000000000000000d19ee104a10d3c701cfd87473cbbe", - "0x0000000000000000000000000000006705f3f382637d00f698e2c5c94ed05ae9", - "0x00000000000000000000000000000000000b9c792da28bb60601dd7ce4b74e68", - "0x000000000000000000000000000000ac5acc8cc21e4ddb225c510670f80c80b3", - "0x00000000000000000000000000000000002da9d3fa57343e6998aba19429b9fa", - "0x0000000000000000000000000000004bacbf54b7c17a560df0af18b6d0d527be", - "0x00000000000000000000000000000000000faea33aeca2025b22c288964b21eb", - "0x000000000000000000000000000000492e756298d68d6e95de096055cc0336c3", - "0x00000000000000000000000000000000001a12a12f004859e5a3675c7315121b", - "0x000000000000000000000000000000893d521d512f30e6d32afbbc0cecd8ee00", - "0x00000000000000000000000000000000001674b3c1ef12c6da690631e0d86c04", - "0x000000000000000000000000000000aa6cb02a52e7a613873d4ac9b411349945", - "0x00000000000000000000000000000000001ecb1fe9c493add46751f9940f73e1", - "0x00000000000000000000000000000045b3d362ca82cba69fb2b9c733a5b8c351", - "0x000000000000000000000000000000000019a683586af466e331945b732d2f8c", - "0x000000000000000000000000000000fc79b052dfdfe67c0ecfc06b4267ffd694", - "0x00000000000000000000000000000000001336a70c396393038d5e9913744ac2", - "0x0000000000000000000000000000005450d29af1e9438e91cd33ddeb2548226e", - "0x000000000000000000000000000000000000993a602891cfd0e6f6ecf7404933", - "0x000000000000000000000000000000498efddab90a32e9b2db729ed6e9b40192", - "0x00000000000000000000000000000000002425efebe9628c63ca6fc28bdb5901", - "0x000000000000000000000000000000d8488157f875a21ab5f93f1c2b641f3de9", - "0x0000000000000000000000000000000000290f95ada3936604dc4b14df7504e3", - "0x0000000000000000000000000000005d6902187f3ed60dcce06fca211b40329a", - "0x00000000000000000000000000000000002b5870a6ba0b20aaa0178e5adfbc36", - "0x000000000000000000000000000000e5c2519171fa0e548fc3c4966ffc1ce570", - "0x00000000000000000000000000000000001cb8d8f4793b7debbdc429389dbf2d", - "0x000000000000000000000000000000a3ee22dd60456277b86c32a18982dcb185", - "0x00000000000000000000000000000000002493c99a3d068b03f8f2b8d28b57ce", - "0x000000000000000000000000000000f6c3731486320082c20ec71bbdc92196c1", - "0x00000000000000000000000000000000001ded39c4c8366469843cd63f09ecac", - "0x000000000000000000000000000000494997477ab161763e46601d95844837ef", - "0x00000000000000000000000000000000002e0cddbc5712d79b59cb3b41ebbcdd", - "0x000000000000000000000000000000426db4c64531d350750df62dbbc41a1bd9", - "0x0000000000000000000000000000000000303126892f664d8d505964d14315ec", - "0x00000000000000000000000000000076a6b2c6040c0c62bd59acfe3e3e125672", - "0x000000000000000000000000000000000000874a5ad262eecc6b565e0b085074", - "0x000000000000000000000000000000ef082fb517183c9c6841c2b8ef2ca1df04", - "0x0000000000000000000000000000000000127b2a745a1b74968c3edc18982b9b", - "0x000000000000000000000000000000c9efd4f8c3d56e1eb23d789a8f710d5be6", - "0x000000000000000000000000000000000015a18748490ff4c2b1871081954e86", - "0x000000000000000000000000000000a0011ef987dc016ab110eacd554a1d8bbf", - "0x00000000000000000000000000000000002097c84955059442a95df075833071", - "0x000000000000000000000000000000d38e9426ad3085b68b00a93c17897c2877", - "0x00000000000000000000000000000000002aecd48089890ea0798eb952c66824", - "0x00000000000000000000000000000078d8a9ce405ce559f441f2e71477ff3ddb", - "0x00000000000000000000000000000000001216bdb2f0d961bb8a7a23331d2150", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb", - "0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56", - "0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc", - "0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4", -] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr deleted file mode 100644 index 17adc68c056..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr +++ /dev/null @@ -1,16 +0,0 @@ - -// This circuit aggregates a single Honk proof from `assert_statement_recursive`. -global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 409; -fn main( - verification_key: [Field; 120], - // This is the proof without public inputs attached. - // - // This means: the size of this does not change with the number of public inputs. - proof: [Field; SIZE_OF_PROOF_IF_LOGN_IS_28], - public_inputs: pub [Field; 1], - // This is currently not public. It is fine given that the vk is a part of the circuit definition. - // I believe we want to eventually make it public too though. - key_hash: Field -) { - std::verify_proof(verification_key, proof, public_inputs, key_hash); -} diff --git a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr index 4d3dd8d030b..1b427043e91 100644 --- a/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/mock_oracle/src/main.nr @@ -34,68 +34,84 @@ unconstrained fn struct_field(point: Point, array: [Field; 4]) -> Field { #[test(should_fail)] fn test_mock_no_returns() { - OracleMock::mock("void_field"); - void_field(); // Some return value must be set + unsafe { + OracleMock::mock("void_field"); + void_field(); // Some return value must be set + } } #[test] fn test_mock() { - OracleMock::mock("void_field").returns(10); - assert_eq(void_field(), 10); + unsafe { + OracleMock::mock("void_field").returns(10); + assert_eq(void_field(), 10); + } } #[test] fn test_multiple_mock() { - let first_mock = OracleMock::mock("void_field").returns(10); - OracleMock::mock("void_field").returns(42); + unsafe { + let first_mock = OracleMock::mock("void_field").returns(10); + OracleMock::mock("void_field").returns(42); - // The mocks are searched for in creation order, so the first one prevents the second from being called. - assert_eq(void_field(), 10); + // The mocks are searched for in creation order, so the first one prevents the second from being called. + assert_eq(void_field(), 10); - first_mock.clear(); - assert_eq(void_field(), 42); + first_mock.clear(); + assert_eq(void_field(), 42); + } } #[test] fn test_multiple_mock_times() { - OracleMock::mock("void_field").returns(10).times(2); - OracleMock::mock("void_field").returns(42); + unsafe { + OracleMock::mock("void_field").returns(10).times(2); + OracleMock::mock("void_field").returns(42); - assert_eq(void_field(), 10); - assert_eq(void_field(), 10); - assert_eq(void_field(), 42); + assert_eq(void_field(), 10); + assert_eq(void_field(), 10); + assert_eq(void_field(), 42); + } } #[test] fn test_mock_with_params() { - OracleMock::mock("field_field").with_params((5,)).returns(10); - assert_eq(field_field(5), 10); + unsafe { + OracleMock::mock("field_field").with_params((5,)).returns(10); + assert_eq(field_field(5), 10); + } } #[test] fn test_multiple_mock_with_params() { - OracleMock::mock("field_field").with_params((5,)).returns(10); - OracleMock::mock("field_field").with_params((7,)).returns(14); + unsafe { + OracleMock::mock("field_field").with_params((5,)).returns(10); + OracleMock::mock("field_field").with_params((7,)).returns(14); - assert_eq(field_field(5), 10); - assert_eq(field_field(7), 14); + assert_eq(field_field(5), 10); + assert_eq(field_field(7), 14); + } } #[test] fn test_mock_last_params() { - let mock = OracleMock::mock("field_field").returns(10); - assert_eq(field_field(5), 10); + unsafe { + let mock = OracleMock::mock("field_field").returns(10); + assert_eq(field_field(5), 10); - assert_eq(mock.get_last_params(), 5); + assert_eq(mock.get_last_params(), 5); + } } #[test] fn test_mock_last_params_many_calls() { - let mock = OracleMock::mock("field_field").returns(10); - assert_eq(field_field(5), 10); - assert_eq(field_field(7), 10); + unsafe { + let mock = OracleMock::mock("field_field").returns(10); + assert_eq(field_field(5), 10); + assert_eq(field_field(7), 10); - assert_eq(mock.get_last_params(), 7); + assert_eq(mock.get_last_params(), 7); + } } #[test] @@ -106,25 +122,25 @@ fn test_mock_struct_field() { let another_array = [4, 3, 2, 1]; let point = Point { x: 14, y: 27 }; - OracleMock::mock("struct_field").returns(42).times(2); - let timeless_mock = OracleMock::mock("struct_field").returns(0); - - assert_eq(42, struct_field(point, array)); - assert_eq(42, struct_field(point, array)); - // The times(2) mock is now cleared - - assert_eq(0, struct_field(point, array)); - - let last_params: (Point, [Field; 4]) = timeless_mock.get_last_params(); - assert_eq(last_params.0, point); - assert_eq(last_params.1, array); - - // We clear the mock with no times() to allow other mocks to be callable - timeless_mock.clear(); - - OracleMock::mock("struct_field").with_params((point, array)).returns(10); - OracleMock::mock("struct_field").with_params((point, another_array)).returns(20); - assert_eq(10, struct_field(point, array)); - assert_eq(20, struct_field(point, another_array)); + unsafe { + OracleMock::mock("struct_field").returns(42).times(2); + let timeless_mock = OracleMock::mock("struct_field").returns(0); + assert_eq(42, struct_field(point, array)); + assert_eq(42, struct_field(point, array)); + // The times(2) mock is now cleared + + assert_eq(0, struct_field(point, array)); + let last_params: (Point, [Field; 4]) = timeless_mock.get_last_params(); + assert_eq(last_params.0, point); + assert_eq(last_params.1, array); + + // We clear the mock with no times() to allow other mocks to be callable + timeless_mock.clear(); + + OracleMock::mock("struct_field").with_params((point, array)).returns(10); + OracleMock::mock("struct_field").with_params((point, another_array)).returns(20); + assert_eq(10, struct_field(point, array)); + assert_eq(20, struct_field(point, another_array)); + } } diff --git a/noir/noir-repo/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr index a47ab37eb31..fb90e3d96c5 100644 --- a/noir/noir-repo/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr @@ -13,5 +13,7 @@ fn test_acir() { #[test(should_fail)] fn test_brillig() { - assert_eq(out_of_bounds_unconstrained_wrapper([0; 50], [0; 50]), 0); + unsafe { + assert_eq(out_of_bounds_unconstrained_wrapper([0; 50], [0; 50]), 0); + } } diff --git a/noir/noir-repo/test_programs/noir_test_success/regression_4561/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/regression_4561/src/main.nr index ad40941ff51..6a8b4bd61fb 100644 --- a/noir/noir-repo/test_programs/noir_test_success/regression_4561/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/regression_4561/src/main.nr @@ -12,7 +12,7 @@ unconstrained fn simple_nested_return_unconstrained() -> TReturn { } #[test] -fn test_simple_nested_return() { +unconstrained fn test_simple_nested_return() { OracleMock::mock("simple_nested_return").returns([1, 2, 3, 4, 5, 6]); assert_eq(simple_nested_return_unconstrained(), [[1, 2, 3], [4, 5, 6]]); } @@ -25,7 +25,7 @@ unconstrained fn nested_with_fields_return_unconstrained() -> (Field, TReturn, F } #[test] -fn test_nested_with_fields_return() { +unconstrained fn test_nested_with_fields_return() { OracleMock::mock("nested_with_fields_return").returns((0, [1, 2, 3, 4, 5, 6], 7)); assert_eq(nested_with_fields_return_unconstrained(), (0, [[1, 2, 3], [4, 5, 6]], 7)); } @@ -38,7 +38,7 @@ unconstrained fn two_nested_return_unconstrained() -> (Field, TReturn, Field, TR } #[test] -fn two_nested_return() { +unconstrained fn two_nested_return() { OracleMock::mock("two_nested_return").returns((0, [1, 2, 3, 4, 5, 6], 7, [1, 2, 3, 4, 5, 6])); assert_eq(two_nested_return_unconstrained(), (0, [[1, 2, 3], [4, 5, 6]], 7, [[1, 2, 3], [4, 5, 6]])); } @@ -57,7 +57,7 @@ struct TestTypeFoo { } #[test] -fn complexe_struct_return() { +unconstrained fn complexe_struct_return() { OracleMock::mock("foo_return").returns( ( 0, [1, 2, 3, 4, 5, 6], 7, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [1, 2, 3, 4, 5, 6] diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..ad2082d75a8 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,12 +4,10 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; +use completion_items::{crate_completion_item, simple_completion_item}; use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, -}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ @@ -26,52 +24,22 @@ use noirc_frontend::{ def_map::{CrateDefMap, LocalModuleId, ModuleId}, resolution::path_resolver::{PathResolver, StandardPathResolver}, }, - hir_def::{function::FuncMeta, stmt::HirPattern}, - macros_api::{ModuleDefId, NodeInterner, StructId}, - node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, parser::{Item, ItemKind}, - token::Keyword, - ParsedModule, Type, + ParsedModule, StructType, Type, }; -use strum::IntoEnumIterator; +use sort_text::underscore_sort_text; use crate::{utils, LspState}; use super::process_request; mod builtins; - -/// When finding items in a module, whether to show only direct children or all visible items. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum ModuleCompletionKind { - // Only show a module's direct children. This is used when completing a use statement - // or a path after the first segment. - DirectChildren, - // Show all of a module's visible items. This is used when completing a path outside - // of a use statement (in regular code) when the path is just a single segment: - // we want to find items exposed in the current module. - AllVisibleItems, -} - -/// When suggest a function as a result of completion, whether to autocomplete its name or its name and parameters. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum FunctionCompleteKind { - // Only complete a function's name. This is used in use statement. - Name, - // Complete a function's name and parameters (as a snippet). This is used in regular code. - NameAndParameters, -} - -/// When requesting completions, whether to list all items or just types. -/// For example, when writing `let x: S` we only want to suggest types at this -/// point (modules too, because they might include types too). -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -enum RequestedItems { - // Suggest any items (types, functions, etc.). - AnyItems, - // Only suggest types. - OnlyTypes, -} +mod completion_items; +mod kinds; +mod sort_text; +mod tests; pub(crate) fn on_completion_request( state: &mut LspState, @@ -173,7 +141,16 @@ impl<'a> NodeFinder<'a> { if self.completion_items.is_empty() { None } else { - Some(CompletionResponse::Array(std::mem::take(&mut self.completion_items))) + let mut items = std::mem::take(&mut self.completion_items); + + // Show items that start with underscore last in the list + for item in items.iter_mut() { + if item.label.starts_with('_') { + item.sort_text = Some(underscore_sort_text()); + } + } + + Some(CompletionResponse::Array(items)) } } @@ -432,7 +409,18 @@ impl<'a> NodeFinder<'a> { fn find_in_lvalue(&mut self, lvalue: &LValue) { match lvalue { - LValue::Ident(_) => (), + LValue::Ident(ident) => { + if self.byte == Some(b'.') && ident.span().end() as usize == self.byte_index - 1 { + let location = Location::new(ident.span(), self.file); + if let Some(ReferenceId::Local(definition_id)) = + self.interner.find_referenced(location) + { + let typ = self.interner.definition_type(definition_id); + let prefix = ""; + self.complete_type_fields_and_methods(&typ, prefix); + } + } + } LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), LValue::Index { array, index, span: _ } => { self.find_in_lvalue(array); @@ -513,6 +501,9 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } + noirc_frontend::ast::ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } @@ -520,6 +511,23 @@ impl<'a> NodeFinder<'a> { | noirc_frontend::ast::ExpressionKind::Resolved(_) | noirc_frontend::ast::ExpressionKind::Error => (), } + + // "foo." (no identifier afterwards) is parsed as the expression on the left hand-side of the dot. + // Here we check if there's a dot at the completion position, and if the expression + // ends right before the dot. If so, it means we want to complete the expression's type fields and methods. + // We only do this after visiting nested expressions, because in an expression like `foo & bar.` we want + // to complete for `bar`, not for `foo & bar`. + if self.completion_items.is_empty() + && self.byte == Some(b'.') + && expression.span.end() as usize == self.byte_index - 1 + { + let location = Location::new(expression.span, self.file); + if let Some(typ) = self.interner.type_at_location(location) { + let typ = typ.follow_bindings(); + let prefix = ""; + self.complete_type_fields_and_methods(&typ, prefix); + } + } } fn find_in_literal(&mut self, literal: &Literal) { @@ -572,6 +580,19 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { + let ident = &member_access_expression.rhs; + + if self.byte_index == ident.span().end() as usize { + // Assuming member_access_expression is of the form `foo.bar`, we are right after `bar` + let location = Location::new(member_access_expression.lhs.span, self.file); + if let Some(typ) = self.interner.type_at_location(location) { + let typ = typ.follow_bindings(); + let prefix = ident.to_string(); + self.complete_type_fields_and_methods(&typ, &prefix); + return; + } + } + self.find_in_expression(&member_access_expression.lhs); } @@ -663,7 +684,7 @@ impl<'a> NodeFinder<'a> { noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); @@ -707,19 +728,50 @@ impl<'a> NodeFinder<'a> { } let is_single_segment = !after_colons && idents.is_empty() && path.kind == PathKind::Plain; + let module_id; - let module_id = - if idents.is_empty() { Some(self.module_id) } else { self.resolve_module(idents) }; - let Some(module_id) = module_id else { - return; - }; + if idents.is_empty() { + module_id = self.module_id; + } else { + let Some(module_def_id) = self.resolve_path(idents) else { + return; + }; + + match module_def_id { + ModuleDefId::ModuleId(id) => module_id = id, + ModuleDefId::TypeId(struct_id) => { + let struct_type = self.interner.get_struct(struct_id); + self.complete_type_methods( + &Type::Struct(struct_type, vec![]), + &prefix, + FunctionKind::Any, + ); + return; + } + ModuleDefId::FunctionId(_) => { + // There's nothing inside a function + return; + } + ModuleDefId::TypeAliasId(type_alias_id) => { + let type_alias = self.interner.get_type_alias(type_alias_id); + let type_alias = type_alias.borrow(); + self.complete_type_methods(&type_alias.typ, &prefix, FunctionKind::Any); + return; + } + ModuleDefId::TraitId(_) => { + // For now we don't suggest trait methods + return; + } + ModuleDefId::GlobalId(_) => return, + } + } let module_completion_kind = if after_colons { ModuleCompletionKind::DirectChildren } else { ModuleCompletionKind::AllVisibleItems }; - let function_completion_kind = FunctionCompleteKind::NameAndParameters; + let function_completion_kind = FunctionCompletionKind::NameAndParameters; self.complete_in_module( module_id, @@ -827,7 +879,7 @@ impl<'a> NodeFinder<'a> { } let module_completion_kind = ModuleCompletionKind::DirectChildren; - let function_completion_kind = FunctionCompleteKind::Name; + let function_completion_kind = FunctionCompletionKind::Name; let requested_items = RequestedItems::AnyItems; if after_colons { @@ -913,6 +965,78 @@ impl<'a> NodeFinder<'a> { }; } + fn complete_type_fields_and_methods(&mut self, typ: &Type, prefix: &str) { + match typ { + Type::Struct(struct_type, generics) => { + self.complete_struct_fields(&struct_type.borrow(), generics, prefix); + } + Type::MutableReference(typ) => { + return self.complete_type_fields_and_methods(typ, prefix); + } + Type::Alias(type_alias, _) => { + let type_alias = type_alias.borrow(); + return self.complete_type_fields_and_methods(&type_alias.typ, prefix); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | Type::Tuple(_) + | Type::TypeVariable(_, _) + | Type::TraitAsType(_, _, _) + | Type::NamedGeneric(_, _, _) + | Type::Function(..) + | Type::Forall(_, _) + | Type::Constant(_) + | Type::Quoted(_) + | Type::InfixExpr(_, _, _) + | Type::Error => (), + } + + self.complete_type_methods(typ, prefix, FunctionKind::SelfType(typ)); + } + + fn complete_type_methods(&mut self, typ: &Type, prefix: &str, function_kind: FunctionKind) { + let Some(methods_by_name) = self.interner.get_type_methods(typ) else { + return; + }; + + for (name, methods) in methods_by_name { + for func_id in methods.iter() { + if name_matches(name, prefix) { + if let Some(completion_item) = self.function_completion_item( + func_id, + FunctionCompletionKind::NameAndParameters, + function_kind, + ) { + self.completion_items.push(completion_item); + } + } + } + } + } + + fn complete_struct_fields( + &mut self, + struct_type: &StructType, + generics: &[Type], + prefix: &str, + ) { + for (name, typ) in struct_type.get_fields(generics) { + if name_matches(&name, prefix) { + self.completion_items.push(simple_completion_item( + name, + CompletionItemKind::FIELD, + Some(typ.to_string()), + )); + } + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +1045,7 @@ impl<'a> NodeFinder<'a> { path_kind: PathKind, at_root: bool, module_completion_kind: ModuleCompletionKind, - function_completion_kind: FunctionCompleteKind, + function_completion_kind: FunctionCompletionKind, requested_items: RequestedItems, ) { let def_map = &self.def_maps[&module_id.krate]; @@ -951,6 +1075,8 @@ impl<'a> NodeFinder<'a> { } } + let function_kind = FunctionKind::Any; + let items = match module_completion_kind { ModuleCompletionKind::DirectChildren => module_data.definitions(), ModuleCompletionKind::AllVisibleItems => module_data.scope(), @@ -966,6 +1092,7 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); @@ -977,6 +1104,7 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); @@ -1011,131 +1139,6 @@ impl<'a> NodeFinder<'a> { } } - fn module_def_id_completion_item( - &self, - module_def_id: ModuleDefId, - name: String, - function_completion_kind: FunctionCompleteKind, - requested_items: RequestedItems, - ) -> Option { - match requested_items { - RequestedItems::OnlyTypes => match module_def_id { - ModuleDefId::FunctionId(_) | ModuleDefId::GlobalId(_) => return None, - ModuleDefId::ModuleId(_) - | ModuleDefId::TypeId(_) - | ModuleDefId::TypeAliasId(_) - | ModuleDefId::TraitId(_) => (), - }, - RequestedItems::AnyItems => (), - } - - let completion_item = match module_def_id { - ModuleDefId::ModuleId(_) => module_completion_item(name), - ModuleDefId::FunctionId(func_id) => { - self.function_completion_item(func_id, function_completion_kind) - } - ModuleDefId::TypeId(struct_id) => self.struct_completion_item(struct_id), - ModuleDefId::TypeAliasId(type_alias_id) => { - self.type_alias_completion_item(type_alias_id) - } - ModuleDefId::TraitId(trait_id) => self.trait_completion_item(trait_id), - ModuleDefId::GlobalId(global_id) => self.global_completion_item(global_id), - }; - Some(completion_item) - } - - fn function_completion_item( - &self, - func_id: FuncId, - function_completion_kind: FunctionCompleteKind, - ) -> CompletionItem { - let func_meta = self.interner.function_meta(&func_id); - let name = self.interner.function_name(&func_id).to_string(); - - let mut typ = &func_meta.typ; - if let Type::Forall(_, typ_) = typ { - typ = typ_; - } - let description = typ.to_string(); - let description = description.strip_suffix(" -> ()").unwrap_or(&description).to_string(); - - match function_completion_kind { - FunctionCompleteKind::Name => { - simple_completion_item(name, CompletionItemKind::FUNCTION, Some(description)) - } - FunctionCompleteKind::NameAndParameters => { - let label = format!("{}(…)", name); - let kind = CompletionItemKind::FUNCTION; - let insert_text = self.compute_function_insert_text(func_meta, &name); - - snippet_completion_item(label, kind, insert_text, Some(description)) - } - } - } - - fn compute_function_insert_text(&self, func_meta: &FuncMeta, name: &str) -> String { - let mut text = String::new(); - text.push_str(name); - text.push('('); - for (index, (pattern, _, _)) in func_meta.parameters.0.iter().enumerate() { - if index > 0 { - text.push_str(", "); - } - - text.push_str("${"); - text.push_str(&(index + 1).to_string()); - text.push(':'); - self.hir_pattern_to_argument(pattern, &mut text); - text.push('}'); - } - text.push(')'); - text - } - - fn hir_pattern_to_argument(&self, pattern: &HirPattern, text: &mut String) { - match pattern { - HirPattern::Identifier(hir_ident) => { - text.push_str(self.interner.definition_name(hir_ident.id)); - } - HirPattern::Mutable(pattern, _) => self.hir_pattern_to_argument(pattern, text), - HirPattern::Tuple(_, _) | HirPattern::Struct(_, _, _) => text.push('_'), - } - } - - fn struct_completion_item(&self, struct_id: StructId) -> CompletionItem { - let struct_type = self.interner.get_struct(struct_id); - let struct_type = struct_type.borrow(); - let name = struct_type.name.to_string(); - - simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) - } - - fn type_alias_completion_item(&self, type_alias_id: TypeAliasId) -> CompletionItem { - let type_alias = self.interner.get_type_alias(type_alias_id); - let type_alias = type_alias.borrow(); - let name = type_alias.name.to_string(); - - simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) - } - - fn trait_completion_item(&self, trait_id: TraitId) -> CompletionItem { - let trait_ = self.interner.get_trait(trait_id); - let name = trait_.name.to_string(); - - simple_completion_item(name.clone(), CompletionItemKind::INTERFACE, Some(name)) - } - - fn global_completion_item(&self, global_id: GlobalId) -> CompletionItem { - let global_definition = self.interner.get_global_definition(global_id); - let name = global_definition.name.clone(); - - let global = self.interner.get_global(global_id); - let typ = self.interner.definition_type(global.definition_id); - let description = typ.to_string(); - - simple_completion_item(name, CompletionItemKind::CONSTANT, Some(description)) - } - fn resolve_module(&self, segments: Vec) -> Option { if let Some(ModuleDefId::ModuleId(module_id)) = self.resolve_path(segments) { Some(module_id) @@ -1145,65 +1148,25 @@ impl<'a> NodeFinder<'a> { } fn resolve_path(&self, segments: Vec) -> Option { + let last_segment = segments.last().unwrap().clone(); + let path_segments = segments.into_iter().map(PathSegment::from).collect(); let path = Path { segments: path_segments, kind: PathKind::Plain, span: Span::default() }; let path_resolver = StandardPathResolver::new(self.root_module_id); - match path_resolver.resolve(self.def_maps, path, &mut None) { - Ok(path_resolution) => Some(path_resolution.module_def_id), - Err(_) => None, - } - } - - fn builtin_functions_completion(&mut self, prefix: &str) { - for keyword in Keyword::iter() { - if let Some(func) = keyword_builtin_function(&keyword) { - if name_matches(func.name, prefix) { - self.completion_items.push(snippet_completion_item( - format!("{}(…)", func.name), - CompletionItemKind::FUNCTION, - format!("{}({})", func.name, func.parameters), - Some(func.description.to_string()), - )); - } - } + if let Ok(path_resolution) = path_resolver.resolve(self.def_maps, path, &mut None) { + return Some(path_resolution.module_def_id); } - } - - fn builtin_values_completion(&mut self, prefix: &str) { - for keyword in ["false", "true"] { - if name_matches(keyword, prefix) { - self.completion_items.push(simple_completion_item( - keyword, - CompletionItemKind::KEYWORD, - Some("bool".to_string()), - )); - } - } - } - fn builtin_types_completion(&mut self, prefix: &str) { - for keyword in Keyword::iter() { - if let Some(typ) = keyword_builtin_type(&keyword) { - if name_matches(typ, prefix) { - self.completion_items.push(simple_completion_item( - typ, - CompletionItemKind::STRUCT, - Some(typ.to_string()), - )); - } + // If we can't resolve a path trough lookup, let's see if the last segment is bound to a type + let location = Location::new(last_segment.span(), self.file); + if let Some(reference_id) = self.interner.find_referenced(location) { + if let Some(id) = module_def_id_from_reference_id(reference_id) { + return Some(id); } } - for typ in builtin_integer_types() { - if name_matches(typ, prefix) { - self.completion_items.push(simple_completion_item( - typ, - CompletionItemKind::STRUCT, - Some(typ.to_string()), - )); - } - } + None } fn includes_span(&self, span: Span) -> bool { @@ -1215,990 +1178,16 @@ fn name_matches(name: &str, prefix: &str) -> bool { name.starts_with(prefix) } -fn module_completion_item(name: impl Into) -> CompletionItem { - simple_completion_item(name, CompletionItemKind::MODULE, None) -} - -fn crate_completion_item(name: impl Into) -> CompletionItem { - simple_completion_item(name, CompletionItemKind::MODULE, None) -} - -fn simple_completion_item( - label: impl Into, - kind: CompletionItemKind, - description: Option, -) -> CompletionItem { - CompletionItem { - label: label.into(), - label_details: Some(CompletionItemLabelDetails { detail: None, description }), - kind: Some(kind), - detail: None, - documentation: None, - deprecated: None, - preselect: None, - sort_text: None, - filter_text: None, - insert_text: None, - insert_text_format: None, - insert_text_mode: None, - text_edit: None, - additional_text_edits: None, - command: None, - commit_characters: None, - data: None, - tags: None, - } -} - -fn snippet_completion_item( - label: impl Into, - kind: CompletionItemKind, - insert_text: impl Into, - description: Option, -) -> CompletionItem { - CompletionItem { - label: label.into(), - label_details: Some(CompletionItemLabelDetails { detail: None, description }), - kind: Some(kind), - insert_text_format: Some(InsertTextFormat::SNIPPET), - insert_text: Some(insert_text.into()), - detail: None, - documentation: None, - deprecated: None, - preselect: None, - sort_text: None, - filter_text: None, - insert_text_mode: None, - text_edit: None, - additional_text_edits: None, - command: None, - commit_characters: None, - data: None, - tags: None, - } -} - -#[cfg(test)] -mod completion_tests { - use crate::{notifications::on_did_open_text_document, test_utils}; - - use super::*; - use lsp_types::{ - DidOpenTextDocumentParams, PartialResultParams, Position, TextDocumentIdentifier, - TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, - }; - use tokio::test; - - async fn assert_completion(src: &str, expected: Vec) { - let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; - - let (line, column) = src - .lines() - .enumerate() - .filter_map(|(line_index, line)| { - line.find(">|<").map(|char_index| (line_index, char_index)) - }) - .next() - .expect("Expected to find one >|< in the source code"); - - let src = src.replace(">|<", ""); - - on_did_open_text_document( - &mut state, - DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: noir_text_document.clone(), - language_id: "noir".to_string(), - version: 0, - text: src.to_string(), - }, - }, - ); - - // Get inlay hints. These should now be relative to the changed text, - // not the saved file's text. - let response = on_completion_request( - &mut state, - CompletionParams { - text_document_position: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { uri: noir_text_document }, - position: Position { line: line as u32, character: column as u32 }, - }, - work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, - partial_result_params: PartialResultParams { partial_result_token: None }, - context: None, - }, - ) - .await - .expect("Could not execute on_completion_request") - .unwrap(); - - let CompletionResponse::Array(items) = response else { - panic!("Expected response to be CompletionResponse::Array"); - }; - - let mut items = items.clone(); - items.sort_by_key(|item| item.label.clone()); - - let mut expected = expected.clone(); - expected.sort_by_key(|item| item.label.clone()); - - if items != expected { - println!( - "Items: {:?}", - items.iter().map(|item| item.label.clone()).collect::>() - ); - println!( - "Expected: {:?}", - expected.iter().map(|item| item.label.clone()).collect::>() - ); - } - - assert_eq!(items, expected); - } - - #[test] - async fn test_use_first_segment() { - let src = r#" - mod foo {} - mod foobar {} - use f>|< - "#; - - assert_completion( - src, - vec![module_completion_item("foo"), module_completion_item("foobar")], - ) - .await; - } - - #[test] - async fn test_use_second_segment() { - let src = r#" - mod foo { - mod bar {} - mod baz {} - } - use foo::>|< - "#; - - assert_completion(src, vec![module_completion_item("bar"), module_completion_item("baz")]) - .await; - } - - #[test] - async fn test_use_second_segment_after_typing() { - let src = r#" - mod foo { - mod bar {} - mod brave {} - } - use foo::ba>|< - "#; - - assert_completion(src, vec![module_completion_item("bar")]).await; - } - - #[test] - async fn test_use_struct() { - let src = r#" - mod foo { - struct Foo {} - } - use foo::>|< - "#; - - assert_completion( - src, - vec![simple_completion_item( - "Foo", - CompletionItemKind::STRUCT, - Some("Foo".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_use_function() { - let src = r#" - mod foo { - fn bar(x: i32) -> u64 { 0 } - } - use foo::>|< - "#; - - assert_completion( - src, - vec![simple_completion_item( - "bar", - CompletionItemKind::FUNCTION, - Some("fn(i32) -> u64".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_use_after_crate_and_letter() { - // Prove that "std" shows up - let src = r#" - use s>|< - "#; - assert_completion(src, vec![crate_completion_item("std")]).await; - - // "std" doesn't show up anymore because of the "crate::" prefix - let src = r#" - mod something {} - use crate::s>|< - "#; - assert_completion(src, vec![module_completion_item("something")]).await; - } - - #[test] - async fn test_use_suggests_hardcoded_crate() { - let src = r#" - use c>|< - "#; - - assert_completion( - src, - vec![simple_completion_item("crate::", CompletionItemKind::KEYWORD, None)], - ) - .await; - } - - #[test] - async fn test_use_in_tree_after_letter() { - let src = r#" - mod foo { - mod bar {} - } - use foo::{b>|<} - "#; - - assert_completion(src, vec![module_completion_item("bar")]).await; - } - - #[test] - async fn test_use_in_tree_after_colons() { - let src = r#" - mod foo { - mod bar { - mod baz {} - } - } - use foo::{bar::>|<} - "#; - - assert_completion(src, vec![module_completion_item("baz")]).await; - } - - #[test] - async fn test_use_in_tree_after_colons_after_another_segment() { - let src = r#" - mod foo { - mod bar {} - mod qux {} - } - use foo::{bar, q>|<} - "#; - - assert_completion(src, vec![module_completion_item("qux")]).await; - } - - #[test] - async fn test_use_in_nested_module() { - let src = r#" - mod foo { - mod something {} - - use s>|< - } - "#; - - assert_completion( - src, - vec![ - module_completion_item("something"), - crate_completion_item("std"), - simple_completion_item("super::", CompletionItemKind::KEYWORD, None), - ], - ) - .await; - } - - #[test] - async fn test_use_after_super() { - let src = r#" - mod foo {} - - mod bar { - mod something {} - - use super::f>|< - } - "#; - - assert_completion(src, vec![module_completion_item("foo")]).await; - } - - #[test] - async fn test_use_after_crate_and_letter_nested_in_module() { - let src = r#" - mod something { - mod something_else {} - use crate::s>|< - } - - "#; - assert_completion(src, vec![module_completion_item("something")]).await; - } - - #[test] - async fn test_use_after_crate_segment_and_letter_nested_in_module() { - let src = r#" - mod something { - mod something_else {} - use crate::something::s>|< - } - - "#; - assert_completion(src, vec![module_completion_item("something_else")]).await; - } - - #[test] - async fn test_complete_path_shows_module() { - let src = r#" - mod foobar {} - - fn main() { - fo>|< - } - "#; - assert_completion(src, vec![module_completion_item("foobar")]).await; - } - - #[test] - async fn test_complete_path_after_colons_shows_submodule() { - let src = r#" - mod foo { - mod bar {} - } - - fn main() { - foo::>|< - } - "#; - assert_completion(src, vec![module_completion_item("bar")]).await; - } - - #[test] - async fn test_complete_path_after_colons_and_letter_shows_submodule() { - let src = r#" - mod foo { - mod bar {} - } - - fn main() { - foo::b>|< - } - "#; - assert_completion(src, vec![module_completion_item("bar")]).await; - } - - #[test] - async fn test_complete_path_with_local_variable() { - let src = r#" - fn main() { - let local = 1; - l>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "local", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_path_with_shadowed_local_variable() { - let src = r#" - fn main() { - let local = 1; - let local = true; - l>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "local", - CompletionItemKind::VARIABLE, - Some("bool".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_path_with_function_argument() { - let src = r#" - fn main(local: Field) { - l>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "local", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_function() { - let src = r#" - fn hello(x: i32, y: Field) { } - - fn main() { - h>|< - } - "#; - assert_completion( - src, - vec![snippet_completion_item( - "hello(…)", - CompletionItemKind::FUNCTION, - "hello(${1:x}, ${2:y})", - Some("fn(i32, Field)".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_builtin_functions() { - let src = r#" - fn main() { - a>|< - } - "#; - assert_completion( - src, - vec![ - snippet_completion_item( - "assert(…)", - CompletionItemKind::FUNCTION, - "assert(${1:predicate})", - Some("fn(T)".to_string()), - ), - snippet_completion_item( - "assert_constant(…)", - CompletionItemKind::FUNCTION, - "assert_constant(${1:x})", - Some("fn(T)".to_string()), - ), - snippet_completion_item( - "assert_eq(…)", - CompletionItemKind::FUNCTION, - "assert_eq(${1:lhs}, ${2:rhs})", - Some("fn(T, T)".to_string()), - ), - ], - ) - .await; - } - - #[test] - async fn test_complete_path_in_impl() { - let src = r#" - struct SomeStruct {} - - impl SomeStruct { - fn foo() { - S>|< - } - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "SomeStruct", - CompletionItemKind::STRUCT, - Some("SomeStruct".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_path_in_trait_impl() { - let src = r#" - struct SomeStruct {} - trait Trait {} - - impl Trait for SomeStruct { - fn foo() { - S>|< - } - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "SomeStruct", - CompletionItemKind::STRUCT, - Some("SomeStruct".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_path_with_for_argument() { - let src = r#" - fn main() { - for index in 0..10 { - i>|< - } - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "index", - CompletionItemKind::VARIABLE, - Some("u32".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_complete_path_with_lambda_argument() { - let src = r#" - fn lambda(f: fn(i32)) { } - - fn main() { - lambda(|var| v>|<) - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "var", - CompletionItemKind::VARIABLE, - Some("_".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_struct_field_type() { - let src = r#" - struct Something {} - - fn SomeFunction() {} - - struct Another { - some: So>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_function_parameter() { - let src = r#" - struct Something {} - - fn foo(x: So>|<) {} - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_function_return_type() { - let src = r#" - struct Something {} - - fn foo() -> So>|< {} - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_type_alias() { - let src = r#" - struct Something {} - - type Foo = So>|< - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_trait_function() { - let src = r#" - struct Something {} - - trait Trait { - fn foo(s: So>|<); - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_trait_function_return_type() { - let src = r#" - struct Something {} - - trait Trait { - fn foo() -> So>|<; - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_let_type() { - let src = r#" - struct Something {} - - fn main() { - let x: So>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_type_in_lambda_parameter() { - let src = r#" - struct Something {} - - fn main() { - foo(|s: So>|<| s) - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "Something", - CompletionItemKind::STRUCT, - Some("Something".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_builtin_types() { - let src = r#" - fn foo(x: i>|<) {} - "#; - assert_completion( - src, - vec![ - simple_completion_item("i8", CompletionItemKind::STRUCT, Some("i8".to_string())), - simple_completion_item("i16", CompletionItemKind::STRUCT, Some("i16".to_string())), - simple_completion_item("i32", CompletionItemKind::STRUCT, Some("i32".to_string())), - simple_completion_item("i64", CompletionItemKind::STRUCT, Some("i64".to_string())), - ], - ) - .await; - } - - #[test] - async fn test_suggest_true() { - let src = r#" - fn main() { - let x = t>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "true", - CompletionItemKind::KEYWORD, - Some("bool".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_regarding_if_scope() { - let src = r#" - fn main() { - let good = 1; - if true { - let great = 2; - g>|< - } else { - let greater = 3; - } - } - "#; - assert_completion( - src, - vec![ - simple_completion_item( - "good", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - ), - simple_completion_item( - "great", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - ), - ], - ) - .await; - - let src = r#" - fn main() { - let good = 1; - if true { - let great = 2; - } else { - let greater = 3; - g>|< - } - } - "#; - assert_completion( - src, - vec![ - simple_completion_item( - "good", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - ), - simple_completion_item( - "greater", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - ), - ], - ) - .await; - - let src = r#" - fn main() { - let good = 1; - if true { - let great = 2; - } else { - let greater = 3; - } - g>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "good", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_regarding_block_scope() { - let src = r#" - fn main() { - let good = 1; - { - let great = 2; - g>|< - } - } - "#; - assert_completion( - src, - vec![ - simple_completion_item( - "good", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - ), - simple_completion_item( - "great", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - ), - ], - ) - .await; - - let src = r#" - fn main() { - let good = 1; - { - let great = 2; - } - g>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item( - "good", - CompletionItemKind::VARIABLE, - Some("Field".to_string()), - )], - ) - .await; - } - - #[test] - async fn test_suggest_struct_type_parameter() { - let src = r#" - struct Foo { - context: C>|< - } - "#; - assert_completion( - src, - vec![simple_completion_item("Context", CompletionItemKind::TYPE_PARAMETER, None)], - ) - .await; - } - - #[test] - async fn test_suggest_impl_type_parameter() { - let src = r#" - struct Foo {} - - impl Foo { - fn foo() { - let x: TypeP>|< - } - } - "#; - assert_completion( - src, - vec![simple_completion_item("TypeParam", CompletionItemKind::TYPE_PARAMETER, None)], - ) - .await; - } - - #[test] - async fn test_suggest_trait_impl_type_parameter() { - let src = r#" - struct Foo {} - trait Trait {} - - impl Trait for Foo { - fn foo() { - let x: TypeP>|< - } - } - "#; - assert_completion( - src, - vec![simple_completion_item("TypeParam", CompletionItemKind::TYPE_PARAMETER, None)], - ) - .await; - } - - #[test] - async fn test_suggest_trait_function_type_parameter() { - let src = r#" - struct Foo {} - trait Trait { - fn foo() { - let x: TypeP>|< - } - } - "#; - assert_completion( - src, - vec![simple_completion_item("TypeParam", CompletionItemKind::TYPE_PARAMETER, None)], - ) - .await; - } - - #[test] - async fn test_suggest_function_type_parameters() { - let src = r#" - fn foo(x: C>|<) {} - "#; - assert_completion( - src, - vec![simple_completion_item("Context", CompletionItemKind::TYPE_PARAMETER, None)], - ) - .await; +fn module_def_id_from_reference_id(reference_id: ReferenceId) -> Option { + match reference_id { + ReferenceId::Module(module_id) => Some(ModuleDefId::ModuleId(module_id)), + ReferenceId::Struct(struct_id) => Some(ModuleDefId::TypeId(struct_id)), + ReferenceId::Trait(trait_id) => Some(ModuleDefId::TraitId(trait_id)), + ReferenceId::Function(func_id) => Some(ModuleDefId::FunctionId(func_id)), + ReferenceId::Alias(type_alias_id) => Some(ModuleDefId::TypeAliasId(type_alias_id)), + ReferenceId::StructMember(_, _) + | ReferenceId::Global(_) + | ReferenceId::Local(_) + | ReferenceId::Reference(_, _) => None, } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs index 070be109f13..75eba7fb3c7 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -1,4 +1,64 @@ +use lsp_types::CompletionItemKind; use noirc_frontend::token::Keyword; +use strum::IntoEnumIterator; + +use super::{ + completion_items::{simple_completion_item, snippet_completion_item}, + name_matches, NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn builtin_functions_completion(&mut self, prefix: &str) { + for keyword in Keyword::iter() { + if let Some(func) = keyword_builtin_function(&keyword) { + if name_matches(func.name, prefix) { + self.completion_items.push(snippet_completion_item( + format!("{}(…)", func.name), + CompletionItemKind::FUNCTION, + format!("{}({})", func.name, func.parameters), + Some(func.description.to_string()), + )); + } + } + } + } + + pub(super) fn builtin_values_completion(&mut self, prefix: &str) { + for keyword in ["false", "true"] { + if name_matches(keyword, prefix) { + self.completion_items.push(simple_completion_item( + keyword, + CompletionItemKind::KEYWORD, + Some("bool".to_string()), + )); + } + } + } + + pub(super) fn builtin_types_completion(&mut self, prefix: &str) { + for keyword in Keyword::iter() { + if let Some(typ) = keyword_builtin_type(&keyword) { + if name_matches(typ, prefix) { + self.completion_items.push(simple_completion_item( + typ, + CompletionItemKind::STRUCT, + Some(typ.to_string()), + )); + } + } + } + + for typ in builtin_integer_types() { + if name_matches(typ, prefix) { + self.completion_items.push(simple_completion_item( + typ, + CompletionItemKind::STRUCT, + Some(typ.to_string()), + )); + } + } + } +} pub(super) fn builtin_integer_types() -> [&'static str; 8] { ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"] @@ -14,6 +74,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { Keyword::StructDefinition => Some("StructDefinition"), Keyword::TraitConstraint => Some("TraitConstraint"), Keyword::TraitDefinition => Some("TraitDefinition"), + Keyword::TraitImpl => Some("TraitImpl"), Keyword::TypeType => Some("Type"), Keyword::As @@ -52,6 +113,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { | Keyword::Type | Keyword::Unchecked | Keyword::Unconstrained + | Keyword::Unsafe | Keyword::Use | Keyword::Where | Keyword::While => None, @@ -116,10 +178,12 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option None, diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs new file mode 100644 index 00000000000..db08dc9054a --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,344 @@ +use lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails, InsertTextFormat}; +use noirc_frontend::{ + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::{ModuleDefId, StructId}, + node_interner::{FuncId, GlobalId, TraitId, TypeAliasId}, + Type, +}; + +use super::{ + sort_text::{default_sort_text, new_sort_text, operator_sort_text, self_mismatch_sort_text}, + FunctionCompletionKind, FunctionKind, NodeFinder, RequestedItems, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn module_def_id_completion_item( + &self, + module_def_id: ModuleDefId, + name: String, + function_completion_kind: FunctionCompletionKind, + function_kind: FunctionKind, + requested_items: RequestedItems, + ) -> Option { + match requested_items { + RequestedItems::OnlyTypes => match module_def_id { + ModuleDefId::FunctionId(_) | ModuleDefId::GlobalId(_) => return None, + ModuleDefId::ModuleId(_) + | ModuleDefId::TypeId(_) + | ModuleDefId::TypeAliasId(_) + | ModuleDefId::TraitId(_) => (), + }, + RequestedItems::AnyItems => (), + } + + match module_def_id { + ModuleDefId::ModuleId(_) => Some(module_completion_item(name)), + ModuleDefId::FunctionId(func_id) => { + self.function_completion_item(func_id, function_completion_kind, function_kind) + } + ModuleDefId::TypeId(struct_id) => Some(self.struct_completion_item(struct_id)), + ModuleDefId::TypeAliasId(type_alias_id) => { + Some(self.type_alias_completion_item(type_alias_id)) + } + ModuleDefId::TraitId(trait_id) => Some(self.trait_completion_item(trait_id)), + ModuleDefId::GlobalId(global_id) => Some(self.global_completion_item(global_id)), + } + } + + fn struct_completion_item(&self, struct_id: StructId) -> CompletionItem { + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); + let name = struct_type.name.to_string(); + + simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) + } + + fn type_alias_completion_item(&self, type_alias_id: TypeAliasId) -> CompletionItem { + let type_alias = self.interner.get_type_alias(type_alias_id); + let type_alias = type_alias.borrow(); + let name = type_alias.name.to_string(); + + simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) + } + + fn trait_completion_item(&self, trait_id: TraitId) -> CompletionItem { + let trait_ = self.interner.get_trait(trait_id); + let name = trait_.name.to_string(); + + simple_completion_item(name.clone(), CompletionItemKind::INTERFACE, Some(name)) + } + + fn global_completion_item(&self, global_id: GlobalId) -> CompletionItem { + let global_definition = self.interner.get_global_definition(global_id); + let name = global_definition.name.clone(); + + let global = self.interner.get_global(global_id); + let typ = self.interner.definition_type(global.definition_id); + let description = typ.to_string(); + + simple_completion_item(name, CompletionItemKind::CONSTANT, Some(description)) + } + + pub(super) fn function_completion_item( + &self, + func_id: FuncId, + function_completion_kind: FunctionCompletionKind, + function_kind: FunctionKind, + ) -> Option { + let func_meta = self.interner.function_meta(&func_id); + let name = &self.interner.function_name(&func_id).to_string(); + + let func_self_type = if let Some((pattern, typ, _)) = func_meta.parameters.0.get(0) { + if self.hir_pattern_is_self_type(pattern) { + if let Type::MutableReference(mut_typ) = typ { + let typ: &Type = mut_typ; + Some(typ) + } else { + Some(typ) + } + } else { + None + } + } else { + None + }; + + match function_kind { + FunctionKind::Any => (), + FunctionKind::SelfType(mut self_type) => { + if let Some(func_self_type) = func_self_type { + if matches!(self_type, Type::Integer(..)) + || matches!(self_type, Type::FieldElement) + { + // Check that the pattern type is the same as self type. + // We do this because some types (only Field and integer types) + // have their methods in the same HashMap. + + if let Type::MutableReference(mut_typ) = self_type { + self_type = mut_typ; + } + + if self_type != func_self_type { + return None; + } + } + } else { + return None; + } + } + } + + let is_operator = if let Some(trait_impl_id) = &func_meta.trait_impl { + let trait_impl = self.interner.get_trait_implementation(*trait_impl_id); + let trait_impl = trait_impl.borrow(); + self.interner.is_operator_trait(trait_impl.trait_id) + } else { + false + }; + let description = func_meta_type_to_string(func_meta, func_self_type.is_some()); + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => { + simple_completion_item(name, CompletionItemKind::FUNCTION, Some(description)) + } + FunctionCompletionKind::NameAndParameters => { + let kind = CompletionItemKind::FUNCTION; + let insert_text = self.compute_function_insert_text(func_meta, name, function_kind); + let label = if insert_text.ends_with("()") { + format!("{}()", name) + } else { + format!("{}(…)", name) + }; + + snippet_completion_item(label, kind, insert_text, Some(description)) + } + }; + + let completion_item = if is_operator { + completion_item_with_sort_text(completion_item, operator_sort_text()) + } else if function_kind == FunctionKind::Any && name == "new" { + completion_item_with_sort_text(completion_item, new_sort_text()) + } else if function_kind == FunctionKind::Any && func_self_type.is_some() { + completion_item_with_sort_text(completion_item, self_mismatch_sort_text()) + } else { + completion_item + }; + + Some(completion_item) + } + + fn compute_function_insert_text( + &self, + func_meta: &FuncMeta, + name: &str, + function_kind: FunctionKind, + ) -> String { + let mut text = String::new(); + text.push_str(name); + text.push('('); + + let mut index = 1; + for (pattern, _, _) in &func_meta.parameters.0 { + if index == 1 { + match function_kind { + FunctionKind::SelfType(_) => { + if self.hir_pattern_is_self_type(pattern) { + continue; + } + } + FunctionKind::Any => (), + } + } + + if index > 1 { + text.push_str(", "); + } + + text.push_str("${"); + text.push_str(&index.to_string()); + text.push(':'); + self.hir_pattern_to_argument(pattern, &mut text); + text.push('}'); + + index += 1; + } + text.push(')'); + text + } + + fn hir_pattern_to_argument(&self, pattern: &HirPattern, text: &mut String) { + match pattern { + HirPattern::Identifier(hir_ident) => { + text.push_str(self.interner.definition_name(hir_ident.id)); + } + HirPattern::Mutable(pattern, _) => self.hir_pattern_to_argument(pattern, text), + HirPattern::Tuple(_, _) | HirPattern::Struct(_, _, _) => text.push('_'), + } + } + + fn hir_pattern_is_self_type(&self, pattern: &HirPattern) -> bool { + match pattern { + HirPattern::Identifier(hir_ident) => { + let name = self.interner.definition_name(hir_ident.id); + name == "self" || name == "_self" + } + HirPattern::Mutable(pattern, _) => self.hir_pattern_is_self_type(pattern), + HirPattern::Tuple(_, _) | HirPattern::Struct(_, _, _) => false, + } + } +} + +pub(super) fn module_completion_item(name: impl Into) -> CompletionItem { + simple_completion_item(name, CompletionItemKind::MODULE, None) +} + +pub(super) fn crate_completion_item(name: impl Into) -> CompletionItem { + simple_completion_item(name, CompletionItemKind::MODULE, None) +} + +fn func_meta_type_to_string(func_meta: &FuncMeta, has_self_type: bool) -> String { + let mut typ = &func_meta.typ; + if let Type::Forall(_, typ_) = typ { + typ = typ_; + } + + if let Type::Function(args, ret, _env, unconstrained) = typ { + let mut string = String::new(); + if *unconstrained { + string.push_str("unconstrained "); + } + string.push_str("fn("); + for (index, arg) in args.iter().enumerate() { + if index > 0 { + string.push_str(", "); + } + if index == 0 && has_self_type { + type_to_self_string(arg, &mut string); + } else { + string.push_str(&arg.to_string()); + } + } + string.push(')'); + + let ret: &Type = ret; + if let Type::Unit = ret { + // Nothing + } else { + string.push_str(" -> "); + string.push_str(&ret.to_string()); + } + string + } else { + typ.to_string() + } +} + +fn type_to_self_string(typ: &Type, string: &mut String) { + if let Type::MutableReference(..) = typ { + string.push_str("&mut self"); + } else { + string.push_str("self"); + } +} + +pub(super) fn simple_completion_item( + label: impl Into, + kind: CompletionItemKind, + description: Option, +) -> CompletionItem { + CompletionItem { + label: label.into(), + label_details: Some(CompletionItemLabelDetails { detail: None, description }), + kind: Some(kind), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: Some(default_sort_text()), + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + } +} + +pub(super) fn snippet_completion_item( + label: impl Into, + kind: CompletionItemKind, + insert_text: impl Into, + description: Option, +) -> CompletionItem { + CompletionItem { + label: label.into(), + label_details: Some(CompletionItemLabelDetails { detail: None, description }), + kind: Some(kind), + insert_text_format: Some(InsertTextFormat::SNIPPET), + insert_text: Some(insert_text.into()), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: Some(default_sort_text()), + filter_text: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + } +} + +pub(super) fn completion_item_with_sort_text( + completion_item: CompletionItem, + sort_text: String, +) -> CompletionItem { + CompletionItem { sort_text: Some(sort_text), ..completion_item } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs new file mode 100644 index 00000000000..e01fcfc8c56 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs @@ -0,0 +1,42 @@ +use noirc_frontend::Type; + +/// When finding items in a module, whether to show only direct children or all visible items. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum ModuleCompletionKind { + // Only show a module's direct children. This is used when completing a use statement + // or a path after the first segment. + DirectChildren, + // Show all of a module's visible items. This is used when completing a path outside + // of a use statement (in regular code) when the path is just a single segment: + // we want to find items exposed in the current module. + AllVisibleItems, +} + +/// When suggest a function as a result of completion, whether to autocomplete its name or its name and parameters. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum FunctionCompletionKind { + // Only complete a function's name. This is used in use statement. + Name, + // Complete a function's name and parameters (as a snippet). This is used in regular code. + NameAndParameters, +} + +/// Is there a requirement for suggesting functions? +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum FunctionKind<'a> { + /// No requirement: any function is okay to suggest. + Any, + /// Only show functions that have the given self type. + SelfType(&'a Type), +} + +/// When requesting completions, whether to list all items or just types. +/// For example, when writing `let x: S` we only want to suggest types at this +/// point (modules too, because they might include types too). +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(super) enum RequestedItems { + // Suggest any items (types, functions, etc.). + AnyItems, + // Only suggest types. + OnlyTypes, +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs new file mode 100644 index 00000000000..4a8aebd7988 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -0,0 +1,28 @@ +/// Sort text for "new" methods: we want these to show up before anything else, +/// if we are completing at something like `Foo::` +pub(super) fn new_sort_text() -> String { + "3".to_string() +} + +/// This is the default sort text. +pub(super) fn default_sort_text() -> String { + "5".to_string() +} + +/// When completing something like `Foo::`, we want to show methods that take +/// self after the other ones. +pub(super) fn self_mismatch_sort_text() -> String { + "7".to_string() +} + +/// We want to show operator methods last. +pub(super) fn operator_sort_text() -> String { + "8".to_string() +} + +/// If a name begins with underscore it's likely something that's meant to +/// be private (but visibility doesn't exist everywhere yet, so for now +/// we assume that) +pub(super) fn underscore_sort_text() -> String { + "9".to_string() +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs new file mode 100644 index 00000000000..1c89cc09413 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1351 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, crate_completion_item, module_completion_item, + simple_completion_item, snippet_completion_item, + }, + sort_text::self_mismatch_sort_text, + }, + on_completion_request, + }, + test_utils, + }; + + use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, + DidOpenTextDocumentParams, PartialResultParams, Position, TextDocumentIdentifier, + TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, + }; + use tokio::test; + + async fn assert_completion(src: &str, expected: Vec) { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .filter_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .next() + .expect("Expected to find one >|< in the source code"); + + let src = src.replace(">|<", ""); + + on_did_open_text_document( + &mut state, + DidOpenTextDocumentParams { + text_document: TextDocumentItem { + uri: noir_text_document.clone(), + language_id: "noir".to_string(), + version: 0, + text: src.to_string(), + }, + }, + ); + + // Get inlay hints. These should now be relative to the changed text, + // not the saved file's text. + let response = on_completion_request( + &mut state, + CompletionParams { + text_document_position: TextDocumentPositionParams { + text_document: TextDocumentIdentifier { uri: noir_text_document }, + position: Position { line: line as u32, character: column as u32 }, + }, + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + partial_result_params: PartialResultParams { partial_result_token: None }, + context: None, + }, + ) + .await + .expect("Could not execute on_completion_request") + .unwrap(); + + let CompletionResponse::Array(items) = response else { + panic!("Expected response to be CompletionResponse::Array"); + }; + + let mut items = items.clone(); + items.sort_by_key(|item| item.label.clone()); + + let mut expected = expected.clone(); + expected.sort_by_key(|item| item.label.clone()); + + if items != expected { + println!( + "Items: {:?}", + items.iter().map(|item| item.label.clone()).collect::>() + ); + println!( + "Expected: {:?}", + expected.iter().map(|item| item.label.clone()).collect::>() + ); + } + + assert_eq!(items, expected); + } + + #[test] + async fn test_use_first_segment() { + let src = r#" + mod foo {} + mod foobar {} + use f>|< + "#; + + assert_completion( + src, + vec![module_completion_item("foo"), module_completion_item("foobar")], + ) + .await; + } + + #[test] + async fn test_use_second_segment() { + let src = r#" + mod foo { + mod bar {} + mod baz {} + } + use foo::>|< + "#; + + assert_completion(src, vec![module_completion_item("bar"), module_completion_item("baz")]) + .await; + } + + #[test] + async fn test_use_second_segment_after_typing() { + let src = r#" + mod foo { + mod bar {} + mod brave {} + } + use foo::ba>|< + "#; + + assert_completion(src, vec![module_completion_item("bar")]).await; + } + + #[test] + async fn test_use_struct() { + let src = r#" + mod foo { + struct Foo {} + } + use foo::>|< + "#; + + assert_completion( + src, + vec![simple_completion_item( + "Foo", + CompletionItemKind::STRUCT, + Some("Foo".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_use_function() { + let src = r#" + mod foo { + fn bar(x: i32) -> u64 { 0 } + } + use foo::>|< + "#; + + assert_completion( + src, + vec![simple_completion_item( + "bar", + CompletionItemKind::FUNCTION, + Some("fn(i32) -> u64".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_use_after_crate_and_letter() { + // Prove that "std" shows up + let src = r#" + use s>|< + "#; + assert_completion(src, vec![crate_completion_item("std")]).await; + + // "std" doesn't show up anymore because of the "crate::" prefix + let src = r#" + mod something {} + use crate::s>|< + "#; + assert_completion(src, vec![module_completion_item("something")]).await; + } + + #[test] + async fn test_use_suggests_hardcoded_crate() { + let src = r#" + use c>|< + "#; + + assert_completion( + src, + vec![simple_completion_item("crate::", CompletionItemKind::KEYWORD, None)], + ) + .await; + } + + #[test] + async fn test_use_in_tree_after_letter() { + let src = r#" + mod foo { + mod bar {} + } + use foo::{b>|<} + "#; + + assert_completion(src, vec![module_completion_item("bar")]).await; + } + + #[test] + async fn test_use_in_tree_after_colons() { + let src = r#" + mod foo { + mod bar { + mod baz {} + } + } + use foo::{bar::>|<} + "#; + + assert_completion(src, vec![module_completion_item("baz")]).await; + } + + #[test] + async fn test_use_in_tree_after_colons_after_another_segment() { + let src = r#" + mod foo { + mod bar {} + mod qux {} + } + use foo::{bar, q>|<} + "#; + + assert_completion(src, vec![module_completion_item("qux")]).await; + } + + #[test] + async fn test_use_in_nested_module() { + let src = r#" + mod foo { + mod something {} + + use s>|< + } + "#; + + assert_completion( + src, + vec![ + module_completion_item("something"), + crate_completion_item("std"), + simple_completion_item("super::", CompletionItemKind::KEYWORD, None), + ], + ) + .await; + } + + #[test] + async fn test_use_after_super() { + let src = r#" + mod foo {} + + mod bar { + mod something {} + + use super::f>|< + } + "#; + + assert_completion(src, vec![module_completion_item("foo")]).await; + } + + #[test] + async fn test_use_after_crate_and_letter_nested_in_module() { + let src = r#" + mod something { + mod something_else {} + use crate::s>|< + } + + "#; + assert_completion(src, vec![module_completion_item("something")]).await; + } + + #[test] + async fn test_use_after_crate_segment_and_letter_nested_in_module() { + let src = r#" + mod something { + mod something_else {} + use crate::something::s>|< + } + + "#; + assert_completion(src, vec![module_completion_item("something_else")]).await; + } + + #[test] + async fn test_complete_path_shows_module() { + let src = r#" + mod foobar {} + + fn main() { + fo>|< + } + "#; + assert_completion(src, vec![module_completion_item("foobar")]).await; + } + + #[test] + async fn test_complete_path_after_colons_shows_submodule() { + let src = r#" + mod foo { + mod bar {} + } + + fn main() { + foo::>|< + } + "#; + assert_completion(src, vec![module_completion_item("bar")]).await; + } + + #[test] + async fn test_complete_path_after_colons_and_letter_shows_submodule() { + let src = r#" + mod foo { + mod bar {} + } + + fn main() { + foo::b>|< + } + "#; + assert_completion(src, vec![module_completion_item("bar")]).await; + } + + #[test] + async fn test_complete_path_with_local_variable() { + let src = r#" + fn main() { + let local = 1; + l>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "local", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_path_with_shadowed_local_variable() { + let src = r#" + fn main() { + let local = 1; + let local = true; + l>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "local", + CompletionItemKind::VARIABLE, + Some("bool".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_path_with_function_argument() { + let src = r#" + fn main(local: Field) { + l>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "local", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_function_without_arguments() { + let src = r#" + fn hello() { } + + fn main() { + h>|< + } + "#; + assert_completion( + src, + vec![snippet_completion_item( + "hello()", + CompletionItemKind::FUNCTION, + "hello()", + Some("fn()".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion( + src, + vec![snippet_completion_item( + "hello(…)", + CompletionItemKind::FUNCTION, + "hello(${1:x}, ${2:y})", + Some("fn(i32, Field)".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_builtin_functions() { + let src = r#" + fn main() { + a>|< + } + "#; + assert_completion( + src, + vec![ + snippet_completion_item( + "assert(…)", + CompletionItemKind::FUNCTION, + "assert(${1:predicate})", + Some("fn(T)".to_string()), + ), + snippet_completion_item( + "assert_constant(…)", + CompletionItemKind::FUNCTION, + "assert_constant(${1:x})", + Some("fn(T)".to_string()), + ), + snippet_completion_item( + "assert_eq(…)", + CompletionItemKind::FUNCTION, + "assert_eq(${1:lhs}, ${2:rhs})", + Some("fn(T, T)".to_string()), + ), + ], + ) + .await; + } + + #[test] + async fn test_complete_path_in_impl() { + let src = r#" + struct SomeStruct {} + + impl SomeStruct { + fn foo() { + S>|< + } + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "SomeStruct", + CompletionItemKind::STRUCT, + Some("SomeStruct".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_path_in_trait_impl() { + let src = r#" + struct SomeStruct {} + trait Trait {} + + impl Trait for SomeStruct { + fn foo() { + S>|< + } + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "SomeStruct", + CompletionItemKind::STRUCT, + Some("SomeStruct".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_path_with_for_argument() { + let src = r#" + fn main() { + for index in 0..10 { + i>|< + } + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "index", + CompletionItemKind::VARIABLE, + Some("u32".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_complete_path_with_lambda_argument() { + let src = r#" + fn lambda(f: fn(i32)) { } + + fn main() { + lambda(|var| v>|<) + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "var", + CompletionItemKind::VARIABLE, + Some("_".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_struct_field_type() { + let src = r#" + struct Something {} + + fn SomeFunction() {} + + struct Another { + some: So>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_function_parameter() { + let src = r#" + struct Something {} + + fn foo(x: So>|<) {} + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_function_return_type() { + let src = r#" + struct Something {} + + fn foo() -> So>|< {} + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_type_alias() { + let src = r#" + struct Something {} + + type Foo = So>|< + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_trait_function() { + let src = r#" + struct Something {} + + trait Trait { + fn foo(s: So>|<); + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_trait_function_return_type() { + let src = r#" + struct Something {} + + trait Trait { + fn foo() -> So>|<; + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_let_type() { + let src = r#" + struct Something {} + + fn main() { + let x: So>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_type_in_lambda_parameter() { + let src = r#" + struct Something {} + + fn main() { + foo(|s: So>|<| s) + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "Something", + CompletionItemKind::STRUCT, + Some("Something".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_builtin_types() { + let src = r#" + fn foo(x: i>|<) {} + "#; + assert_completion( + src, + vec![ + simple_completion_item("i8", CompletionItemKind::STRUCT, Some("i8".to_string())), + simple_completion_item("i16", CompletionItemKind::STRUCT, Some("i16".to_string())), + simple_completion_item("i32", CompletionItemKind::STRUCT, Some("i32".to_string())), + simple_completion_item("i64", CompletionItemKind::STRUCT, Some("i64".to_string())), + ], + ) + .await; + } + + #[test] + async fn test_suggest_true() { + let src = r#" + fn main() { + let x = t>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "true", + CompletionItemKind::KEYWORD, + Some("bool".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_regarding_if_scope() { + let src = r#" + fn main() { + let good = 1; + if true { + let great = 2; + g>|< + } else { + let greater = 3; + } + } + "#; + assert_completion( + src, + vec![ + simple_completion_item( + "good", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + ), + simple_completion_item( + "great", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + ), + ], + ) + .await; + + let src = r#" + fn main() { + let good = 1; + if true { + let great = 2; + } else { + let greater = 3; + g>|< + } + } + "#; + assert_completion( + src, + vec![ + simple_completion_item( + "good", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + ), + simple_completion_item( + "greater", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + ), + ], + ) + .await; + + let src = r#" + fn main() { + let good = 1; + if true { + let great = 2; + } else { + let greater = 3; + } + g>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "good", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_regarding_block_scope() { + let src = r#" + fn main() { + let good = 1; + { + let great = 2; + g>|< + } + } + "#; + assert_completion( + src, + vec![ + simple_completion_item( + "good", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + ), + simple_completion_item( + "great", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + ), + ], + ) + .await; + + let src = r#" + fn main() { + let good = 1; + { + let great = 2; + } + g>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "good", + CompletionItemKind::VARIABLE, + Some("Field".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggest_struct_type_parameter() { + let src = r#" + struct Foo { + context: C>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item("Context", CompletionItemKind::TYPE_PARAMETER, None)], + ) + .await; + } + + #[test] + async fn test_suggest_impl_type_parameter() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo() { + let x: TypeP>|< + } + } + "#; + assert_completion( + src, + vec![simple_completion_item("TypeParam", CompletionItemKind::TYPE_PARAMETER, None)], + ) + .await; + } + + #[test] + async fn test_suggest_trait_impl_type_parameter() { + let src = r#" + struct Foo {} + trait Trait {} + + impl Trait for Foo { + fn foo() { + let x: TypeP>|< + } + } + "#; + assert_completion( + src, + vec![simple_completion_item("TypeParam", CompletionItemKind::TYPE_PARAMETER, None)], + ) + .await; + } + + #[test] + async fn test_suggest_trait_function_type_parameter() { + let src = r#" + struct Foo {} + trait Trait { + fn foo() { + let x: TypeP>|< + } + } + "#; + assert_completion( + src, + vec![simple_completion_item("TypeParam", CompletionItemKind::TYPE_PARAMETER, None)], + ) + .await; + } + + #[test] + async fn test_suggest_function_type_parameters() { + let src = r#" + fn foo(x: C>|<) {} + "#; + assert_completion( + src, + vec![simple_completion_item("Context", CompletionItemKind::TYPE_PARAMETER, None)], + ) + .await; + } + + #[test] + async fn test_suggests_struct_field_after_dot_and_letter() { + let src = r#" + struct Some { + property: i32, + } + + fn foo(s: Some) { + s.p>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "property", + CompletionItemKind::FIELD, + Some("i32".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggests_struct_field_after_dot_and_letter_for_generic_type() { + let src = r#" + struct Some { + property: T, + } + + fn foo(s: Some) { + s.p>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "property", + CompletionItemKind::FIELD, + Some("i32".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggests_struct_field_after_dot_followed_by_brace() { + let src = r#" + struct Some { + property: i32, + } + + fn foo(s: Some) { + s.>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item( + "property", + CompletionItemKind::FIELD, + Some("i32".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggests_struct_field_after_dot_chain() { + let src = r#" + struct Some { + property: Other, + } + + struct Other { + bar: i32, + } + + fn foo(some: Some) { + some.property.>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item("bar", CompletionItemKind::FIELD, Some("i32".to_string()))], + ) + .await; + } + + #[test] + async fn test_suggests_struct_impl_method() { + let src = r#" + struct Some { + } + + impl Some { + fn foobar(self, x: i32) {} + fn foobar2(&mut self, x: i32) {} + fn foobar3(y: i32) {} + } + + fn foo(some: Some) { + some.f>|< + } + "#; + assert_completion( + src, + vec![ + snippet_completion_item( + "foobar(…)", + CompletionItemKind::FUNCTION, + "foobar(${1:x})", + Some("fn(self, i32)".to_string()), + ), + snippet_completion_item( + "foobar2(…)", + CompletionItemKind::FUNCTION, + "foobar2(${1:x})", + Some("fn(&mut self, i32)".to_string()), + ), + ], + ) + .await; + } + + #[test] + async fn test_suggests_struct_trait_impl_method() { + let src = r#" + struct Some { + } + + trait SomeTrait { + fn foobar(self, x: i32); + fn foobar2(y: i32); + } + + impl SomeTrait for Some { + fn foobar(self, x: i32) {} + fn foobar2(y: i32) {} + } + + fn foo(some: Some) { + some.f>|< + } + "#; + assert_completion( + src, + vec![snippet_completion_item( + "foobar(…)", + CompletionItemKind::FUNCTION, + "foobar(${1:x})", + Some("fn(self, i32)".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggests_primitive_trait_impl_method() { + let src = r#" + trait SomeTrait { + fn foobar(self, x: i32); + fn foobar2(y: i32); + } + + impl SomeTrait for Field { + fn foobar(self, x: i32) {} + fn foobar2(y: i32) {} + } + + fn foo(field: Field) { + field.f>|< + } + "#; + assert_completion( + src, + vec![snippet_completion_item( + "foobar(…)", + CompletionItemKind::FUNCTION, + "foobar(${1:x})", + Some("fn(self, i32)".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggests_struct_methods_after_colons() { + let src = r#" + struct Some { + } + + impl Some { + fn foobar(self, x: i32) {} + fn foobar2(&mut self, x: i32) {} + fn foobar3(y: i32) {} + } + + fn foo() { + Some::>|< + } + "#; + assert_completion( + src, + vec![ + completion_item_with_sort_text( + snippet_completion_item( + "foobar(…)", + CompletionItemKind::FUNCTION, + "foobar(${1:self}, ${2:x})", + Some("fn(self, i32)".to_string()), + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + snippet_completion_item( + "foobar2(…)", + CompletionItemKind::FUNCTION, + "foobar2(${1:self}, ${2:x})", + Some("fn(&mut self, i32)".to_string()), + ), + self_mismatch_sort_text(), + ), + snippet_completion_item( + "foobar3(…)", + CompletionItemKind::FUNCTION, + "foobar3(${1:y})", + Some("fn(i32)".to_string()), + ), + ], + ) + .await; + } + + #[test] + async fn test_suggests_struct_behind_alias_methods_after_dot() { + let src = r#" + struct Some { + } + + type Alias = Some; + + impl Some { + fn foobar(self, x: i32) {} + } + + fn foo(some: Alias) { + some.>|< + } + "#; + assert_completion( + src, + vec![snippet_completion_item( + "foobar(…)", + CompletionItemKind::FUNCTION, + "foobar(${1:x})", + Some("fn(self, i32)".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_suggests_struct_behind_alias_methods_after_colons() { + let src = r#" + struct Some { + } + + type Alias = Some; + + impl Some { + fn foobar(self, x: i32) {} + fn foobar2(&mut self, x: i32) {} + fn foobar3(y: i32) {} + } + + fn foo() { + Alias::>|< + } + "#; + assert_completion( + src, + vec![ + completion_item_with_sort_text( + snippet_completion_item( + "foobar(…)", + CompletionItemKind::FUNCTION, + "foobar(${1:self}, ${2:x})", + Some("fn(self, i32)".to_string()), + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + snippet_completion_item( + "foobar2(…)", + CompletionItemKind::FUNCTION, + "foobar2(${1:self}, ${2:x})", + Some("fn(&mut self, i32)".to_string()), + ), + self_mismatch_sort_text(), + ), + snippet_completion_item( + "foobar3(…)", + CompletionItemKind::FUNCTION, + "foobar3(${1:y})", + Some("fn(i32)".to_string()), + ), + ], + ) + .await; + } + + #[test] + async fn test_completes_in_broken_if_after_dot() { + let src = r#" + struct Some { + foo: i32, + } + + fn foo(s: Some) { + if s.>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item("foo", CompletionItemKind::FIELD, Some("i32".to_string()))], + ) + .await; + } + + #[test] + async fn test_completes_in_nested_expression() { + let src = r#" + struct Foo { bar: Bar } + struct Bar { baz: i32 } + + fn foo(f: Foo) { + f.bar & f.>|< + } + "#; + assert_completion( + src, + vec![simple_completion_item("bar", CompletionItemKind::FIELD, Some("Bar".to_string()))], + ) + .await; + } + + #[test] + async fn test_completes_in_call_chain() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self) -> Foo { self } + } + + fn foo(f: Foo) { + f.foo().>|< + } + "#; + assert_completion( + src, + vec![snippet_completion_item( + "foo()", + CompletionItemKind::FUNCTION, + "foo()", + Some("fn(self) -> Foo".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_completes_when_assignment_follows() { + let src = r#" + struct Foo { + bar: i32, + } + + fn foo(f: Foo) { + let mut x = 1; + + f.>|< + + x = 2; + } + "#; + assert_completion( + src, + vec![simple_completion_item("bar", CompletionItemKind::FIELD, Some("i32".to_string()))], + ) + .await; + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index b6fdc6f7842..aa97def8274 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -431,7 +431,7 @@ impl<'a> TypeLinksGatherer<'a> { Type::NamedGeneric(var, _, _) => { self.gather_type_variable_links(var); } - Type::Function(args, return_type, env) => { + Type::Function(args, return_type, env, _) => { for arg in args { self.gather_type_links(arg); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs index 8c3d8a05652..8dd63963b5d 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -293,6 +293,9 @@ impl<'a> InlayHintCollector<'a> { ExpressionKind::Comptime(block_expression, _span) => { self.collect_in_block_expression(block_expression); } + ExpressionKind::Unsafe(block_expression, _span) => { + self.collect_in_block_expression(block_expression); + } ExpressionKind::AsTraitPath(path) => { self.collect_in_ident(&path.impl_item, true); } @@ -589,7 +592,11 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File parts.push(string_part(">")); } } - Type::Function(args, return_type, _env) => { + Type::Function(args, return_type, _env, unconstrained) => { + if *unconstrained { + parts.push(string_part("unconstrained ")); + } + parts.push(string_part("fn(")); for (index, arg) in args.iter().enumerate() { push_type_parts(arg, parts, files); @@ -686,6 +693,7 @@ fn get_expression_name(expression: &Expression) -> Option { | ExpressionKind::Comptime(..) | ExpressionKind::Resolved(..) | ExpressionKind::Literal(..) + | ExpressionKind::Unsafe(..) | ExpressionKind::Error => None, } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index e138f839600..b6c26110e59 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -234,7 +234,7 @@ pub(crate) fn on_initialize( )), completion_provider: Some(lsp_types::OneOf::Right(lsp_types::CompletionOptions { resolve_provider: None, - trigger_characters: Some(vec![":".to_string()]), + trigger_characters: Some(vec![".".to_string(), ":".to_string()]), all_commit_characters: None, work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs index 41b15069546..4fee7d3e197 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs @@ -168,6 +168,9 @@ pub(crate) fn rewrite( ExpressionKind::Comptime(block, block_span) => { format!("comptime {}", rewrite_block(visitor, block, block_span)) } + ExpressionKind::Unsafe(block, block_span) => { + format!("unsafe {}", rewrite_block(visitor, block, block_span)) + } ExpressionKind::Error => unreachable!(), ExpressionKind::Resolved(_) => { unreachable!("ExpressionKind::Resolved should only emitted by the comptime interpreter") diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs index b586f32a6fe..86cc618eb33 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs @@ -37,7 +37,9 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) format!("({types})") } } - UnresolvedTypeData::Function(args, return_type, env) => { + UnresolvedTypeData::Function(args, return_type, env, unconstrained) => { + let unconstrained = if unconstrained { "unconstrained " } else { "" }; + let env = if span_is_empty(env.span.unwrap()) { "".into() } else { @@ -53,7 +55,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) let return_type = rewrite(visitor, _shape, *return_type); - format!("fn{env}({args}) -> {return_type}") + format!("{unconstrained}fn{env}({args}) -> {return_type}") } UnresolvedTypeData::Resolved(_) => { unreachable!("Unexpected macro expansion of a type in nargo fmt input") diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr index 4dde9a1b3ec..73248d0c04f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr @@ -69,3 +69,5 @@ fn id_two(x: [Field; I]) -> [Field; I] {} fn whitespace_before_generics(foo: T) {} fn more_whitespace_before_generics(foo: T) {} + +fn with_unconstrained(x: unconstrained fn() -> ()) {} diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr new file mode 100644 index 00000000000..7d733c203de --- /dev/null +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr @@ -0,0 +1,8 @@ +fn main(x: pub u8, y: u8) { + unsafe {} + + unsafe { + assert_eq(x, y); + } +} + diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr index 16ed95a540d..8db6022808f 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr @@ -54,3 +54,5 @@ fn whitespace_before_generics < T > (foo: T) {} fn more_whitespace_before_generics < T > (foo: T) {} + +fn with_unconstrained(x: unconstrained fn() -> ()) {} diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr b/noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr new file mode 100644 index 00000000000..6e12ef975ee --- /dev/null +++ b/noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr @@ -0,0 +1,8 @@ +fn main(x: pub u8, y: u8) { + unsafe { } + + unsafe { + assert_eq(x, y); + } +} + diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index a5593cc284c..661d71fb9c3 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -41,7 +41,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "portal:../../../../barretenberg/ts", + "@aztec/bb.js": "0.48.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index c07d2d8a4c1..16fb26e55db 100755 --- a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh +++ b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index f77e9f7e72e..f10b5b2cb67 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,18 +221,19 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" +"@aztec/bb.js@npm:0.48.0": + version: 0.48.0 + resolution: "@aztec/bb.js@npm:0.48.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: ./dest/node/main.js + bb.js: dest/node/main.js + checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 languageName: node - linkType: soft + linkType: hard "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4160,7 +4161,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.48.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3