From 9aca22f62596dbb16c3c7f14f50fcc13e49b2f0e Mon Sep 17 00:00:00 2001 From: AztecBot Date: Thu, 15 Aug 2024 08:02:11 +0000 Subject: [PATCH 01/21] [1 changes] chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 ++++- .../src/ssa/ssa_gen/context.rs | 2 +- .../noirc_frontend/src/ast/expression.rs | 2 + .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../src/elaborator/expressions.rs | 21 +- .../noirc_frontend/src/elaborator/mod.rs | 9 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 60 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 1 + .../src/hir/comptime/interpreter/builtin.rs | 68 +- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/type_check/errors.rs | 5 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 121 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 99 +- .../noirc_frontend/src/parser/errors.rs | 4 + .../noirc_frontend/src/parser/parser.rs | 99 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 203 +++ .../compiler/noirc_printable_type/src/lib.rs | 1 + .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 4 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/mod.nr | 5 +- .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_fmt_strings/src/main.nr | 2 +- .../comptime_trait_impl/Nargo.toml | 7 + .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 4 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ------- .../verify_honk_proof/src/main.nr | 16 - .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- .../tooling/lsp/src/requests/completion.rs | 1417 +++-------------- .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 344 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 28 + .../lsp/src/requests/completion/tests.rs | 1351 ++++++++++++++++ .../tooling/lsp/src/requests/hover.rs | 2 +- .../tooling/lsp/src/requests/inlay_hint.rs | 10 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 2 +- .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 +- 154 files changed, 3906 insertions(+), 2405 deletions(-) rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr 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 From 9346baa580344387019b27cefdf998c9b7a5e453 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Thu, 15 Aug 2024 08:03:05 +0000 Subject: [PATCH 02/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From c1e585ebe38b6aaac2e6dda34db0f33540deede5 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Fri, 16 Aug 2024 08:02:13 +0000 Subject: [PATCH 03/21] [1 changes] feat: suggest trait methods in LSP completion (https://github.com/noir-lang/noir/pull/5735) feat: LSP autocomplete constructor fields (https://github.com/noir-lang/noir/pull/5732) feat: add `Expr::as_unary` (https://github.com/noir-lang/noir/pull/5731) chore: count brillig opcodes in nargo info (https://github.com/noir-lang/noir/pull/5189) feat: suggest tuple fields in LSP completion (https://github.com/noir-lang/noir/pull/5730) feat: add `Expr::as_bool` (https://github.com/noir-lang/noir/pull/5729) feat: add `Expr` methods: `as_tuple`, `as_parenthesized`, `as_index`, `as_if` (https://github.com/noir-lang/noir/pull/5726) feat: LSP signature help (https://github.com/noir-lang/noir/pull/5725) chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 4 +- .../compiler/noirc_driver/src/program.rs | 2 + .../src/brillig/brillig_gen.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 2 + .../src/brillig/brillig_ir/artifact.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 9 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 27 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 +++- .../src/ssa/ssa_gen/context.rs | 2 +- .../noirc_frontend/src/ast/expression.rs | 2 + .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../src/elaborator/expressions.rs | 21 +- .../noirc_frontend/src/elaborator/mod.rs | 9 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 60 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 3 + .../src/hir/comptime/interpreter/builtin.rs | 202 +- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/type_check/errors.rs | 5 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 121 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 99 +- .../noirc_frontend/src/parser/errors.rs | 4 + .../noirc_frontend/src/parser/parser.rs | 99 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 203 ++ .../compiler/noirc_printable_type/src/lib.rs | 1 + .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 4 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/expr.nr | 16 + noir/noir-repo/noir_stdlib/src/meta/mod.nr | 6 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 21 + .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_exp/src/main.nr | 47 + .../comptime_fmt_strings/src/main.nr | 2 +- .../comptime_trait_impl/Nargo.toml | 7 + .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 4 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ------ .../verify_honk_proof/src/main.nr | 16 - .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- noir/noir-repo/tooling/lsp/src/lib.rs | 5 +- .../tooling/lsp/src/requests/completion.rs | 1699 +++-------------- .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 382 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 28 + .../lsp/src/requests/completion/tests.rs | 1326 +++++++++++++ .../lsp/src/requests/completion/traversal.rs | 132 ++ .../tooling/lsp/src/requests/hover.rs | 2 +- .../tooling/lsp/src/requests/inlay_hint.rs | 10 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 15 +- .../lsp/src/requests/signature_help.rs | 291 +++ .../lsp/src/requests/signature_help/tests.rs | 196 ++ .../src/requests/signature_help/traversal.rs | 304 +++ noir/noir-repo/tooling/lsp/src/types.rs | 6 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 48 +- .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_artifacts/src/program.rs | 4 + .../profiler/src/cli/gates_flamegraph_cmd.rs | 1 + .../src/cli/opcodes_flamegraph_cmd.rs | 2 + noir/noir-repo/yarn.lock | 13 +- 174 files changed, 5278 insertions(+), 2603 deletions(-) rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/op.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..cd2891c1558 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +e2f7e950c44883228d5e1230b04c83e479de7ed0 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_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 72c95823553..b28606d3739 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -557,6 +557,7 @@ pub fn compile_no_check( let force_compile = force_compile || options.print_acir || options.show_brillig + || options.force_brillig || options.show_ssa || options.emit_ssa; @@ -578,7 +579,7 @@ pub fn compile_no_check( emit_ssa: if options.emit_ssa { Some(context.package_build_path.clone()) } else { None }, }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + let SsaProgramArtifact { program, debug, warnings, names, brillig_names, error_types, .. } = create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); @@ -593,5 +594,6 @@ pub fn compile_no_check( noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, names, + brillig_names, }) } diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 8e02de0b8b3..88460482928 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -29,4 +29,6 @@ pub struct CompiledProgram { pub warnings: Vec, /// Names of the functions in the program. These are used for more informative debugging and benchmarking. pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..d6364cefc9a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -27,5 +27,7 @@ pub(crate) fn convert_ssa_function( BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); } - brillig_context.artifact() + let mut artifact = brillig_context.artifact(); + artifact.name = func.name().to_string(); + artifact } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index dff4da56c1e..fca1f60544d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -55,6 +55,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_invert".to_string(), } } @@ -109,5 +110,6 @@ pub(crate) fn directive_quotient() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_integer_quotient".to_string(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..53964f64170 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -22,6 +22,7 @@ pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, + pub(crate) name: String, } #[derive(Default, Debug, Clone)] @@ -49,6 +50,8 @@ pub(crate) struct BrilligArtifact { locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. call_stack: CallStack, + /// Name of the function, only used for debugging purposes. + pub(crate) name: String, } /// A pointer to a location in the opcode. @@ -81,6 +84,7 @@ impl BrilligArtifact { byte_code: self.byte_code, locations: self.locations, assert_messages: self.assert_messages, + name: self.name, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index b632958f37f..9daf98e606b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -151,6 +151,7 @@ pub struct SsaProgramArtifact { pub main_input_witnesses: Vec, pub main_return_witnesses: Vec, pub names: Vec, + pub brillig_names: Vec, pub error_types: BTreeMap, } @@ -167,6 +168,7 @@ impl SsaProgramArtifact { main_input_witnesses: Vec::default(), main_return_witnesses: Vec::default(), names: Vec::default(), + brillig_names: Vec::default(), error_types, } } @@ -202,8 +204,10 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = - optimize_into_acir(program, options)?; + let ArtifactsAndWarnings( + (generated_acirs, generated_brillig, brillig_function_names, error_types), + ssa_level_warnings, + ) = optimize_into_acir(program, options)?; if options.force_brillig_output { assert_eq!( generated_acirs.len(), @@ -236,6 +240,7 @@ pub fn create_program( program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } + program_artifact.brillig_names = brillig_function_names; Ok(program_artifact) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 22b13aeae79..9f11267f100 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -282,6 +282,7 @@ impl AcirValue { pub(crate) type Artifacts = ( Vec>, Vec>, + Vec, BTreeMap, ); @@ -334,11 +335,13 @@ impl Ssa { } } - let brillig = vecmap(shared_context.generated_brillig, |brillig| BrilligBytecode { - bytecode: brillig.byte_code, - }); + let (brillig_bytecode, brillig_names) = shared_context + .generated_brillig + .into_iter() + .map(|brillig| (BrilligBytecode { bytecode: brillig.byte_code }, brillig.name)) + .unzip(); - Ok((acirs, brillig, self.error_selector_to_type)) + Ok((acirs, brillig_bytecode, brillig_names, self.error_selector_to_type)) } } @@ -955,6 +958,8 @@ impl<'a> Context<'a> { BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); + entry_point.name = func.name().to_string(); + // Link the entry point with all dependencies while let Some(unresolved_fn_label) = entry_point.first_unresolved_function_call() { let artifact = &brillig.find_by_function_label(unresolved_fn_label.clone()); @@ -2966,7 +2971,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: @@ -3061,7 +3066,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` @@ -3151,7 +3156,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3265,7 +3270,7 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3329,7 +3334,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3403,7 +3408,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3491,7 +3496,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); 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..33f8c9d8332 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. @@ -896,6 +897,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "=="), }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -908,6 +910,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "!="), }, BinaryOpKind::Less => match (lhs, rhs) { 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..4ed72cc0263 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}; @@ -17,13 +17,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, UnresolvedType, - UnresolvedTypeData, Visibility, + ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnaryOp, + UnresolvedType, UnresolvedTypeData, Visibility, }, 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, @@ -47,7 +47,12 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), + "expr_as_if" => expr_as_if(arguments, return_type, location), + "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -86,6 +91,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 +102,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 +519,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 +639,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 { @@ -688,6 +755,21 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn as_bool(self) -> Option +fn expr_as_bool( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + Some(Value::Bool(bool)) + } else { + None + } + }) +} + // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( arguments: Vec<(Value, Location)>, @@ -708,6 +790,108 @@ fn expr_as_function_call( }) } +// fn as_if(self) -> Option<(Expr, Expr, Option)> +fn expr_as_if( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::If(if_expr) = expr { + // Get the type of `Option` + let option_type = extract_option_generic_type(return_type.clone()); + let Type::Tuple(option_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(option_types.len(), 3); + let alternative_option_type = option_types[2].clone(); + + let alternative = + option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + + Some(Value::Tuple(vec![ + Value::Expr(if_expr.condition.kind), + Value::Expr(if_expr.consequence.kind), + alternative.ok()?, + ])) + } else { + None + } + }) +} + +// fn as_index(self) -> Option +fn expr_as_index( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Index(index_expr) = expr { + Some(Value::Tuple(vec![ + Value::Expr(index_expr.collection.kind), + Value::Expr(index_expr.index.kind), + ])) + } else { + None + } + }) +} + +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value = match prefix_expr.operator { + UnaryOp::Minus => 0_u128, + UnaryOp::Not => 1_u128, + UnaryOp::MutableReference => 2_u128, + UnaryOp::Dereference { .. } => 3_u128, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Expr]> +fn expr_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Tuple(expressions) = expr { + let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(expressions, typ)) + } else { + None + } + }) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -719,8 +903,12 @@ where F: FnOnce(ExpressionKind) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let expr = get_expr(self_argument)?; - let option_value = f(expr); + let mut expression_kind = get_expr(self_argument)?; + while let ExpressionKind::Parenthesized(expression) = expression_kind { + expression_kind = expression.kind; + } + + let option_value = f(expression_kind); option(return_type, option_value) } 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/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 54681632543..3230ce42457 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,6 +1,22 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; impl Expr { + #[builtin(expr_as_bool)] + fn as_bool(self) -> Option {} + #[builtin(expr_as_function_call)] fn as_function_call(self) -> Option<(Expr, [Expr])> {} + + #[builtin(expr_as_if)] + fn as_if(self) -> Option<(Expr, Expr, Option)> {} + + #[builtin(expr_as_index)] + fn as_index(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_tuple)] + fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..beaee8e44ea 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -5,9 +5,11 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +33,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/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..1fbd4884841 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -0,0 +1,21 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} 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_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr index 8b6f7b480c7..eb0d27e2a38 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -1,8 +1,55 @@ fn main() { comptime { + // Check Expr::as_function_call let expr = quote { foo(bar) }.as_expr().unwrap(); let (_function, args) = expr.as_function_call().unwrap(); assert_eq(args.len(), 1); + + // Check Expr::as_index + let expr = quote { foo[bar] }.as_expr().unwrap(); + let _ = expr.as_index().unwrap(); + + // Check Expr::as_tuple + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + + // Check Expr::as_if + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + + // Check parenthesized expression is automatically unwrapped + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + + // Check Expr::as_bool + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + + // Check Expr::as_unary_op + let expr = quote { -x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_minus()); + + let expr = quote { !x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_not()); + + let expr = quote { &mut x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_mutable_reference()); + + let expr = quote { *x }.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + assert(op.is_dereference()); } } 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/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ca34d7686fd..796137a8e9f 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -23,7 +23,7 @@ use fxhash::FxHashSet; use lsp_types::{ request::{ Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, - References, Rename, + References, Rename, SignatureHelpRequest, }, CodeLens, }; @@ -55,7 +55,7 @@ use requests::{ on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, - on_test_run_request, on_tests_request, LspInitializationOptions, + on_signature_help_request, on_test_run_request, on_tests_request, LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -143,6 +143,7 @@ impl NargoLspService { .request::(on_hover_request) .request::(on_inlay_hint_request) .request::(on_completion_request) + .request::(on_signature_help_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..dd3131aba56 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,74 +4,45 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; -use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, +use completion_items::{ + crate_completion_item, field_completion_item, simple_completion_item, + struct_field_completion_item, }; +use fm::{FileId, PathString}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - ArrayLiteral, AsTraitPath, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, Expression, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, PathSegment, Pattern, - Statement, TraitImplItem, TraitItem, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UseTree, UseTreeKind, + AsTraitPath, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement, + MemberAccessExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, + PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, }, graph::{CrateId, Dependency}, hir::{ 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}, + hir_def::traits::Trait, + 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; +mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -173,13 +144,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()); + } + } - fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); + Some(CompletionResponse::Array(items)) } } @@ -253,14 +227,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); @@ -277,10 +243,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_struct.generics); @@ -292,12 +254,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Function { @@ -357,22 +313,22 @@ impl<'a> NodeFinder<'a> { fn find_in_statement(&mut self, statement: &Statement) { match &statement.kind { - noirc_frontend::ast::StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement) => { self.find_in_let_statement(let_statement, true); } - noirc_frontend::ast::StatementKind::Constrain(constrain_statement) => { + StatementKind::Constrain(constrain_statement) => { self.find_in_constrain_statement(constrain_statement); } - noirc_frontend::ast::StatementKind::Expression(expression) => { + StatementKind::Expression(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement) => { self.find_in_assign_statement(assign_statement); } - noirc_frontend::ast::StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement) => { self.find_in_for_loop_statement(for_loop_statement); } - noirc_frontend::ast::StatementKind::Comptime(statement) => { + StatementKind::Comptime(statement) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -381,12 +337,10 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::StatementKind::Semi(expression) => { + StatementKind::Semi(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Break - | noirc_frontend::ast::StatementKind::Continue - | noirc_frontend::ast::StatementKind::Error => (), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } @@ -403,22 +357,6 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - fn find_in_assign_statement( - &mut self, - assign_statement: &noirc_frontend::ast::AssignStatement, - ) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { let old_local_variables = self.local_variables.clone(); let ident = &for_loop_statement.identifier; @@ -432,7 +370,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); @@ -442,69 +391,53 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - fn find_in_expression(&mut self, expression: &Expression) { match &expression.kind { - noirc_frontend::ast::ExpressionKind::Literal(literal) => self.find_in_literal(literal), - noirc_frontend::ast::ExpressionKind::Block(block_expression) => { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { self.find_in_block_expression(block_expression); } - noirc_frontend::ast::ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(prefix_expression) => { self.find_in_expression(&prefix_expression.rhs); } - noirc_frontend::ast::ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(index_expression) => { self.find_in_index_expression(index_expression); } - noirc_frontend::ast::ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(call_expression) => { self.find_in_call_expression(call_expression); } - noirc_frontend::ast::ExpressionKind::MethodCall(method_call_expression) => { + ExpressionKind::MethodCall(method_call_expression) => { self.find_in_method_call_expression(method_call_expression); } - noirc_frontend::ast::ExpressionKind::Constructor(constructor_expression) => { + ExpressionKind::Constructor(constructor_expression) => { self.find_in_constructor_expression(constructor_expression); } - noirc_frontend::ast::ExpressionKind::MemberAccess(member_access_expression) => { + ExpressionKind::MemberAccess(member_access_expression) => { self.find_in_member_access_expression(member_access_expression); } - noirc_frontend::ast::ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(cast_expression) => { self.find_in_cast_expression(cast_expression); } - noirc_frontend::ast::ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(infix_expression) => { self.find_in_infix_expression(infix_expression); } - noirc_frontend::ast::ExpressionKind::If(if_expression) => { + ExpressionKind::If(if_expression) => { self.find_in_if_expression(if_expression); } - noirc_frontend::ast::ExpressionKind::Variable(path) => { + ExpressionKind::Variable(path) => { self.find_in_path(path, RequestedItems::AnyItems); } - noirc_frontend::ast::ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions) => { self.find_in_expressions(expressions); } - noirc_frontend::ast::ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - noirc_frontend::ast::ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Comptime(block_expression, _) => { + ExpressionKind::Comptime(block_expression, _) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -513,58 +446,75 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::ExpressionKind::Quote(_) - | noirc_frontend::ast::ExpressionKind::Resolved(_) - | noirc_frontend::ast::ExpressionKind::Error => (), + ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), } - } - fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), + // "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_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } + fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return; } - } - fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } } - fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - } + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { + return; + }; - fn find_in_method_call_expression(&mut self, method_call_expression: &MethodCallExpression) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); + } + + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } } @@ -572,16 +522,20 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } + 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; + } + } - fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); + self.find_in_expression(&member_access_expression.lhs); } fn find_in_if_expression(&mut self, if_expression: &IfExpression) { @@ -617,21 +571,6 @@ impl<'a> NodeFinder<'a> { self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { if let Some(span) = unresolved_type.span { if !self.includes_span(span) { @@ -640,48 +579,48 @@ impl<'a> NodeFinder<'a> { } match &unresolved_type.typ { - noirc_frontend::ast::UnresolvedTypeData::Array(_, unresolved_type) => { + UnresolvedTypeData::Array(_, unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Slice(unresolved_type) => { + UnresolvedTypeData::Slice(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Parenthesized(unresolved_type) => { + UnresolvedTypeData::Parenthesized(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Named(path, unresolved_types, _) => { + UnresolvedTypeData::Named(path, unresolved_types, _) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::MutableReference(unresolved_type) => { + UnresolvedTypeData::MutableReference(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { + UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); } - noirc_frontend::ast::UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::UnresolvedTypeData::Expression(_) - | noirc_frontend::ast::UnresolvedTypeData::FormatString(_, _) - | noirc_frontend::ast::UnresolvedTypeData::String(_) - | noirc_frontend::ast::UnresolvedTypeData::Unspecified - | noirc_frontend::ast::UnresolvedTypeData::Quoted(_) - | noirc_frontend::ast::UnresolvedTypeData::FieldElement - | noirc_frontend::ast::UnresolvedTypeData::Integer(_, _) - | noirc_frontend::ast::UnresolvedTypeData::Bool - | noirc_frontend::ast::UnresolvedTypeData::Unit - | noirc_frontend::ast::UnresolvedTypeData::Resolved(_) - | noirc_frontend::ast::UnresolvedTypeData::Error => (), + UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Error => (), } } @@ -707,19 +646,51 @@ 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(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods(trait_, &prefix, FunctionKind::Any); + 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 +798,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 +884,101 @@ 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::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | 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_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + ) { + for (name, func_id) in &trait_.method_ids { + 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(struct_field_completion_item(name, typ)); + } + } + } + + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +987,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 +1017,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 +1034,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 +1046,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 +1081,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 +1090,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, + if let Ok(path_resolution) = path_resolver.resolve(self.def_maps, path, &mut None) { + return Some(path_resolution.module_def_id); } - } - 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 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); } } - } - 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()), - )); - } - } - } - - 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 +1120,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..70afc43fe55 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,382 @@ +use lsp_types::{ + Command, 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.first() { + 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 if let Type::Tuple(self_tuple_types) = self_type { + // Tuple types of different lengths seem to also have methods defined on all of them, + // so here we reject methods for tuples where the length doesn't match. + if let Type::Tuple(func_self_tuple_types) = func_self_type { + if self_tuple_types.len() != func_self_tuple_types.len() { + 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 + }; + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => completion_item, + FunctionCompletionKind::NameAndParameters => { + completion_item_with_trigger_parameter_hints_command(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 struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { + field_completion_item(field, typ.to_string()) +} + +pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +} + +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 } +} + +pub(super) fn completion_item_with_trigger_parameter_hints_command( + completion_item: CompletionItem, +) -> CompletionItem { + CompletionItem { + command: Some(Command { + title: "Trigger parameter hints".to_string(), + command: "editor.action.triggerParameterHints".to_string(), + arguments: None, + }), + ..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..ff028954f7b --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1326 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, + completion_item_with_trigger_parameter_hints_command, crate_completion_item, + field_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 get_completions(src: &str) -> 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(), + }, + }, + ); + + 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"); + }; + + items + } + + fn assert_items_match(mut items: Vec, mut expected: Vec) { + items.sort_by_key(|item| item.label.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); + } + + async fn assert_completion(src: &str, expected: Vec) { + let items = get_completions(src).await; + assert_items_match(items, expected); + } + + pub(super) fn function_completion_item( + label: impl Into, + insert_text: impl Into, + description: impl Into, + ) -> CompletionItem { + completion_item_with_trigger_parameter_hints_command(snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + Some(description.into()), + )) + } + + #[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![function_completion_item("hello()", "hello()", "fn()")]).await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion( + src, + vec![function_completion_item( + "hello(…)", + "hello(${1:x}, ${2:y})", + "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()), + ), + function_completion_item("assert_constant(…)", "assert_constant(${1:x})", "fn(T)"), + 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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("bar", "i32")]).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![ + function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)"), + function_completion_item("foobar2(…)", "foobar2(${1:x})", "fn(&mut self, i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![field_completion_item("foo", "i32")]).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![field_completion_item("bar", "Bar")]).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![function_completion_item("foo()", "foo()", "fn(self) -> Foo")]) + .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![field_completion_item("bar", "i32")]).await; + } + + #[test] + async fn test_completes_tuple_fields() { + let src = r#" + fn main() { + let tuple = (1, true); + tuple.>|< + } + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.kind == Some(CompletionItemKind::FIELD)); + let items = items.collect(); + + assert_items_match( + items, + vec![field_completion_item("0", "Field"), field_completion_item("1", "bool")], + ); + } + + #[test] + async fn test_completes_constructor_fields() { + let src = r#" + mod foobar { + struct Foo { + bb: i32, + bbb: Field, + bbbb: bool, + bbbbb: str<6>, + } + } + + fn main() { + foobar::Foo { bbb: 1, b>|<, bbbbb } + } + "#; + assert_completion( + src, + vec![field_completion_item("bb", "i32"), field_completion_item("bbbb", "bool")], + ) + .await; + } + + #[test] + async fn test_completes_trait_methods() { + let src = r#" + trait One { + fn one() -> Self; + } + + fn main() { + One::>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) + .await; + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs new file mode 100644 index 00000000000..b487c8baf36 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs @@ -0,0 +1,132 @@ +/// This file includes the completion logic that's just about +/// traversing the AST without any additional logic. +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, CallExpression, CastExpression, ConstrainStatement, + Expression, ForRange, FunctionReturnType, IndexExpression, InfixExpression, Literal, + MethodCallExpression, NoirTrait, NoirTypeAlias, TraitImplItem, UnresolvedType, + }, + ParsedModule, +}; + +use super::NodeFinder; + +impl<'a> NodeFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + } + + pub(super) fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { + for unresolved_type in unresolved_type { + self.find_in_unresolved_type(unresolved_type); + } + } + + pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { + match return_type { + noirc_frontend::ast::FunctionReturnType::Default(_) => (), + noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { + self.find_in_unresolved_type(unresolved_type); + } + } + } + + pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { + self.find_in_unresolved_type(&noir_type_alias.typ); + } +} 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..e88c7f11465 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -46,6 +46,7 @@ mod inlay_hint; mod profile_run; mod references; mod rename; +mod signature_help; mod test_run; mod tests; @@ -56,7 +57,8 @@ pub(crate) use { goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + rename::on_rename_request, signature_help::on_signature_help_request, + test_run::on_test_run_request, tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -234,13 +236,22 @@ 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, }, completion_item: None, })), + signature_help_provider: Some(lsp_types::OneOf::Right( + lsp_types::SignatureHelpOptions { + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs new file mode 100644 index 00000000000..c2c69185547 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -0,0 +1,291 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, PathString}; +use lsp_types::{ + ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, +}; +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::NodeInterner, + node_interner::ReferenceId, + ParsedModule, Type, +}; + +use crate::{utils, LspState}; + +use super::process_request; + +mod tests; +mod traversal; + +pub(crate) fn on_signature_help_request( + state: &mut LspState, + params: SignatureHelpParams, +) -> impl Future, ResponseError>> { + let uri = params.text_document_position_params.clone().text_document.uri; + + let result = process_request(state, params.text_document_position_params.clone(), |args| { + let path = PathString::from_path(uri.to_file_path().unwrap()); + args.files.get_file_id(&path).and_then(|file_id| { + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) + }) + }) + }); + future::ready(result) +} + +struct SignatureFinder<'a> { + file: FileId, + byte_index: usize, + interner: &'a NodeInterner, + signature_help: Option, +} + +impl<'a> SignatureFinder<'a> { + fn new(file: FileId, byte_index: usize, interner: &'a NodeInterner) -> Self { + Self { file, byte_index, interner, signature_help: None } + } + + fn find(&mut self, parsed_module: &ParsedModule) -> Option { + self.find_in_parsed_module(parsed_module); + + self.signature_help.clone() + } + + fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn try_compute_signature_help( + &mut self, + arguments: &[Expression], + arguments_span: Span, + name_span: Span, + has_self: bool, + ) { + if self.signature_help.is_some() { + return; + } + + if !self.includes_span(arguments_span) { + return; + } + + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + let location = Location::new(name_span, self.file); + + // Check if the call references a named function + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(location) { + let name = self.interner.function_name(&func_id); + let func_meta = self.interner.function_meta(&func_id); + + let signature_information = + self.func_meta_signature_information(func_meta, name, active_parameter, has_self); + self.set_signature_help(signature_information); + return; + } + + // Otherwise, the call must be a reference to an fn type + if let Some(mut typ) = self.interner.type_at_location(location) { + if let Type::Forall(_, forall_typ) = typ { + typ = *forall_typ; + } + if let Type::Function(args, return_type, _, unconstrained) = typ { + let signature_information = self.function_type_signature_information( + &args, + &return_type, + unconstrained, + active_parameter, + ); + self.set_signature_help(signature_information); + } + } + } + + fn func_meta_signature_information( + &self, + func_meta: &FuncMeta, + name: &str, + active_parameter: Option, + has_self: bool, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str("fn "); + label.push_str(name); + label.push('('); + for (index, (pattern, typ, _)) in func_meta.parameters.0.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + if has_self && index == 0 { + if let Type::MutableReference(..) = typ { + label.push_str("&mut self"); + } else { + label.push_str("self"); + } + } else { + let parameter_start = label.chars().count(); + + self.hir_pattern_to_argument(pattern, &mut label); + label.push_str(": "); + label.push_str(&typ.to_string()); + + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([ + parameter_start as u32, + parameter_end as u32, + ]), + documentation: None, + }); + } + } + label.push(')'); + + match &func_meta.return_type { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(typ) => { + label.push_str(" -> "); + label.push_str(&typ.to_string()); + } + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + fn function_type_signature_information( + &self, + args: &[Type], + return_type: &Type, + unconstrained: bool, + active_parameter: Option, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + if unconstrained { + label.push_str("unconstrained "); + } + label.push_str("fn("); + for (index, typ) in args.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(&typ.to_string()); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + if let Type::Unit = return_type { + // Nothing + } else { + label.push_str(" -> "); + label.push_str(&return_type.to_string()); + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + 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 set_signature_help(&mut self, signature_information: SignatureInformation) { + let signature_help = SignatureHelp { + active_parameter: signature_information.active_parameter, + signatures: vec![signature_information], + active_signature: Some(0), + }; + self.signature_help = Some(signature_help); + } + + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs new file mode 100644 index 00000000000..c48ee159084 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -0,0 +1,196 @@ +#[cfg(test)] +mod signature_help_tests { + use crate::{ + notifications::on_did_open_text_document, requests::on_signature_help_request, test_utils, + }; + + use lsp_types::{ + DidOpenTextDocumentParams, ParameterLabel, Position, SignatureHelp, SignatureHelpParams, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_signature_help(src: &str) -> SignatureHelp { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .find_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .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(), + }, + }, + ); + + on_signature_help_request( + &mut state, + SignatureHelpParams { + context: None, + text_document_position_params: 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 }, + }, + ) + .await + .expect("Could not execute on_signature_help_request") + .unwrap() + } + + fn check_label(signature_label: &str, parameter_label: &ParameterLabel, expected_string: &str) { + let ParameterLabel::LabelOffsets(offsets) = parameter_label else { + panic!("Expected label to be LabelOffsets, got {:?}", parameter_label); + }; + + assert_eq!(&signature_label[offsets[0] as usize..offsets[1] as usize], expected_string); + } + + #[test] + async fn test_signature_help_for_call_at_first_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + fn wrapper(x: u32) {} + + fn bar() { + wrapper(foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_call_between_arguments() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1,>|< 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_at_second_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, >|<2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_past_last_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, 2, >|<); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(2)); + } + + #[test] + async fn test_signature_help_for_method_call() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self, x: i32, y: Field) -> u32 { 0 } + } + + fn wrapper(x: u32) {} + + fn bar(f: Foo) { + wrapper(f.foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(self, x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_fn_call() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + let f = foo; + f(>|<1, 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn(i32, Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "i32"); + check_label(&signature.label, ¶ms[1].label, "Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs new file mode 100644 index 00000000000..22f92a86124 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs @@ -0,0 +1,304 @@ +/// This file includes the signature help logic that's just about +/// traversing the AST without any additional logic. +use super::SignatureFinder; + +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, + ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, + IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, + TraitImplItem, TraitItem, TypeImpl, + }, + parser::{Item, ItemKind}, + ParsedModule, +}; + +impl<'a> SignatureFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_item(&mut self, item: &Item) { + if !self.includes_span(item.span) { + return; + } + + match &item.kind { + ItemKind::Submodules(parsed_sub_module) => { + self.find_in_parsed_module(&parsed_sub_module.contents); + } + ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), + ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), + ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), + ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), + ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), + ItemKind::Import(..) + | ItemKind::TypeAlias(_) + | ItemKind::Struct(_) + | ItemKind::ModuleDecl(_) => (), + } + } + + pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { + self.find_in_block_expression(&noir_function.def.body); + } + + pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + for item in &noir_trait_impl.items { + self.find_in_trait_impl_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + for (method, span) in &type_impl.methods { + if self.includes_span(*span) { + self.find_in_noir_function(method); + } + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { + match trait_item { + TraitItem::Function { body, .. } => { + if let Some(body) = body { + self.find_in_block_expression(body); + }; + } + TraitItem::Constant { default_value, .. } => { + if let Some(default_value) = default_value { + self.find_in_expression(default_value); + } + } + TraitItem::Type { .. } => (), + } + } + + pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { + for statement in &block_expression.statements { + if self.includes_span(statement.span) { + self.find_in_statement(statement); + } + } + } + + pub(super) fn find_in_statement(&mut self, statement: &Statement) { + if !self.includes_span(statement.span) { + return; + } + + match &statement.kind { + StatementKind::Let(let_statement) => { + self.find_in_let_statement(let_statement); + } + StatementKind::Constrain(constrain_statement) => { + self.find_in_constrain_statement(constrain_statement); + } + StatementKind::Expression(expression) => { + self.find_in_expression(expression); + } + StatementKind::Assign(assign_statement) => { + self.find_in_assign_statement(assign_statement); + } + StatementKind::For(for_loop_statement) => { + self.find_in_for_loop_statement(for_loop_statement); + } + StatementKind::Comptime(statement) => { + self.find_in_statement(statement); + } + StatementKind::Semi(expression) => { + self.find_in_expression(expression); + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } + } + + pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { + self.find_in_expression(&let_statement.expression); + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { + self.find_in_for_range(&for_loop_statement.range); + self.find_in_expression(&for_loop_statement.block); + } + + pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { + match lvalue { + LValue::Ident(_) => (), + LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), + LValue::Index { array, index, span: _ } => { + self.find_in_lvalue(array); + self.find_in_expression(index); + } + LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), + } + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_expression(&mut self, expression: &Expression) { + match &expression.kind { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Prefix(prefix_expression) => { + self.find_in_expression(&prefix_expression.rhs); + } + ExpressionKind::Index(index_expression) => { + self.find_in_index_expression(index_expression); + } + ExpressionKind::Call(call_expression) => { + self.find_in_call_expression(call_expression, expression.span); + } + ExpressionKind::MethodCall(method_call_expression) => { + self.find_in_method_call_expression(method_call_expression, expression.span); + } + ExpressionKind::Constructor(constructor_expression) => { + self.find_in_constructor_expression(constructor_expression); + } + ExpressionKind::MemberAccess(member_access_expression) => { + self.find_in_member_access_expression(member_access_expression); + } + ExpressionKind::Cast(cast_expression) => { + self.find_in_cast_expression(cast_expression); + } + ExpressionKind::Infix(infix_expression) => { + self.find_in_infix_expression(infix_expression); + } + ExpressionKind::If(if_expression) => { + self.find_in_if_expression(if_expression); + } + ExpressionKind::Tuple(expressions) => { + self.find_in_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Unquote(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Comptime(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Variable(_) + | ExpressionKind::AsTraitPath(_) + | ExpressionKind::Quote(_) + | ExpressionKind::Resolved(_) + | ExpressionKind::Error => (), + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_constructor_expression( + &mut self, + constructor_expression: &ConstructorExpression, + ) { + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_member_access_expression( + &mut self, + member_access_expression: &MemberAccessExpression, + ) { + self.find_in_expression(&member_access_expression.lhs); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { + self.find_in_expression(&if_expression.condition); + self.find_in_expression(&if_expression.consequence); + + if let Some(alternative) = &if_expression.alternative { + self.find_in_expression(alternative); + } + } + + pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { + self.find_in_expression(&lambda.body); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 5afda0d292a..3ac1f35e18e 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ CompletionOptions, DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, - HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, + HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, SignatureHelpOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; @@ -161,6 +161,10 @@ pub(crate) struct ServerCapabilities { /// The server provides completion support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) completion_provider: Option>, + + /// The server provides signature help support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) signature_help_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index a6395d1c8c9..2fd189172d8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -100,8 +100,7 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = - table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]); for program_info in info_report.programs { let program_rows: Vec = program_info.into(); @@ -176,18 +175,31 @@ struct ProgramInfo { #[serde(skip)] expression_width: ExpressionWidth, functions: Vec, + unconstrained_functions_opcodes: usize, + unconstrained_functions: Vec, } impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { + let mut main = vecmap(program_info.functions, |function| { row![ Fm->format!("{}", program_info.package_name), Fc->format!("{}", function.name), format!("{:?}", program_info.expression_width), Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", program_info.unconstrained_functions_opcodes), ] - }) + }); + main.extend(vecmap(program_info.unconstrained_functions, |function| { + row![ + Fm->format!("{}", program_info.package_name), + Fc->format!("{}", function.name), + format!("N/A", ), + Fc->format!("N/A"), + Fc->format!("{}", function.acir_opcodes), + ] + })); + main } } @@ -235,5 +247,31 @@ fn count_opcodes_and_gates_in_program( }) .collect(); - ProgramInfo { package_name: package.name.to_string(), expression_width, functions } + let opcodes_len: Vec = compiled_program + .bytecode + .unconstrained_functions + .iter() + .map(|func| func.bytecode.len()) + .collect(); + let unconstrained_functions_opcodes = compiled_program + .bytecode + .unconstrained_functions + .into_par_iter() + .map(|function| function.bytecode.len()) + .sum(); + let unconstrained_info: Vec = compiled_program + .brillig_names + .clone() + .iter() + .zip(opcodes_len) + .map(|(name, len)| FunctionInfo { name: name.clone(), acir_opcodes: len }) + .collect(); + + ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions, + unconstrained_functions_opcodes, + unconstrained_functions: unconstrained_info, + } } 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/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index 91f02157414..ffffc6345d5 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -37,6 +37,8 @@ pub struct ProgramArtifact { pub file_map: BTreeMap, pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } impl From for ProgramArtifact { @@ -49,6 +51,7 @@ impl From for ProgramArtifact { debug_symbols: ProgramDebugInfo { debug_infos: compiled_program.debug }, file_map: compiled_program.file_map, names: compiled_program.names, + brillig_names: compiled_program.brillig_names, } } } @@ -64,6 +67,7 @@ impl From for CompiledProgram { file_map: program.file_map, warnings: vec![], names: program.names, + brillig_names: program.brillig_names, } } } diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e072c63f274..7c6e77917a5 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -173,6 +173,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index f91e50d8716..d7f3cbb9b85 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -188,6 +188,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file @@ -228,6 +229,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: vec!["main".to_string()], }; // Write the artifact to a file 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 From e6354cf0fcb919d205c092229fa86c7971830e6a Mon Sep 17 00:00:00 2001 From: AztecBot Date: Fri, 16 Aug 2024 08:03:08 +0000 Subject: [PATCH 04/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From f446a2b5b09ccf210d010935374a64f33330b02d Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sat, 17 Aug 2024 08:01:56 +0000 Subject: [PATCH 05/21] [1 changes] feat: LSP auto-import completion (https://github.com/noir-lang/noir/pull/5741) fix: Allow comptime code to use break without also being `unconstrained` (https://github.com/noir-lang/noir/pull/5744) feat: add `Expr::as_any_integer` and `Expr::as_member_access` (https://github.com/noir-lang/noir/pull/5742) chore: clarify Field use (https://github.com/noir-lang/noir/pull/5740) feat: add `Expr::as_binary_op` (https://github.com/noir-lang/noir/pull/5734) chore(docs): expanding solidity verifier chain list (https://github.com/noir-lang/noir/pull/5587) chore: apply some new lints across workspace (https://github.com/noir-lang/noir/pull/5736) feat: suggest trait methods in LSP completion (https://github.com/noir-lang/noir/pull/5735) feat: LSP autocomplete constructor fields (https://github.com/noir-lang/noir/pull/5732) feat: add `Expr::as_unary` (https://github.com/noir-lang/noir/pull/5731) chore: count brillig opcodes in nargo info (https://github.com/noir-lang/noir/pull/5189) feat: suggest tuple fields in LSP completion (https://github.com/noir-lang/noir/pull/5730) feat: add `Expr::as_bool` (https://github.com/noir-lang/noir/pull/5729) feat: add `Expr` methods: `as_tuple`, `as_parenthesized`, `as_index`, `as_if` (https://github.com/noir-lang/noir/pull/5726) feat: LSP signature help (https://github.com/noir-lang/noir/pull/5725) chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- noir/noir-repo/Cargo.toml | 6 + noir/noir-repo/acvm-repo/acir/Cargo.toml | 3 + .../acvm-repo/acir/src/circuit/mod.rs | 8 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 3 + .../acvm-repo/acir_field/src/field_element.rs | 4 +- .../acvm-repo/acir_field/src/generic_ark.rs | 4 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../acvm_js/src/js_execution_error.rs | 8 +- .../acvm-repo/blackbox_solver/Cargo.toml | 3 + .../bn254_blackbox_solver/Cargo.toml | 3 + .../src/generator/generators.rs | 2 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 3 + .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 3 + noir/noir-repo/aztec_macros/Cargo.toml | 3 + .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- noir/noir-repo/compiler/fm/Cargo.toml | 3 + .../noir-repo/compiler/noirc_arena/Cargo.toml | 3 + .../compiler/noirc_driver/Cargo.toml | 3 + .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 11 +- .../compiler/noirc_driver/src/program.rs | 2 + .../compiler/noirc_errors/Cargo.toml | 3 + .../compiler/noirc_errors/src/position.rs | 2 +- .../compiler/noirc_evaluator/Cargo.toml | 3 + .../src/brillig/brillig_gen.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 2 + .../src/brillig/brillig_ir/artifact.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 9 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 31 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 9 +- .../noirc_evaluator/src/ssa/ir/map.rs | 2 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 +++- .../src/ssa/ssa_gen/context.rs | 8 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../noirc_frontend/src/ast/expression.rs | 18 +- .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../noirc_frontend/src/elaborator/comptime.rs | 8 + .../src/elaborator/expressions.rs | 21 +- .../noirc_frontend/src/elaborator/mod.rs | 31 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 60 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 3 + .../src/hir/comptime/interpreter/builtin.rs | 269 ++- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/def_collector/dc_mod.rs | 48 +- .../src/hir/def_map/module_def.rs | 2 +- .../src/hir/type_check/errors.rs | 5 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 121 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../compiler/noirc_frontend/src/locations.rs | 76 +- .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 107 +- .../noirc_frontend/src/parser/errors.rs | 4 + .../noirc_frontend/src/parser/parser.rs | 99 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 203 ++ .../compiler/noirc_printable_type/Cargo.toml | 3 + .../compiler/noirc_printable_type/src/lib.rs | 1 + noir/noir-repo/compiler/wasm/Cargo.toml | 4 +- noir/noir-repo/cspell.json | 3 +- .../docs/explainers/explainer-writing-noir.md | 8 +- .../docs/how_to/how-to-solidity-verifier.md | 8 +- .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../how_to/how-to-solidity-verifier.md | 8 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 4 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/expr.nr | 26 + noir/noir-repo/noir_stdlib/src/meta/mod.nr | 8 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 92 + .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_exp/src/main.nr | 79 + .../comptime_fmt_strings/src/main.nr | 2 +- .../comptime_trait_impl/Nargo.toml | 7 + .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 6 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ----- .../verify_honk_proof/src/main.nr | 16 - .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- noir/noir-repo/tooling/acvm_cli/Cargo.toml | 3 + noir/noir-repo/tooling/debugger/Cargo.toml | 3 + noir/noir-repo/tooling/fuzzer/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/src/lib.rs | 11 +- .../tooling/lsp/src/notifications/mod.rs | 8 +- .../tooling/lsp/src/requests/completion.rs | 1740 ++++------------- .../src/requests/completion/auto_import.rs | 195 ++ .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 382 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 33 + .../lsp/src/requests/completion/tests.rs | 1559 +++++++++++++++ .../lsp/src/requests/completion/traversal.rs | 132 ++ .../tooling/lsp/src/requests/hover.rs | 2 +- .../tooling/lsp/src/requests/inlay_hint.rs | 16 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 15 +- .../lsp/src/requests/signature_help.rs | 291 +++ .../lsp/src/requests/signature_help/tests.rs | 196 ++ .../src/requests/signature_help/traversal.rs | 304 +++ noir/noir-repo/tooling/lsp/src/types.rs | 6 +- noir/noir-repo/tooling/nargo/Cargo.toml | 3 + noir/noir-repo/tooling/nargo_cli/Cargo.toml | 3 + .../tooling/nargo_cli/src/cli/info_cmd.rs | 48 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 3 + .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + noir/noir-repo/tooling/nargo_toml/Cargo.toml | 3 + .../noir_js_backend_barretenberg/package.json | 2 +- noir/noir-repo/tooling/noirc_abi/Cargo.toml | 3 + .../tooling/noirc_abi/src/arbitrary.rs | 8 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/src/lib.rs | 5 +- .../tooling/noirc_artifacts/Cargo.toml | 3 + .../tooling/noirc_artifacts/src/program.rs | 4 + noir/noir-repo/tooling/profiler/Cargo.toml | 3 + .../profiler/src/cli/gates_flamegraph_cmd.rs | 3 +- .../src/cli/opcodes_flamegraph_cmd.rs | 2 + .../tooling/profiler/src/opcode_formatter.rs | 2 +- noir/noir-repo/yarn.lock | 13 +- 225 files changed, 6242 insertions(+), 2699 deletions(-) rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/op.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..336e1ccacbf 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +cdbb940a883ae32dd84c667ec06b0d155f2d7520 diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 63509170057..7d9b3254c53 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -50,6 +50,12 @@ rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" +[workspace.lints.rust] +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" +unused_qualifications = "warn" + [workspace.dependencies] # ACVM workspace dependencies diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 88616ccffb5..860b565544b 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 00d0933a3aa..43984e4a922 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -103,7 +103,7 @@ impl ErrorSelector { impl Serialize for ErrorSelector { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.to_string().serialize(serializer) } @@ -112,7 +112,7 @@ impl Serialize for ErrorSelector { impl<'de> Deserialize<'de> for ErrorSelector { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; let as_u64 = s.parse().map_err(serde::de::Error::custom)?; @@ -224,7 +224,7 @@ impl Circuit { } impl Program { - fn write(&self, writer: W) -> std::io::Result<()> { + fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); encoder.write_all(&buf)?; @@ -250,7 +250,7 @@ impl Program { } impl Deserialize<'a>> Program { - fn read(reader: R) -> std::io::Result { + fn read(reader: R) -> std::io::Result { let mut gz_decoder = flate2::read::GzDecoder::new(reader); let mut buf_d = Vec::new(); gz_decoder.read_to_end(&mut buf_d)?; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index a037a453348..c1cffc1334e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 92ccbc4e5f6..2323f008dbe 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -115,7 +115,7 @@ impl From for FieldElement { } } -impl Serialize for FieldElement { +impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -124,7 +124,7 @@ impl Serialize for FieldElement { } } -impl<'de, T: ark_ff::PrimeField> Deserialize<'de> for FieldElement { +impl<'de, T: PrimeField> Deserialize<'de> for FieldElement { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs index f7228e66314..74927c07a36 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs @@ -2,7 +2,7 @@ use num_bigint::BigUint; /// This trait is extremely unstable and WILL have breaking changes. pub trait AcirField: - std::marker::Sized + Sized + std::fmt::Display + std::fmt::Debug + Default @@ -24,7 +24,7 @@ pub trait AcirField: // + From + From + std::hash::Hash - + std::cmp::Eq + + Eq { fn one() -> Self; fn zero() -> Self; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 2ee4d529e5a..bf1170ce073 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 0d90b9ba54f..6b457a8f5f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] 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/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index 23da88247fc..f6a00af7942 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -54,16 +54,14 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; let assertion_payload = match assertion_payload { - Some(raw) => ::from_serde(&raw) + Some(raw) => ::from_serde(&raw) .expect("Cannot serialize assertion payload"), None => JsValue::UNDEFINED, }; let brillig_function_id = match brillig_function_id { - Some(function_id) => { - ::from_serde(&function_id) - .expect("Cannot serialize Brillig function id") - } + Some(function_id) => ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id"), None => JsValue::UNDEFINED, }; diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 10b491c7f67..d0473857e1c 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ab677396c22..9a23f503a06 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index bb51426b33b..a4125014d56 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -91,7 +91,7 @@ mod test { fn test_derive_generators() { let res = derive_generators("test domain".as_bytes(), 128, 0); - let is_unique = |y: Affine, j: usize| -> bool { + let is_unique = |y: Affine, j: usize| -> bool { for (i, res) in res.iter().enumerate() { if i != j && *res == y { return false; diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index 631acbd55d8..9f5a5ce0ee2 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ebd35f1579f..cd9f754ebc0 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed..c9d88e36e28 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index b48f445be36..c367333a6f4 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml index b94f997b7b9..68fc4d1c920 100644 --- a/noir/noir-repo/compiler/noirc_arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -5,3 +5,6 @@ authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a9949f5093a..b244018cc71 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] 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_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 72c95823553..467bda2ca88 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -274,11 +274,8 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = if options.disable_macros { - &[] - } else { - &[&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs( @@ -557,6 +554,7 @@ pub fn compile_no_check( let force_compile = force_compile || options.print_acir || options.show_brillig + || options.force_brillig || options.show_ssa || options.emit_ssa; @@ -578,7 +576,7 @@ pub fn compile_no_check( emit_ssa: if options.emit_ssa { Some(context.package_build_path.clone()) } else { None }, }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + let SsaProgramArtifact { program, debug, warnings, names, brillig_names, error_types, .. } = create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); @@ -593,5 +591,6 @@ pub fn compile_no_check( noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, names, + brillig_names, }) } diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 8e02de0b8b3..88460482928 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -29,4 +29,6 @@ pub struct CompiledProgram { pub warnings: Vec, /// Names of the functions in the program. These are used for more informative debugging and benchmarking. pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index 41b1cd0ff58..61b274c605f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 02b242e8b4d..1792197eab7 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -16,7 +16,7 @@ pub struct Spanned { /// This is important for tests. Two Spanned objects are equal if their content is equal /// They may not have the same span. Use into_span to test for Span being equal specifically -impl PartialEq> for Spanned { +impl PartialEq> for Spanned { fn eq(&self, other: &Spanned) -> bool { self.contents == other.contents } diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 66c770b5064..81feb0b7154 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..d6364cefc9a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -27,5 +27,7 @@ pub(crate) fn convert_ssa_function( BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); } - brillig_context.artifact() + let mut artifact = brillig_context.artifact(); + artifact.name = func.name().to_string(); + artifact } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index dff4da56c1e..fca1f60544d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -55,6 +55,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_invert".to_string(), } } @@ -109,5 +110,6 @@ pub(crate) fn directive_quotient() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_integer_quotient".to_string(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..53964f64170 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -22,6 +22,7 @@ pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, + pub(crate) name: String, } #[derive(Default, Debug, Clone)] @@ -49,6 +50,8 @@ pub(crate) struct BrilligArtifact { locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. call_stack: CallStack, + /// Name of the function, only used for debugging purposes. + pub(crate) name: String, } /// A pointer to a location in the opcode. @@ -81,6 +84,7 @@ impl BrilligArtifact { byte_code: self.byte_code, locations: self.locations, assert_messages: self.assert_messages, + name: self.name, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index b632958f37f..9daf98e606b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -151,6 +151,7 @@ pub struct SsaProgramArtifact { pub main_input_witnesses: Vec, pub main_return_witnesses: Vec, pub names: Vec, + pub brillig_names: Vec, pub error_types: BTreeMap, } @@ -167,6 +168,7 @@ impl SsaProgramArtifact { main_input_witnesses: Vec::default(), main_return_witnesses: Vec::default(), names: Vec::default(), + brillig_names: Vec::default(), error_types, } } @@ -202,8 +204,10 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = - optimize_into_acir(program, options)?; + let ArtifactsAndWarnings( + (generated_acirs, generated_brillig, brillig_function_names, error_types), + ssa_level_warnings, + ) = optimize_into_acir(program, options)?; if options.force_brillig_output { assert_eq!( generated_acirs.len(), @@ -236,6 +240,7 @@ pub fn create_program( program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } + program_artifact.brillig_names = brillig_function_names; Ok(program_artifact) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index b2a73106468..24b30b22ec4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -2005,7 +2005,7 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 22b13aeae79..e7465f62ece 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -235,7 +235,7 @@ impl Debug for AcirDynamicArray { #[derive(Debug, Clone)] pub(crate) enum AcirValue { Var(AcirVar, AcirType), - Array(im::Vector), + Array(Vector), DynamicArray(AcirDynamicArray), } @@ -282,6 +282,7 @@ impl AcirValue { pub(crate) type Artifacts = ( Vec>, Vec>, + Vec, BTreeMap, ); @@ -334,11 +335,13 @@ impl Ssa { } } - let brillig = vecmap(shared_context.generated_brillig, |brillig| BrilligBytecode { - bytecode: brillig.byte_code, - }); + let (brillig_bytecode, brillig_names) = shared_context + .generated_brillig + .into_iter() + .map(|brillig| (BrilligBytecode { bytecode: brillig.byte_code }, brillig.name)) + .unzip(); - Ok((acirs, brillig, self.error_selector_to_type)) + Ok((acirs, brillig_bytecode, brillig_names, self.error_selector_to_type)) } } @@ -955,6 +958,8 @@ impl<'a> Context<'a> { BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); + entry_point.name = func.name().to_string(); + // Link the entry point with all dependencies while let Some(unresolved_fn_label) = entry_point.first_unresolved_function_call() { let artifact = &brillig.find_by_function_label(unresolved_fn_label.clone()); @@ -1645,7 +1650,7 @@ impl<'a> Context<'a> { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - let array: im::Vector = init_values.into(); + let array: Vector = init_values.into(); self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } @@ -2966,7 +2971,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: @@ -3061,7 +3066,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` @@ -3151,7 +3156,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3265,7 +3270,7 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3329,7 +3334,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3403,7 +3408,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3491,7 +3496,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 34d7d595eb9..f06f46d7af8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -171,13 +171,14 @@ impl DataFlowGraph { ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - use InsertInstructionResult::*; match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { - SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification), + SimplifyResult::SimplifiedTo(simplification) => { + InsertInstructionResult::SimplifiedTo(simplification) + } SimplifyResult::SimplifiedToMultiple(simplification) => { - SimplifiedToMultiple(simplification) + InsertInstructionResult::SimplifiedToMultiple(simplification) } - SimplifyResult::Remove => InstructionRemoved, + SimplifyResult::Remove => InsertInstructionResult::InstructionRemoved, result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1c9a31f0c99..f1265553b83 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -50,7 +50,7 @@ impl Id { // Need to manually implement most impls on Id. // Otherwise rust assumes that Id: Hash only if T: Hash, // which isn't true since the T is not used internally. -impl std::hash::Hash for Id { +impl Hash for Id { fn hash(&self, state: &mut H) { self.index.hash(state); } 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..13e5c2445ad 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 @@ -85,7 +85,7 @@ pub(super) struct Loop { } /// The queue of functions remaining to compile -type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; +type FunctionQueue = Vec<(FuncId, IrFunctionId)>; impl<'a> FunctionContext<'a> { /// Create a new FunctionContext to compile the first function in the shared_context's @@ -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 @@ -1005,14 +1005,14 @@ impl SharedContext { } /// Pops the next function from the shared function queue, returning None if the queue is empty. - pub(super) fn pop_next_function_in_queue(&self) -> Option<(ast::FuncId, IrFunctionId)> { + pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() } /// Return the matching id for the given function if known. If it is not known this /// will add the function to the queue of functions to compile, assign it a new id, /// and return this new id. - pub(super) fn get_or_queue_function(&self, id: ast::FuncId) -> IrFunctionId { + pub(super) fn get_or_queue_function(&self, id: FuncId) -> IrFunctionId { // Start a new block to guarantee the destructor for the map lock is released // before map needs to be acquired again in self.functions.write() below { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1373d1bc46e..6b19aff2674 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -396,11 +396,11 @@ impl<'a> FunctionContext<'a> { /// return a reference to each element, for use with the store instruction. fn codegen_array_index( &mut self, - array: super::ir::value::ValueId, - index: super::ir::value::ValueId, + array: ValueId, + index: ValueId, element_type: &ast::Type, location: Location, - length: Option, + length: Option, ) -> Result { // base_index = index * type_size let index = self.make_array_index(index); @@ -438,11 +438,7 @@ impl<'a> FunctionContext<'a> { /// Prepare a slice access. /// Check that the index being used to access a slice element /// is less than the dynamic slice length. - fn codegen_slice_access_check( - &mut self, - index: super::ir::value::ValueId, - length: Option, - ) { + fn codegen_slice_access_check(&mut self, index: ValueId, length: Option) { let index = self.make_array_index(index); // We convert the length as an array index type for comparison let array_len = self 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..1fd1e313c44 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, QuotedTypeId}; -use crate::token::{Attributes, Token, Tokens}; +use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -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 @@ -477,6 +478,20 @@ pub struct FunctionDefinition { pub return_visibility: Visibility, } +impl FunctionDefinition { + pub fn is_private(&self) -> bool { + self.visibility == ItemVisibility::Private + } + + pub fn is_test(&self) -> bool { + if let Some(attribute) = &self.attributes.function { + matches!(attribute, FunctionAttribute::Test(..)) + } else { + false + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Param { pub visibility: Visibility, @@ -587,6 +602,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/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index afa2e7fa7a8..2b78c02e53c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -267,6 +267,14 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_empty_fn(); let module = self.module_id(); self.interner.push_function(id, &function.def, module, location); + + if self.interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + self.interner.register_function(id, &function.def); + } + let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { file_id: self.file, 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..2e4a69c067f 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)); @@ -1290,6 +1297,12 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; + let name = if self.interner.is_in_lsp_mode() { + Some(let_stmt.pattern.name_ident().to_string()) + } else { + None + }; + if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { @@ -1312,8 +1325,9 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner - .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); + if let Some(name) = name { + self.interner.register_global(global_id, name, self.module_id()); + } self.local_module = old_module; self.file = old_file; @@ -1451,9 +1465,12 @@ impl<'context> Elaborator<'context> { /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { - self.current_item.map_or(true, |id| match id { - DependencyId::Function(id) => !self.interner.function_modifiers(&id).is_unconstrained, - _ => true, - }) + !self.in_comptime_context() + && self.current_item.map_or(true, |id| match id { + DependencyId::Function(id) => { + !self.interner.function_modifiers(&id).is_unconstrained + } + _ => true, + }) } } 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..33f8c9d8332 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. @@ -896,6 +897,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "=="), }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -908,6 +910,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "!="), }, BinaryOpKind::Less => match (lhs, rhs) { 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..cec4b1fd434 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}; @@ -17,13 +17,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, UnresolvedType, - UnresolvedTypeData, Visibility, + ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnaryOp, + UnresolvedType, UnresolvedTypeData, Visibility, }, 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, @@ -47,7 +47,15 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), + "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), + "expr_as_if" => expr_as_if(arguments, return_type, location), + "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_integer" => expr_as_integer(arguments, return_type, location), + "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -86,6 +94,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 +105,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 +522,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 +642,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 { @@ -688,6 +758,21 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn as_bool(self) -> Option +fn expr_as_bool( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + Some(Value::Bool(bool)) + } else { + None + } + }) +} + // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( arguments: Vec<(Value, Location)>, @@ -708,6 +793,172 @@ fn expr_as_function_call( }) } +// fn as_if(self) -> Option<(Expr, Expr, Option)> +fn expr_as_if( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::If(if_expr) = expr { + // Get the type of `Option` + let option_type = extract_option_generic_type(return_type.clone()); + let Type::Tuple(option_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(option_types.len(), 3); + let alternative_option_type = option_types[2].clone(); + + let alternative = + option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + + Some(Value::Tuple(vec![ + Value::Expr(if_expr.condition.kind), + Value::Expr(if_expr.consequence.kind), + alternative.ok()?, + ])) + } else { + None + } + }) +} + +// fn as_index(self) -> Option +fn expr_as_index( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Index(index_expr) = expr { + Some(Value::Tuple(vec![ + Value::Expr(index_expr.collection.kind), + Value::Expr(index_expr.index.kind), + ])) + } else { + None + } + }) +} + +// fn as_integer(self) -> Option<(Field, bool)> +fn expr_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + }) +} + +// fn as_member_access(self) -> Option<(Expr, Quoted)> +fn expr_as_member_access( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::MemberAccess(member_access) = expr { + let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); + Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + } else { + None + } + }) +} + +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Expr]> +fn expr_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Tuple(expressions) = expr { + let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(expressions, typ)) + } else { + None + } + }) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -719,8 +970,12 @@ where F: FnOnce(ExpressionKind) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let expr = get_expr(self_argument)?; - let option_value = f(expr); + let mut expression_kind = get_expr(self_argument)?; + while let ExpressionKind::Parenthesized(expression) = expression_kind { + expression_kind = expression.kind; + } + + let option_value = f(expression_kind); option(return_type, option_value) } 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/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index be2afd13507..6436c2562bc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::ModuleAttributes; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -232,6 +232,13 @@ impl<'a> ModCollector<'a> { let location = Location::new(function.span(), self.file_id); context.def_interner.push_function(func_id, &function.def, module, location); + if context.def_interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + context.def_interner.register_function(func_id, &function.def); + } + // Now link this func_id to a crate level map with the noir function and the module id // Encountering a NoirFunction, we retrieve it's module_data to get the namespace // Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner @@ -307,8 +314,8 @@ impl<'a> ModCollector<'a> { }; // Add the struct to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_struct(name, id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_struct(name.clone(), id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -322,10 +329,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Struct(id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_struct(id, name.to_string(), parent_module_id); + } } definition_errors } @@ -383,7 +390,7 @@ impl<'a> ModCollector<'a> { // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_type_alias(name, type_alias_id); + .declare_type_alias(name.clone(), type_alias_id); if let Err((first_def, second_def)) = result { let err = DefCollectorErrorKind::Duplicate { @@ -396,10 +403,11 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Alias(type_alias_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + let name = name.to_string(); + context.def_interner.register_type_alias(type_alias_id, name, parent_module_id); + } } errors } @@ -432,8 +440,8 @@ impl<'a> ModCollector<'a> { }; // Add the trait to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, trait_id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_trait(name.clone(), trait_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -567,10 +575,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location( - ReferenceId::Trait(trait_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_trait(trait_id, name.to_string(), parent_module_id); + } self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -766,6 +774,10 @@ impl<'a> ModCollector<'a> { parent: self.module_id, }, ); + + if context.def_interner.is_in_lsp_mode() { + context.def_interner.register_module(mod_id, mod_name.0.contents.clone()); + } } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs index 54d092f9515..a487bda81b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs @@ -3,7 +3,7 @@ use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; use super::ModuleId; /// A generic ID that references either a module, function, type, interface or global -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), 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/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index c437676b605..0ac13a58ecf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,9 +1,10 @@ use fm::FileId; use noirc_errors::Location; use rangemap::RangeMap; -use rustc_hash::FxHashMap; +use rustc_hash::FxHashMap as HashMap; use crate::{ + ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, macros_api::{NodeInterner, StructId}, node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, @@ -12,7 +13,7 @@ use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] pub(crate) struct LocationIndices { - map_file_to_range: FxHashMap>, + map_file_to_range: HashMap>, } impl LocationIndices { @@ -275,4 +276,75 @@ impl NodeInterner { .neighbors_directed(reference_index, petgraph::Direction::Outgoing) .next() } + + pub(crate) fn register_module(&mut self, id: ModuleId, name: String) { + self.register_name_for_auto_import(name, ModuleDefId::ModuleId(id), ItemVisibility::Public); + } + + pub(crate) fn register_global( + &mut self, + id: GlobalId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Global(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::GlobalId(id), visibility); + } + + pub(crate) fn register_struct( + &mut self, + id: StructId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Struct(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeId(id), visibility); + } + + pub(crate) fn register_trait(&mut self, id: TraitId, name: String, parent_module_id: ModuleId) { + self.add_definition_location(ReferenceId::Trait(id), Some(parent_module_id)); + + self.register_name_for_auto_import(name, ModuleDefId::TraitId(id), ItemVisibility::Public); + } + + pub(crate) fn register_type_alias( + &mut self, + id: TypeAliasId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Alias(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeAliasId(id), visibility); + } + + pub(crate) fn register_function(&mut self, id: FuncId, func_def: &FunctionDefinition) { + self.register_name_for_auto_import( + func_def.name.0.contents.clone(), + ModuleDefId::FunctionId(id), + func_def.visibility, + ); + } + + fn register_name_for_auto_import( + &mut self, + name: String, + module_def_id: ModuleDefId, + visibility: ItemVisibility, + ) { + if !self.lsp_mode { + return; + } + + self.auto_import_names.entry(name).or_default().push((module_def_id, visibility)); + } + + pub fn get_auto_import_names(&self) -> &HashMap> { + &self.auto_import_names + } } 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..46ec2676930 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -19,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::ModuleDefId; use crate::macros_api::UnaryOp; use crate::QuotedType; @@ -179,10 +180,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, @@ -231,6 +232,11 @@ pub struct NodeInterner { // (ReferenceId::Reference and ReferenceId::Local aren't included here) pub(crate) reference_modules: HashMap, + // All names (and their definitions) that can be offered for auto_import. + // These include top-level functions, global variables and types, but excludes + // impl and trait-impl methods. + pub(crate) auto_import_names: HashMap>, + /// Each value currently in scope in the comptime interpreter. /// Each element of the Vec represents a scope with every scope together making /// up all currently visible definitions. The first scope is always the global scope. @@ -602,6 +608,7 @@ impl Default for NodeInterner { reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::default(), reference_modules: HashMap::default(), + auto_import_names: HashMap::default(), comptime_scopes: vec![HashMap::default()], } } @@ -910,6 +917,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored self.add_definition_location(ReferenceId::Function(id), Some(module)); + definition_id } @@ -951,12 +959,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 +1139,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 +1212,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 +1318,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 +1336,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 +1672,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 +1697,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 +1706,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 +1803,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 +1827,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 +2006,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 +2015,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 +2046,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 +2056,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 +2107,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/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index 5140f5a5a8c..8bb56703e8a 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index 20272dfeecb..c8b8c3bb06e 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -6,8 +6,10 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index e939cd583e8..18417b376e5 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -216,7 +216,8 @@ "wasi", "wasmer", "Weierstraß", - "zshell" + "zshell", + "Linea" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md index c8a42c379e6..3ef6e014a2f 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -86,7 +86,7 @@ A Noir program makes a statement that can be verified. It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure + - Conceptually a tree structure - Leaves (inputs) are the `Field` type - Nodes contain arithmetic operations to combine them (gates) - The root is the final result (return value) @@ -99,12 +99,16 @@ You can dig deeper and use the `--print-acir` param to take a closer look at ind ### Use the `Field` type Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. +Some things to be mindful of when using a Field type for a regular integer value: +- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) + - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 +- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size +- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to :::tip Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates ::: -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. ### Use Arithmetic over non-arithmetic operations diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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.32.0/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 54681632543..55c49531017 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,6 +1,32 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_integer)] + fn as_integer(self) -> Option<(Field, bool)> {} + + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + + #[builtin(expr_as_bool)] + fn as_bool(self) -> Option {} + #[builtin(expr_as_function_call)] fn as_function_call(self) -> Option<(Expr, [Expr])> {} + + #[builtin(expr_as_if)] + fn as_if(self) -> Option<(Expr, Expr, Option)> {} + + #[builtin(expr_as_index)] + fn as_index(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_member_access)] + fn as_member_access(self) -> Option<(Expr, Quoted)> {} + + #[builtin(expr_as_tuple)] + fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..d16a8648bc2 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -5,9 +5,11 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +33,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); @@ -41,7 +45,7 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted result } -unconstrained pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { HANDLERS.insert(t, f); } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..ebd89677c50 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -0,0 +1,92 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + 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_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr index 8b6f7b480c7..7b25dcb5097 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -1,8 +1,87 @@ +use std::meta::op::UnaryOp; +use std::meta::op::BinaryOp; + fn main() { comptime { + // Check Expr::as_function_call let expr = quote { foo(bar) }.as_expr().unwrap(); let (_function, args) = expr.as_function_call().unwrap(); assert_eq(args.len(), 1); + + // Check Expr::as_index + let expr = quote { foo[bar] }.as_expr().unwrap(); + let _ = expr.as_index().unwrap(); + + // Check Expr::as_tuple + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + + // Check Expr::as_if + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + + // Check parenthesized expression is automatically unwrapped + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + + // Check Expr::as_bool + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + + // Check Expr::as_unary_op + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + + // Check Expr::as_binary_op + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + + // Check Expr::as_integer + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + + // Check Expr::as_member_access + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); } } + +comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op +} + +comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op +} 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..c5cc7880112 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,14 +4,16 @@ use std::meta::unquote; fn main() { comptime { - foo::<3>(5); + unsafe { + foo::<3>(5) + }; submodule::bar(); } } // Call a different function from the interpreter, then have the // elaborator switch to the middle of foo from its previous scope in main -unconstrained comptime fn foo(x: Field) { +comptime fn foo(x: Field) { assert(modulus_num_bits() != 0); let cond = quote { modulus_num_bits() != 0 }; 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/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3..06dd5e676bd 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `acvm_cli` to `acvm` diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 540d6d11bc0..b9b83d86836 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [build-dependencies] build-data.workspace = true diff --git a/noir/noir-repo/tooling/fuzzer/Cargo.toml b/noir/noir-repo/tooling/fuzzer/Cargo.toml index 106d8abead1..31701991844 100644 --- a/noir/noir-repo/tooling/fuzzer/Cargo.toml +++ b/noir/noir-repo/tooling/fuzzer/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 03c6c9105ba..c5203478f5a 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true# rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ca34d7686fd..366b158ad89 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -23,14 +23,14 @@ use fxhash::FxHashSet; use lsp_types::{ request::{ Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, - References, Rename, + References, Rename, SignatureHelpRequest, }, CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, - workspace::{self, Workspace}, + workspace::Workspace, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -55,7 +55,7 @@ use requests::{ on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, - on_test_run_request, on_tests_request, LspInitializationOptions, + on_signature_help_request, on_test_run_request, on_tests_request, LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -143,6 +143,7 @@ impl NargoLspService { .request::(on_hover_request) .request::(on_inlay_hint_request) .request::(on_completion_request) + .request::(on_signature_help_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -383,7 +384,7 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { pub fn insert_all_files_for_workspace_into_file_manager( state: &LspState, - workspace: &workspace::Workspace, + workspace: &Workspace, file_manager: &mut FileManager, ) { // First add files we cached: these have the source code of files that are modified @@ -410,7 +411,7 @@ fn prepare_package_from_source_string() { let client = ClientSocket::new_closed(); let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); - let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); + let (mut context, crate_id) = prepare_source(source.to_string(), &mut state); let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3a60de15c4a..4d2186badc3 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -223,7 +223,7 @@ mod notification_tests { use super::*; use lsp_types::{ - InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + InlayHintLabel, InlayHintParams, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, WorkDoneProgressParams, }; @@ -270,9 +270,9 @@ mod notification_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: 0, character: 0 }, - end: lsp_types::Position { line: 1, character: 0 }, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 1, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..ad0cf0c5b9b 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,74 +4,46 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; -use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, +use completion_items::{ + crate_completion_item, field_completion_item, simple_completion_item, + struct_field_completion_item, }; +use fm::{FileId, FileMap, PathString}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - ArrayLiteral, AsTraitPath, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, Expression, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, PathSegment, Pattern, - Statement, TraitImplItem, TraitItem, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UseTree, UseTreeKind, + AsTraitPath, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement, + MemberAccessExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, + PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, }, graph::{CrateId, Dependency}, hir::{ 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}, + hir_def::traits::Trait, + 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 crate::{requests::to_lsp_location, utils, LspState}; use super::process_request; +mod auto_import; 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; +mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -94,7 +66,9 @@ pub(crate) fn on_completion_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut finder = NodeFinder::new( + args.files, file_id, + source, byte_index, byte, args.crate_id, @@ -110,7 +84,9 @@ pub(crate) fn on_completion_request( } struct NodeFinder<'a> { + files: &'a FileMap, file: FileId, + lines: Vec<&'a str>, byte_index: usize, byte: Option, /// The module ID of the current file. @@ -129,11 +105,20 @@ struct NodeFinder<'a> { /// Type parameters in the current scope. These are collected when entering /// a struct, a function, etc., and cleared afterwards. type_parameters: HashSet, + /// ModuleDefIds we already suggested, so we don't offer these for auto-import. + suggested_module_def_ids: HashSet, + /// How many nested `mod` we are in deep + nesting: usize, + /// The line where an auto_import must be inserted + auto_import_line: usize, } impl<'a> NodeFinder<'a> { + #[allow(clippy::too_many_arguments)] fn new( + files: &'a FileMap, file: FileId, + source: &'a str, byte_index: usize, byte: Option, krate: CrateId, @@ -153,7 +138,9 @@ impl<'a> NodeFinder<'a> { }; let module_id = ModuleId { krate, local_id }; Self { + files, file, + lines: source.lines().collect(), byte_index, byte, root_module_id, @@ -164,6 +151,9 @@ impl<'a> NodeFinder<'a> { completion_items: Vec::new(), local_variables: HashMap::new(), type_parameters: HashSet::new(), + suggested_module_def_ids: HashSet::new(), + nesting: 0, + auto_import_line: 0, } } @@ -173,17 +163,26 @@ 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()); + } + } - fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); + Some(CompletionResponse::Array(items)) } } fn find_in_item(&mut self, item: &Item) { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; + } + } + if !self.includes_span(item.span) { return; } @@ -206,10 +205,19 @@ impl<'a> NodeFinder<'a> { ModuleId { krate: self.module_id.krate, local_id: *child_module }; } + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + self.find_in_parsed_module(&parsed_sub_module.contents); // Restore the old module before continuing self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; } ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), @@ -253,14 +261,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); @@ -277,10 +277,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_struct.generics); @@ -292,12 +288,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Function { @@ -357,22 +347,22 @@ impl<'a> NodeFinder<'a> { fn find_in_statement(&mut self, statement: &Statement) { match &statement.kind { - noirc_frontend::ast::StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement) => { self.find_in_let_statement(let_statement, true); } - noirc_frontend::ast::StatementKind::Constrain(constrain_statement) => { + StatementKind::Constrain(constrain_statement) => { self.find_in_constrain_statement(constrain_statement); } - noirc_frontend::ast::StatementKind::Expression(expression) => { + StatementKind::Expression(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement) => { self.find_in_assign_statement(assign_statement); } - noirc_frontend::ast::StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement) => { self.find_in_for_loop_statement(for_loop_statement); } - noirc_frontend::ast::StatementKind::Comptime(statement) => { + StatementKind::Comptime(statement) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -381,12 +371,10 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::StatementKind::Semi(expression) => { + StatementKind::Semi(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Break - | noirc_frontend::ast::StatementKind::Continue - | noirc_frontend::ast::StatementKind::Error => (), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } @@ -403,22 +391,6 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - fn find_in_assign_statement( - &mut self, - assign_statement: &noirc_frontend::ast::AssignStatement, - ) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { let old_local_variables = self.local_variables.clone(); let ident = &for_loop_statement.identifier; @@ -432,7 +404,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); @@ -442,69 +425,53 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - fn find_in_expression(&mut self, expression: &Expression) { match &expression.kind { - noirc_frontend::ast::ExpressionKind::Literal(literal) => self.find_in_literal(literal), - noirc_frontend::ast::ExpressionKind::Block(block_expression) => { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { self.find_in_block_expression(block_expression); } - noirc_frontend::ast::ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(prefix_expression) => { self.find_in_expression(&prefix_expression.rhs); } - noirc_frontend::ast::ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(index_expression) => { self.find_in_index_expression(index_expression); } - noirc_frontend::ast::ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(call_expression) => { self.find_in_call_expression(call_expression); } - noirc_frontend::ast::ExpressionKind::MethodCall(method_call_expression) => { + ExpressionKind::MethodCall(method_call_expression) => { self.find_in_method_call_expression(method_call_expression); } - noirc_frontend::ast::ExpressionKind::Constructor(constructor_expression) => { + ExpressionKind::Constructor(constructor_expression) => { self.find_in_constructor_expression(constructor_expression); } - noirc_frontend::ast::ExpressionKind::MemberAccess(member_access_expression) => { + ExpressionKind::MemberAccess(member_access_expression) => { self.find_in_member_access_expression(member_access_expression); } - noirc_frontend::ast::ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(cast_expression) => { self.find_in_cast_expression(cast_expression); } - noirc_frontend::ast::ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(infix_expression) => { self.find_in_infix_expression(infix_expression); } - noirc_frontend::ast::ExpressionKind::If(if_expression) => { + ExpressionKind::If(if_expression) => { self.find_in_if_expression(if_expression); } - noirc_frontend::ast::ExpressionKind::Variable(path) => { + ExpressionKind::Variable(path) => { self.find_in_path(path, RequestedItems::AnyItems); } - noirc_frontend::ast::ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions) => { self.find_in_expressions(expressions); } - noirc_frontend::ast::ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - noirc_frontend::ast::ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Comptime(block_expression, _) => { + ExpressionKind::Comptime(block_expression, _) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -513,58 +480,75 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::ExpressionKind::Quote(_) - | noirc_frontend::ast::ExpressionKind::Resolved(_) - | noirc_frontend::ast::ExpressionKind::Error => (), + ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), } - } - fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), + // "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_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } + fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return; } - } - fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } } - fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - } + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { + return; + }; - fn find_in_method_call_expression(&mut self, method_call_expression: &MethodCallExpression) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); + } + + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } } @@ -572,16 +556,20 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } + 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; + } + } - fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); + self.find_in_expression(&member_access_expression.lhs); } fn find_in_if_expression(&mut self, if_expression: &IfExpression) { @@ -617,21 +605,6 @@ impl<'a> NodeFinder<'a> { self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { if let Some(span) = unresolved_type.span { if !self.includes_span(span) { @@ -640,48 +613,48 @@ impl<'a> NodeFinder<'a> { } match &unresolved_type.typ { - noirc_frontend::ast::UnresolvedTypeData::Array(_, unresolved_type) => { + UnresolvedTypeData::Array(_, unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Slice(unresolved_type) => { + UnresolvedTypeData::Slice(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Parenthesized(unresolved_type) => { + UnresolvedTypeData::Parenthesized(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Named(path, unresolved_types, _) => { + UnresolvedTypeData::Named(path, unresolved_types, _) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::MutableReference(unresolved_type) => { + UnresolvedTypeData::MutableReference(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { + UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); } - noirc_frontend::ast::UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::UnresolvedTypeData::Expression(_) - | noirc_frontend::ast::UnresolvedTypeData::FormatString(_, _) - | noirc_frontend::ast::UnresolvedTypeData::String(_) - | noirc_frontend::ast::UnresolvedTypeData::Unspecified - | noirc_frontend::ast::UnresolvedTypeData::Quoted(_) - | noirc_frontend::ast::UnresolvedTypeData::FieldElement - | noirc_frontend::ast::UnresolvedTypeData::Integer(_, _) - | noirc_frontend::ast::UnresolvedTypeData::Bool - | noirc_frontend::ast::UnresolvedTypeData::Unit - | noirc_frontend::ast::UnresolvedTypeData::Resolved(_) - | noirc_frontend::ast::UnresolvedTypeData::Error => (), + UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Error => (), } } @@ -707,19 +680,51 @@ 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(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods(trait_, &prefix, FunctionKind::Any); + 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, @@ -743,6 +748,7 @@ impl<'a> NodeFinder<'a> { self.type_parameters_completion(&prefix); } } + self.complete_auto_imports(&prefix, requested_items); } } @@ -827,7 +833,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 +919,103 @@ 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::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); + } + } + } + } + } + + fn complete_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + ) { + for (name, func_id) in &trait_.method_ids { + 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); + } + } + } + } + + 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(struct_field_completion_item(name, typ)); + } + } + } + + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +1024,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 +1054,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,9 +1071,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } @@ -977,9 +1084,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } } @@ -1011,131 +1120,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 +1129,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 +1159,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/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs new file mode 100644 index 00000000000..c5710d30b5c --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -0,0 +1,195 @@ +use lsp_types::{Position, Range, TextEdit}; +use noirc_frontend::{ + ast::ItemVisibility, + graph::{CrateId, Dependency}, + hir::def_map::ModuleId, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, +}; + +use super::{ + kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, + name_matches, + sort_text::auto_import_sort_text, + NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn complete_auto_imports(&mut self, prefix: &str, requested_items: RequestedItems) { + for (name, entries) in self.interner.get_auto_import_names() { + if !name_matches(name, prefix) { + continue; + } + + for (module_def_id, visibility) in entries { + if self.suggested_module_def_ids.contains(module_def_id) { + continue; + } + + let Some(mut completion_item) = self.module_def_id_completion_item( + *module_def_id, + name.clone(), + FunctionCompletionKind::NameAndParameters, + FunctionKind::Any, + requested_items, + ) else { + continue; + }; + + let module_full_path; + if let ModuleDefId::ModuleId(module_id) = module_def_id { + module_full_path = module_id_path( + module_id, + &self.module_id, + self.interner, + self.dependencies, + ); + } else { + let Some(parent_module) = get_parent_module(self.interner, *module_def_id) + else { + continue; + }; + + match *visibility { + ItemVisibility::Public => (), + ItemVisibility::Private => { + // Technically this can't be reached because we don't record private items for auto-import, + // but this is here for completeness. + continue; + } + ItemVisibility::PublicCrate => { + if self.module_id.krate != parent_module.krate { + continue; + } + } + } + + module_full_path = module_id_path( + parent_module, + &self.module_id, + self.interner, + self.dependencies, + ); + } + + let full_path = if let ModuleDefId::ModuleId(..) = module_def_id { + module_full_path + } else { + format!("{}::{}", module_full_path, name) + }; + + let mut label_details = completion_item.label_details.unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + + let line = self.auto_import_line as u32; + let character = (self.nesting * 4) as u32; + let indent = " ".repeat(self.nesting * 4); + let mut newlines = "\n"; + + // If the line we are inserting into is not an empty line, insert an extra line to make some room + if let Some(line_text) = self.lines.get(line as usize) { + if !line_text.trim().is_empty() { + newlines = "\n\n"; + } + } + + completion_item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + new_text: format!("use {};{}{}", full_path, newlines, indent), + }]); + + completion_item.sort_text = Some(auto_import_sort_text()); + + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(*module_def_id); + } + } + } +} + +fn get_parent_module(interner: &NodeInterner, module_def_id: ModuleDefId) -> Option<&ModuleId> { + let reference_id = module_def_id_to_reference_id(module_def_id); + interner.reference_module(reference_id) +} + +fn module_def_id_to_reference_id(module_def_id: ModuleDefId) -> ReferenceId { + match module_def_id { + ModuleDefId::ModuleId(id) => ReferenceId::Module(id), + ModuleDefId::FunctionId(id) => ReferenceId::Function(id), + ModuleDefId::TypeId(id) => ReferenceId::Struct(id), + ModuleDefId::TypeAliasId(id) => ReferenceId::Alias(id), + ModuleDefId::TraitId(id) => ReferenceId::Trait(id), + ModuleDefId::GlobalId(id) => ReferenceId::Global(id), + } +} + +/// Computes the path of `module_id` relative to `current_module_id`. +/// If it's not relative, the full path is returned. +fn module_id_path( + module_id: &ModuleId, + current_module_id: &ModuleId, + interner: &NodeInterner, + dependencies: &[Dependency], +) -> String { + let mut string = String::new(); + + let crate_id = module_id.krate; + let crate_name = match crate_id { + CrateId::Root(_) => Some("crate".to_string()), + CrateId::Crate(_) => dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| format!("{}", dep.name)), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Dummy => None, + }; + + let wrote_crate = if let Some(crate_name) = crate_name { + string.push_str(&crate_name); + true + } else { + false + }; + + let Some(module_attributes) = interner.try_module_attributes(module_id) else { + return string; + }; + + if wrote_crate { + string.push_str("::"); + } + + let mut segments = Vec::new(); + let mut current_attributes = module_attributes; + loop { + let parent_module_id = + &ModuleId { krate: module_id.krate, local_id: current_attributes.parent }; + + // If the parent module is the current module we stop because we want a relative path to the module + if current_module_id == parent_module_id { + // When the path is relative we don't want the "crate::" prefix anymore + string = string.strip_prefix("crate::").unwrap_or(&string).to_string(); + break; + } + + let Some(parent_attributes) = interner.try_module_attributes(parent_module_id) else { + break; + }; + + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + + for segment in segments.iter().rev() { + string.push_str(segment); + string.push_str("::"); + } + + string.push_str(&module_attributes.name); + + string +} 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..70afc43fe55 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,382 @@ +use lsp_types::{ + Command, 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.first() { + 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 if let Type::Tuple(self_tuple_types) = self_type { + // Tuple types of different lengths seem to also have methods defined on all of them, + // so here we reject methods for tuples where the length doesn't match. + if let Type::Tuple(func_self_tuple_types) = func_self_type { + if self_tuple_types.len() != func_self_tuple_types.len() { + 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 + }; + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => completion_item, + FunctionCompletionKind::NameAndParameters => { + completion_item_with_trigger_parameter_hints_command(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 struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { + field_completion_item(field, typ.to_string()) +} + +pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +} + +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 } +} + +pub(super) fn completion_item_with_trigger_parameter_hints_command( + completion_item: CompletionItem, +) -> CompletionItem { + CompletionItem { + command: Some(Command { + title: "Trigger parameter hints".to_string(), + command: "editor.action.triggerParameterHints".to_string(), + arguments: None, + }), + ..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..a1dd0ba5e94 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -0,0 +1,33 @@ +/// 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() +} + +/// Sort text for auto-import items. We want these to show up after local definitions. +pub(super) fn auto_import_sort_text() -> String { + "6".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..825a7dd0081 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1559 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, + completion_item_with_trigger_parameter_hints_command, crate_completion_item, + field_completion_item, module_completion_item, simple_completion_item, + snippet_completion_item, + }, + sort_text::{auto_import_sort_text, self_mismatch_sort_text}, + }, + on_completion_request, + }, + test_utils, + }; + + use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, + CompletionResponse, DidOpenTextDocumentParams, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_completions(src: &str) -> 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(), + }, + }, + ); + + 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"); + + if let Some(CompletionResponse::Array(items)) = response { + items + } else { + vec![] + } + } + + fn assert_items_match(mut items: Vec, mut expected: Vec) { + items.sort_by_key(|item| item.label.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); + } + + async fn assert_completion(src: &str, expected: Vec) { + let items = get_completions(src).await; + assert_items_match(items, expected); + } + + async fn assert_completion_excluding_auto_import(src: &str, expected: Vec) { + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.additional_text_edits.is_none()).collect(); + assert_items_match(items, expected); + } + + pub(super) fn function_completion_item( + label: impl Into, + insert_text: impl Into, + description: impl Into, + ) -> CompletionItem { + completion_item_with_trigger_parameter_hints_command(snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + Some(description.into()), + )) + } + + #[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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + src, + vec![function_completion_item("hello()", "hello()", "fn()")], + ) + .await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "hello(…)", + "hello(${1:x}, ${2:y})", + "fn(i32, Field)".to_string(), + )], + ) + .await; + } + + #[test] + async fn test_complete_builtin_functions() { + let src = r#" + fn main() { + a>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![ + snippet_completion_item( + "assert(…)", + CompletionItemKind::FUNCTION, + "assert(${1:predicate})", + Some("fn(T)".to_string()), + ), + function_completion_item("assert_constant(…)", "assert_constant(${1:x})", "fn(T)"), + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("bar", "i32")]).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![ + function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)"), + function_completion_item("foobar2(…)", "foobar2(${1:x})", "fn(&mut self, i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![field_completion_item("foo", "i32")]).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![field_completion_item("bar", "Bar")]).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![function_completion_item("foo()", "foo()", "fn(self) -> Foo")]) + .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![field_completion_item("bar", "i32")]).await; + } + + #[test] + async fn test_completes_tuple_fields() { + let src = r#" + fn main() { + let tuple = (1, true); + tuple.>|< + } + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.kind == Some(CompletionItemKind::FIELD)); + let items = items.collect(); + + assert_items_match( + items, + vec![field_completion_item("0", "Field"), field_completion_item("1", "bool")], + ); + } + + #[test] + async fn test_completes_constructor_fields() { + let src = r#" + mod foobar { + struct Foo { + bb: i32, + bbb: Field, + bbbb: bool, + bbbbb: str<6>, + } + } + + fn main() { + foobar::Foo { bbb: 1, b>|<, bbbbb } + } + "#; + assert_completion( + src, + vec![field_completion_item("bb", "i32"), field_completion_item("bbbb", "bool")], + ) + .await; + } + + #[test] + async fn test_completes_trait_methods() { + let src = r#" + trait One { + fn one() -> Self; + } + + fn main() { + One::>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) + .await; + } + + #[test] + async fn test_auto_imports() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + + struct Foo { + } + + impl Foo { + // This is here to make sure it's not offered for completion + fn hello_world() {} + } + } + } + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 0, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + + assert_eq!(item.sort_text, Some(auto_import_sort_text())); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + fn foo() { + hel>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 2, character: 4 }, + end: Position { line: 2, character: 4 }, + }, + new_text: "use bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_not_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + mod baz { + fn foo() { + hel>|< + } + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use crate::foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 7, character: 8 }, + end: Position { line: 7, character: 8 }, + }, + new_text: "use crate::foo::bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_import_inserts_after_last_use() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 8, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + } + + #[test] + async fn test_does_not_auto_import_test_functions() { + let src = r#" + mod foo { + mod bar { + #[test] + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_does_not_auto_import_private_functions() { + let src = r#" + mod foo { + mod bar { + fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_auto_import_suggests_modules_too() { + let src = r#" + mod foo { + mod barbaz { + fn hello_world() {} + } + } + + fn main() { + barb>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "barbaz"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::barbaz)".to_string()), + description: None + }) + ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs new file mode 100644 index 00000000000..b487c8baf36 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs @@ -0,0 +1,132 @@ +/// This file includes the completion logic that's just about +/// traversing the AST without any additional logic. +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, CallExpression, CastExpression, ConstrainStatement, + Expression, ForRange, FunctionReturnType, IndexExpression, InfixExpression, Literal, + MethodCallExpression, NoirTrait, NoirTypeAlias, TraitImplItem, UnresolvedType, + }, + ParsedModule, +}; + +use super::NodeFinder; + +impl<'a> NodeFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + } + + pub(super) fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { + for unresolved_type in unresolved_type { + self.find_in_unresolved_type(unresolved_type); + } + } + + pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { + match return_type { + noirc_frontend::ast::FunctionReturnType::Default(_) => (), + noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { + self.find_in_unresolved_type(unresolved_type); + } + } + } + + pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { + self.find_in_unresolved_type(&noir_type_alias.typ); + } +} 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..a1e083187d3 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, } } @@ -714,9 +722,9 @@ mod inlay_hints_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: start_line, character: 0 }, - end: lsp_types::Position { line: end_line, character: 0 }, + range: Range { + start: Position { line: start_line, character: 0 }, + end: Position { line: end_line, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index e138f839600..e88c7f11465 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -46,6 +46,7 @@ mod inlay_hint; mod profile_run; mod references; mod rename; +mod signature_help; mod test_run; mod tests; @@ -56,7 +57,8 @@ pub(crate) use { goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + rename::on_rename_request, signature_help::on_signature_help_request, + test_run::on_test_run_request, tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -234,13 +236,22 @@ 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, }, completion_item: None, })), + signature_help_provider: Some(lsp_types::OneOf::Right( + lsp_types::SignatureHelpOptions { + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs new file mode 100644 index 00000000000..c2c69185547 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -0,0 +1,291 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, PathString}; +use lsp_types::{ + ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, +}; +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::NodeInterner, + node_interner::ReferenceId, + ParsedModule, Type, +}; + +use crate::{utils, LspState}; + +use super::process_request; + +mod tests; +mod traversal; + +pub(crate) fn on_signature_help_request( + state: &mut LspState, + params: SignatureHelpParams, +) -> impl Future, ResponseError>> { + let uri = params.text_document_position_params.clone().text_document.uri; + + let result = process_request(state, params.text_document_position_params.clone(), |args| { + let path = PathString::from_path(uri.to_file_path().unwrap()); + args.files.get_file_id(&path).and_then(|file_id| { + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) + }) + }) + }); + future::ready(result) +} + +struct SignatureFinder<'a> { + file: FileId, + byte_index: usize, + interner: &'a NodeInterner, + signature_help: Option, +} + +impl<'a> SignatureFinder<'a> { + fn new(file: FileId, byte_index: usize, interner: &'a NodeInterner) -> Self { + Self { file, byte_index, interner, signature_help: None } + } + + fn find(&mut self, parsed_module: &ParsedModule) -> Option { + self.find_in_parsed_module(parsed_module); + + self.signature_help.clone() + } + + fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn try_compute_signature_help( + &mut self, + arguments: &[Expression], + arguments_span: Span, + name_span: Span, + has_self: bool, + ) { + if self.signature_help.is_some() { + return; + } + + if !self.includes_span(arguments_span) { + return; + } + + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + let location = Location::new(name_span, self.file); + + // Check if the call references a named function + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(location) { + let name = self.interner.function_name(&func_id); + let func_meta = self.interner.function_meta(&func_id); + + let signature_information = + self.func_meta_signature_information(func_meta, name, active_parameter, has_self); + self.set_signature_help(signature_information); + return; + } + + // Otherwise, the call must be a reference to an fn type + if let Some(mut typ) = self.interner.type_at_location(location) { + if let Type::Forall(_, forall_typ) = typ { + typ = *forall_typ; + } + if let Type::Function(args, return_type, _, unconstrained) = typ { + let signature_information = self.function_type_signature_information( + &args, + &return_type, + unconstrained, + active_parameter, + ); + self.set_signature_help(signature_information); + } + } + } + + fn func_meta_signature_information( + &self, + func_meta: &FuncMeta, + name: &str, + active_parameter: Option, + has_self: bool, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str("fn "); + label.push_str(name); + label.push('('); + for (index, (pattern, typ, _)) in func_meta.parameters.0.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + if has_self && index == 0 { + if let Type::MutableReference(..) = typ { + label.push_str("&mut self"); + } else { + label.push_str("self"); + } + } else { + let parameter_start = label.chars().count(); + + self.hir_pattern_to_argument(pattern, &mut label); + label.push_str(": "); + label.push_str(&typ.to_string()); + + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([ + parameter_start as u32, + parameter_end as u32, + ]), + documentation: None, + }); + } + } + label.push(')'); + + match &func_meta.return_type { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(typ) => { + label.push_str(" -> "); + label.push_str(&typ.to_string()); + } + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + fn function_type_signature_information( + &self, + args: &[Type], + return_type: &Type, + unconstrained: bool, + active_parameter: Option, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + if unconstrained { + label.push_str("unconstrained "); + } + label.push_str("fn("); + for (index, typ) in args.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(&typ.to_string()); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + if let Type::Unit = return_type { + // Nothing + } else { + label.push_str(" -> "); + label.push_str(&return_type.to_string()); + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + 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 set_signature_help(&mut self, signature_information: SignatureInformation) { + let signature_help = SignatureHelp { + active_parameter: signature_information.active_parameter, + signatures: vec![signature_information], + active_signature: Some(0), + }; + self.signature_help = Some(signature_help); + } + + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs new file mode 100644 index 00000000000..c48ee159084 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -0,0 +1,196 @@ +#[cfg(test)] +mod signature_help_tests { + use crate::{ + notifications::on_did_open_text_document, requests::on_signature_help_request, test_utils, + }; + + use lsp_types::{ + DidOpenTextDocumentParams, ParameterLabel, Position, SignatureHelp, SignatureHelpParams, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_signature_help(src: &str) -> SignatureHelp { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .find_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .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(), + }, + }, + ); + + on_signature_help_request( + &mut state, + SignatureHelpParams { + context: None, + text_document_position_params: 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 }, + }, + ) + .await + .expect("Could not execute on_signature_help_request") + .unwrap() + } + + fn check_label(signature_label: &str, parameter_label: &ParameterLabel, expected_string: &str) { + let ParameterLabel::LabelOffsets(offsets) = parameter_label else { + panic!("Expected label to be LabelOffsets, got {:?}", parameter_label); + }; + + assert_eq!(&signature_label[offsets[0] as usize..offsets[1] as usize], expected_string); + } + + #[test] + async fn test_signature_help_for_call_at_first_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + fn wrapper(x: u32) {} + + fn bar() { + wrapper(foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_call_between_arguments() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1,>|< 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_at_second_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, >|<2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_past_last_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, 2, >|<); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(2)); + } + + #[test] + async fn test_signature_help_for_method_call() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self, x: i32, y: Field) -> u32 { 0 } + } + + fn wrapper(x: u32) {} + + fn bar(f: Foo) { + wrapper(f.foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(self, x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_fn_call() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + let f = foo; + f(>|<1, 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn(i32, Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "i32"); + check_label(&signature.label, ¶ms[1].label, "Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs new file mode 100644 index 00000000000..22f92a86124 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs @@ -0,0 +1,304 @@ +/// This file includes the signature help logic that's just about +/// traversing the AST without any additional logic. +use super::SignatureFinder; + +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, + ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, + IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, + TraitImplItem, TraitItem, TypeImpl, + }, + parser::{Item, ItemKind}, + ParsedModule, +}; + +impl<'a> SignatureFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_item(&mut self, item: &Item) { + if !self.includes_span(item.span) { + return; + } + + match &item.kind { + ItemKind::Submodules(parsed_sub_module) => { + self.find_in_parsed_module(&parsed_sub_module.contents); + } + ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), + ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), + ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), + ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), + ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), + ItemKind::Import(..) + | ItemKind::TypeAlias(_) + | ItemKind::Struct(_) + | ItemKind::ModuleDecl(_) => (), + } + } + + pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { + self.find_in_block_expression(&noir_function.def.body); + } + + pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + for item in &noir_trait_impl.items { + self.find_in_trait_impl_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + for (method, span) in &type_impl.methods { + if self.includes_span(*span) { + self.find_in_noir_function(method); + } + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { + match trait_item { + TraitItem::Function { body, .. } => { + if let Some(body) = body { + self.find_in_block_expression(body); + }; + } + TraitItem::Constant { default_value, .. } => { + if let Some(default_value) = default_value { + self.find_in_expression(default_value); + } + } + TraitItem::Type { .. } => (), + } + } + + pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { + for statement in &block_expression.statements { + if self.includes_span(statement.span) { + self.find_in_statement(statement); + } + } + } + + pub(super) fn find_in_statement(&mut self, statement: &Statement) { + if !self.includes_span(statement.span) { + return; + } + + match &statement.kind { + StatementKind::Let(let_statement) => { + self.find_in_let_statement(let_statement); + } + StatementKind::Constrain(constrain_statement) => { + self.find_in_constrain_statement(constrain_statement); + } + StatementKind::Expression(expression) => { + self.find_in_expression(expression); + } + StatementKind::Assign(assign_statement) => { + self.find_in_assign_statement(assign_statement); + } + StatementKind::For(for_loop_statement) => { + self.find_in_for_loop_statement(for_loop_statement); + } + StatementKind::Comptime(statement) => { + self.find_in_statement(statement); + } + StatementKind::Semi(expression) => { + self.find_in_expression(expression); + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } + } + + pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { + self.find_in_expression(&let_statement.expression); + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { + self.find_in_for_range(&for_loop_statement.range); + self.find_in_expression(&for_loop_statement.block); + } + + pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { + match lvalue { + LValue::Ident(_) => (), + LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), + LValue::Index { array, index, span: _ } => { + self.find_in_lvalue(array); + self.find_in_expression(index); + } + LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), + } + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_expression(&mut self, expression: &Expression) { + match &expression.kind { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Prefix(prefix_expression) => { + self.find_in_expression(&prefix_expression.rhs); + } + ExpressionKind::Index(index_expression) => { + self.find_in_index_expression(index_expression); + } + ExpressionKind::Call(call_expression) => { + self.find_in_call_expression(call_expression, expression.span); + } + ExpressionKind::MethodCall(method_call_expression) => { + self.find_in_method_call_expression(method_call_expression, expression.span); + } + ExpressionKind::Constructor(constructor_expression) => { + self.find_in_constructor_expression(constructor_expression); + } + ExpressionKind::MemberAccess(member_access_expression) => { + self.find_in_member_access_expression(member_access_expression); + } + ExpressionKind::Cast(cast_expression) => { + self.find_in_cast_expression(cast_expression); + } + ExpressionKind::Infix(infix_expression) => { + self.find_in_infix_expression(infix_expression); + } + ExpressionKind::If(if_expression) => { + self.find_in_if_expression(if_expression); + } + ExpressionKind::Tuple(expressions) => { + self.find_in_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Unquote(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Comptime(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Variable(_) + | ExpressionKind::AsTraitPath(_) + | ExpressionKind::Quote(_) + | ExpressionKind::Resolved(_) + | ExpressionKind::Error => (), + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_constructor_expression( + &mut self, + constructor_expression: &ConstructorExpression, + ) { + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_member_access_expression( + &mut self, + member_access_expression: &MemberAccessExpression, + ) { + self.find_in_expression(&member_access_expression.lhs); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { + self.find_in_expression(&if_expression.condition); + self.find_in_expression(&if_expression.consequence); + + if let Some(alternative) = &if_expression.alternative { + self.find_in_expression(alternative); + } + } + + pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { + self.find_in_expression(&lambda.body); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 5afda0d292a..3ac1f35e18e 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ CompletionOptions, DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, - HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, + HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, SignatureHelpOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; @@ -161,6 +161,10 @@ pub(crate) struct ServerCapabilities { /// The server provides completion support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) completion_provider: Option>, + + /// The server provides signature help support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) signature_help_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 56e88dacf2d..046eca88099 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index dabb779ae8d..f3d9f92caaa 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -8,6 +8,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `nargo_cli` to `nargo` diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index a6395d1c8c9..2fd189172d8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -100,8 +100,7 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = - table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]); for program_info in info_report.programs { let program_rows: Vec = program_info.into(); @@ -176,18 +175,31 @@ struct ProgramInfo { #[serde(skip)] expression_width: ExpressionWidth, functions: Vec, + unconstrained_functions_opcodes: usize, + unconstrained_functions: Vec, } impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { + let mut main = vecmap(program_info.functions, |function| { row![ Fm->format!("{}", program_info.package_name), Fc->format!("{}", function.name), format!("{:?}", program_info.expression_width), Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", program_info.unconstrained_functions_opcodes), ] - }) + }); + main.extend(vecmap(program_info.unconstrained_functions, |function| { + row![ + Fm->format!("{}", program_info.package_name), + Fc->format!("{}", function.name), + format!("N/A", ), + Fc->format!("N/A"), + Fc->format!("{}", function.acir_opcodes), + ] + })); + main } } @@ -235,5 +247,31 @@ fn count_opcodes_and_gates_in_program( }) .collect(); - ProgramInfo { package_name: package.name.to_string(), expression_width, functions } + let opcodes_len: Vec = compiled_program + .bytecode + .unconstrained_functions + .iter() + .map(|func| func.bytecode.len()) + .collect(); + let unconstrained_functions_opcodes = compiled_program + .bytecode + .unconstrained_functions + .into_par_iter() + .map(|function| function.bytecode.len()) + .sum(); + let unconstrained_info: Vec = compiled_program + .brillig_names + .clone() + .iter() + .zip(opcodes_len) + .map(|(name, len)| FunctionInfo { name: name.clone(), acir_opcodes: len }) + .collect(); + + ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions, + unconstrained_functions_opcodes, + unconstrained_functions: unconstrained_info, + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 05b2fdb7d52..9868f259097 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] bytecount = "0.6.3" noirc_frontend.workspace = true 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/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index 7c9faa4562a..e4766e44859 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index 4c0c1f75e42..a7baf334bff 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs index aecb620b79d..b79f232d9e8 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs @@ -107,10 +107,8 @@ pub(super) fn arb_abi_type() -> BoxedStrategy { (1..10u32, inner.clone()) .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) .boxed(), - prop::collection::vec(inner.clone(), 1..10) - .prop_map(|fields| { AbiType::Tuple { fields } }) - .boxed(), - (".*", prop::collection::vec((".+", inner), 1..10)) + vec(inner.clone(), 1..10).prop_map(|fields| { AbiType::Tuple { fields } }).boxed(), + (".*", vec((".+", inner), 1..10)) .prop_map(|(path, mut fields)| { // Require that all field names are unique. ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); @@ -141,7 +139,7 @@ fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { prop_compose! { pub(super) fn arb_abi_and_input_map() - (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + (mut parameters_with_values in vec(arb_abi_param_and_value(), 0..100), return_type: Option) -> (Abi, InputMap) { // Require that all parameter names are unique. ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); 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/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index ef4a468b661..b0c11979d17 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -121,8 +121,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result::from_serde(&return_struct) - .map_err(|err| err.to_string().into()) + ::from_serde(&return_struct).map_err(|err| err.to_string().into()) } #[wasm_bindgen(js_name = serializeWitness)] @@ -155,7 +154,7 @@ pub fn abi_decode_error( AbiErrorType::Custom(typ) => { let input_value = decode_value(&mut raw_error.data.into_iter(), &typ)?; let json_types = JsonTypes::try_from_input_value(&input_value, &typ)?; - ::from_serde(&json_types) + ::from_serde(&json_types) .map_err(|err| err.to_string().into()) } } diff --git a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml index 4249604f949..13ff68e423a 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index 91f02157414..ffffc6345d5 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -37,6 +37,8 @@ pub struct ProgramArtifact { pub file_map: BTreeMap, pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } impl From for ProgramArtifact { @@ -49,6 +51,7 @@ impl From for ProgramArtifact { debug_symbols: ProgramDebugInfo { debug_infos: compiled_program.debug }, file_map: compiled_program.file_map, names: compiled_program.names, + brillig_names: compiled_program.brillig_names, } } } @@ -64,6 +67,7 @@ impl From for CompiledProgram { file_map: program.file_map, warnings: vec![], names: program.names, + brillig_names: program.brillig_names, } } } diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 0ccd56b791f..136775d5831 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e072c63f274..0fa12239d05 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -128,7 +128,7 @@ mod tests { } impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { + fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let response = self .mock_responses .get(artifact_path) @@ -173,6 +173,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index f91e50d8716..d7f3cbb9b85 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -188,6 +188,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file @@ -228,6 +229,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: vec!["main".to_string()], }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index 772c87ad1cb..edb3e1b0f08 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -100,7 +100,7 @@ fn format_binary_field_op(op: &BinaryFieldOp) -> String { } } -fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { +fn format_binary_int(op: &BinaryIntOp) -> String { match op { BinaryIntOp::Add => "add".to_string(), BinaryIntOp::Sub => "sub".to_string(), 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 From f988fa7bf465b551f45ac54566305cd5ec2cde38 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sat, 17 Aug 2024 08:02:52 +0000 Subject: [PATCH 06/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From eb12a649a326a27669757f6bc9bcdcd91421b782 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sun, 18 Aug 2024 08:02:05 +0000 Subject: [PATCH 07/21] [1 changes] feat: LSP auto-import completion (https://github.com/noir-lang/noir/pull/5741) fix: Allow comptime code to use break without also being `unconstrained` (https://github.com/noir-lang/noir/pull/5744) feat: add `Expr::as_any_integer` and `Expr::as_member_access` (https://github.com/noir-lang/noir/pull/5742) chore: clarify Field use (https://github.com/noir-lang/noir/pull/5740) feat: add `Expr::as_binary_op` (https://github.com/noir-lang/noir/pull/5734) chore(docs): expanding solidity verifier chain list (https://github.com/noir-lang/noir/pull/5587) chore: apply some new lints across workspace (https://github.com/noir-lang/noir/pull/5736) feat: suggest trait methods in LSP completion (https://github.com/noir-lang/noir/pull/5735) feat: LSP autocomplete constructor fields (https://github.com/noir-lang/noir/pull/5732) feat: add `Expr::as_unary` (https://github.com/noir-lang/noir/pull/5731) chore: count brillig opcodes in nargo info (https://github.com/noir-lang/noir/pull/5189) feat: suggest tuple fields in LSP completion (https://github.com/noir-lang/noir/pull/5730) feat: add `Expr::as_bool` (https://github.com/noir-lang/noir/pull/5729) feat: add `Expr` methods: `as_tuple`, `as_parenthesized`, `as_index`, `as_if` (https://github.com/noir-lang/noir/pull/5726) feat: LSP signature help (https://github.com/noir-lang/noir/pull/5725) chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- noir/noir-repo/Cargo.toml | 6 + noir/noir-repo/acvm-repo/acir/Cargo.toml | 3 + .../acvm-repo/acir/src/circuit/mod.rs | 8 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 3 + .../acvm-repo/acir_field/src/field_element.rs | 4 +- .../acvm-repo/acir_field/src/generic_ark.rs | 4 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../acvm_js/src/js_execution_error.rs | 8 +- .../acvm-repo/blackbox_solver/Cargo.toml | 3 + .../bn254_blackbox_solver/Cargo.toml | 3 + .../src/generator/generators.rs | 2 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 3 + .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 3 + noir/noir-repo/aztec_macros/Cargo.toml | 3 + .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- noir/noir-repo/compiler/fm/Cargo.toml | 3 + .../noir-repo/compiler/noirc_arena/Cargo.toml | 3 + .../compiler/noirc_driver/Cargo.toml | 3 + .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 11 +- .../compiler/noirc_driver/src/program.rs | 2 + .../compiler/noirc_errors/Cargo.toml | 3 + .../compiler/noirc_errors/src/position.rs | 2 +- .../compiler/noirc_evaluator/Cargo.toml | 3 + .../src/brillig/brillig_gen.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 2 + .../src/brillig/brillig_ir/artifact.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 9 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 31 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 9 +- .../noirc_evaluator/src/ssa/ir/map.rs | 2 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 +++- .../src/ssa/ssa_gen/context.rs | 8 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../noirc_frontend/src/ast/expression.rs | 18 +- .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../noirc_frontend/src/elaborator/comptime.rs | 8 + .../src/elaborator/expressions.rs | 21 +- .../noirc_frontend/src/elaborator/mod.rs | 31 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 60 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 3 + .../src/hir/comptime/interpreter/builtin.rs | 269 ++- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/def_collector/dc_mod.rs | 48 +- .../src/hir/def_map/module_def.rs | 2 +- .../src/hir/type_check/errors.rs | 5 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 121 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../compiler/noirc_frontend/src/locations.rs | 76 +- .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 107 +- .../noirc_frontend/src/parser/errors.rs | 4 + .../noirc_frontend/src/parser/parser.rs | 99 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 203 ++ .../compiler/noirc_printable_type/Cargo.toml | 3 + .../compiler/noirc_printable_type/src/lib.rs | 1 + noir/noir-repo/compiler/wasm/Cargo.toml | 4 +- noir/noir-repo/cspell.json | 3 +- .../docs/explainers/explainer-writing-noir.md | 8 +- .../docs/how_to/how-to-solidity-verifier.md | 8 +- .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../how_to/how-to-solidity-verifier.md | 8 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 4 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/expr.nr | 26 + noir/noir-repo/noir_stdlib/src/meta/mod.nr | 8 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 92 + .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_exp/src/main.nr | 79 + .../comptime_fmt_strings/src/main.nr | 2 +- .../comptime_trait_impl/Nargo.toml | 7 + .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 6 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ----- .../verify_honk_proof/src/main.nr | 16 - .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- noir/noir-repo/tooling/acvm_cli/Cargo.toml | 3 + noir/noir-repo/tooling/debugger/Cargo.toml | 3 + noir/noir-repo/tooling/fuzzer/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/src/lib.rs | 11 +- .../tooling/lsp/src/notifications/mod.rs | 8 +- .../tooling/lsp/src/requests/completion.rs | 1740 ++++------------- .../src/requests/completion/auto_import.rs | 195 ++ .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 382 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 33 + .../lsp/src/requests/completion/tests.rs | 1559 +++++++++++++++ .../lsp/src/requests/completion/traversal.rs | 132 ++ .../tooling/lsp/src/requests/hover.rs | 2 +- .../tooling/lsp/src/requests/inlay_hint.rs | 16 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 15 +- .../lsp/src/requests/signature_help.rs | 291 +++ .../lsp/src/requests/signature_help/tests.rs | 196 ++ .../src/requests/signature_help/traversal.rs | 304 +++ noir/noir-repo/tooling/lsp/src/types.rs | 6 +- noir/noir-repo/tooling/nargo/Cargo.toml | 3 + noir/noir-repo/tooling/nargo_cli/Cargo.toml | 3 + .../tooling/nargo_cli/src/cli/info_cmd.rs | 48 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 3 + .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + noir/noir-repo/tooling/nargo_toml/Cargo.toml | 3 + .../noir_js_backend_barretenberg/package.json | 2 +- noir/noir-repo/tooling/noirc_abi/Cargo.toml | 3 + .../tooling/noirc_abi/src/arbitrary.rs | 8 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/src/lib.rs | 5 +- .../tooling/noirc_artifacts/Cargo.toml | 3 + .../tooling/noirc_artifacts/src/program.rs | 4 + noir/noir-repo/tooling/profiler/Cargo.toml | 3 + .../profiler/src/cli/gates_flamegraph_cmd.rs | 3 +- .../src/cli/opcodes_flamegraph_cmd.rs | 2 + .../tooling/profiler/src/opcode_formatter.rs | 2 +- noir/noir-repo/yarn.lock | 13 +- 225 files changed, 6242 insertions(+), 2699 deletions(-) rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/op.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..336e1ccacbf 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +cdbb940a883ae32dd84c667ec06b0d155f2d7520 diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 63509170057..7d9b3254c53 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -50,6 +50,12 @@ rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" +[workspace.lints.rust] +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" +unused_qualifications = "warn" + [workspace.dependencies] # ACVM workspace dependencies diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 88616ccffb5..860b565544b 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 00d0933a3aa..43984e4a922 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -103,7 +103,7 @@ impl ErrorSelector { impl Serialize for ErrorSelector { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.to_string().serialize(serializer) } @@ -112,7 +112,7 @@ impl Serialize for ErrorSelector { impl<'de> Deserialize<'de> for ErrorSelector { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; let as_u64 = s.parse().map_err(serde::de::Error::custom)?; @@ -224,7 +224,7 @@ impl Circuit { } impl Program { - fn write(&self, writer: W) -> std::io::Result<()> { + fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); encoder.write_all(&buf)?; @@ -250,7 +250,7 @@ impl Program { } impl Deserialize<'a>> Program { - fn read(reader: R) -> std::io::Result { + fn read(reader: R) -> std::io::Result { let mut gz_decoder = flate2::read::GzDecoder::new(reader); let mut buf_d = Vec::new(); gz_decoder.read_to_end(&mut buf_d)?; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index a037a453348..c1cffc1334e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 92ccbc4e5f6..2323f008dbe 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -115,7 +115,7 @@ impl From for FieldElement { } } -impl Serialize for FieldElement { +impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -124,7 +124,7 @@ impl Serialize for FieldElement { } } -impl<'de, T: ark_ff::PrimeField> Deserialize<'de> for FieldElement { +impl<'de, T: PrimeField> Deserialize<'de> for FieldElement { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs index f7228e66314..74927c07a36 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs @@ -2,7 +2,7 @@ use num_bigint::BigUint; /// This trait is extremely unstable and WILL have breaking changes. pub trait AcirField: - std::marker::Sized + Sized + std::fmt::Display + std::fmt::Debug + Default @@ -24,7 +24,7 @@ pub trait AcirField: // + From + From + std::hash::Hash - + std::cmp::Eq + + Eq { fn one() -> Self; fn zero() -> Self; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 2ee4d529e5a..bf1170ce073 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 0d90b9ba54f..6b457a8f5f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] 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/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index 23da88247fc..f6a00af7942 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -54,16 +54,14 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; let assertion_payload = match assertion_payload { - Some(raw) => ::from_serde(&raw) + Some(raw) => ::from_serde(&raw) .expect("Cannot serialize assertion payload"), None => JsValue::UNDEFINED, }; let brillig_function_id = match brillig_function_id { - Some(function_id) => { - ::from_serde(&function_id) - .expect("Cannot serialize Brillig function id") - } + Some(function_id) => ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id"), None => JsValue::UNDEFINED, }; diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 10b491c7f67..d0473857e1c 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ab677396c22..9a23f503a06 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index bb51426b33b..a4125014d56 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -91,7 +91,7 @@ mod test { fn test_derive_generators() { let res = derive_generators("test domain".as_bytes(), 128, 0); - let is_unique = |y: Affine, j: usize| -> bool { + let is_unique = |y: Affine, j: usize| -> bool { for (i, res) in res.iter().enumerate() { if i != j && *res == y { return false; diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index 631acbd55d8..9f5a5ce0ee2 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ebd35f1579f..cd9f754ebc0 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed..c9d88e36e28 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index b48f445be36..c367333a6f4 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml index b94f997b7b9..68fc4d1c920 100644 --- a/noir/noir-repo/compiler/noirc_arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -5,3 +5,6 @@ authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a9949f5093a..b244018cc71 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] 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_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 72c95823553..467bda2ca88 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -274,11 +274,8 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = if options.disable_macros { - &[] - } else { - &[&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs( @@ -557,6 +554,7 @@ pub fn compile_no_check( let force_compile = force_compile || options.print_acir || options.show_brillig + || options.force_brillig || options.show_ssa || options.emit_ssa; @@ -578,7 +576,7 @@ pub fn compile_no_check( emit_ssa: if options.emit_ssa { Some(context.package_build_path.clone()) } else { None }, }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + let SsaProgramArtifact { program, debug, warnings, names, brillig_names, error_types, .. } = create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); @@ -593,5 +591,6 @@ pub fn compile_no_check( noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, names, + brillig_names, }) } diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 8e02de0b8b3..88460482928 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -29,4 +29,6 @@ pub struct CompiledProgram { pub warnings: Vec, /// Names of the functions in the program. These are used for more informative debugging and benchmarking. pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index 41b1cd0ff58..61b274c605f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 02b242e8b4d..1792197eab7 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -16,7 +16,7 @@ pub struct Spanned { /// This is important for tests. Two Spanned objects are equal if their content is equal /// They may not have the same span. Use into_span to test for Span being equal specifically -impl PartialEq> for Spanned { +impl PartialEq> for Spanned { fn eq(&self, other: &Spanned) -> bool { self.contents == other.contents } diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 66c770b5064..81feb0b7154 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..d6364cefc9a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -27,5 +27,7 @@ pub(crate) fn convert_ssa_function( BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); } - brillig_context.artifact() + let mut artifact = brillig_context.artifact(); + artifact.name = func.name().to_string(); + artifact } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index dff4da56c1e..fca1f60544d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -55,6 +55,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_invert".to_string(), } } @@ -109,5 +110,6 @@ pub(crate) fn directive_quotient() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_integer_quotient".to_string(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..53964f64170 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -22,6 +22,7 @@ pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, + pub(crate) name: String, } #[derive(Default, Debug, Clone)] @@ -49,6 +50,8 @@ pub(crate) struct BrilligArtifact { locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. call_stack: CallStack, + /// Name of the function, only used for debugging purposes. + pub(crate) name: String, } /// A pointer to a location in the opcode. @@ -81,6 +84,7 @@ impl BrilligArtifact { byte_code: self.byte_code, locations: self.locations, assert_messages: self.assert_messages, + name: self.name, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index b632958f37f..9daf98e606b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -151,6 +151,7 @@ pub struct SsaProgramArtifact { pub main_input_witnesses: Vec, pub main_return_witnesses: Vec, pub names: Vec, + pub brillig_names: Vec, pub error_types: BTreeMap, } @@ -167,6 +168,7 @@ impl SsaProgramArtifact { main_input_witnesses: Vec::default(), main_return_witnesses: Vec::default(), names: Vec::default(), + brillig_names: Vec::default(), error_types, } } @@ -202,8 +204,10 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = - optimize_into_acir(program, options)?; + let ArtifactsAndWarnings( + (generated_acirs, generated_brillig, brillig_function_names, error_types), + ssa_level_warnings, + ) = optimize_into_acir(program, options)?; if options.force_brillig_output { assert_eq!( generated_acirs.len(), @@ -236,6 +240,7 @@ pub fn create_program( program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } + program_artifact.brillig_names = brillig_function_names; Ok(program_artifact) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index b2a73106468..24b30b22ec4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -2005,7 +2005,7 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 22b13aeae79..e7465f62ece 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -235,7 +235,7 @@ impl Debug for AcirDynamicArray { #[derive(Debug, Clone)] pub(crate) enum AcirValue { Var(AcirVar, AcirType), - Array(im::Vector), + Array(Vector), DynamicArray(AcirDynamicArray), } @@ -282,6 +282,7 @@ impl AcirValue { pub(crate) type Artifacts = ( Vec>, Vec>, + Vec, BTreeMap, ); @@ -334,11 +335,13 @@ impl Ssa { } } - let brillig = vecmap(shared_context.generated_brillig, |brillig| BrilligBytecode { - bytecode: brillig.byte_code, - }); + let (brillig_bytecode, brillig_names) = shared_context + .generated_brillig + .into_iter() + .map(|brillig| (BrilligBytecode { bytecode: brillig.byte_code }, brillig.name)) + .unzip(); - Ok((acirs, brillig, self.error_selector_to_type)) + Ok((acirs, brillig_bytecode, brillig_names, self.error_selector_to_type)) } } @@ -955,6 +958,8 @@ impl<'a> Context<'a> { BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); + entry_point.name = func.name().to_string(); + // Link the entry point with all dependencies while let Some(unresolved_fn_label) = entry_point.first_unresolved_function_call() { let artifact = &brillig.find_by_function_label(unresolved_fn_label.clone()); @@ -1645,7 +1650,7 @@ impl<'a> Context<'a> { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - let array: im::Vector = init_values.into(); + let array: Vector = init_values.into(); self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } @@ -2966,7 +2971,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: @@ -3061,7 +3066,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` @@ -3151,7 +3156,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3265,7 +3270,7 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3329,7 +3334,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3403,7 +3408,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3491,7 +3496,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 34d7d595eb9..f06f46d7af8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -171,13 +171,14 @@ impl DataFlowGraph { ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - use InsertInstructionResult::*; match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { - SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification), + SimplifyResult::SimplifiedTo(simplification) => { + InsertInstructionResult::SimplifiedTo(simplification) + } SimplifyResult::SimplifiedToMultiple(simplification) => { - SimplifiedToMultiple(simplification) + InsertInstructionResult::SimplifiedToMultiple(simplification) } - SimplifyResult::Remove => InstructionRemoved, + SimplifyResult::Remove => InsertInstructionResult::InstructionRemoved, result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1c9a31f0c99..f1265553b83 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -50,7 +50,7 @@ impl Id { // Need to manually implement most impls on Id. // Otherwise rust assumes that Id: Hash only if T: Hash, // which isn't true since the T is not used internally. -impl std::hash::Hash for Id { +impl Hash for Id { fn hash(&self, state: &mut H) { self.index.hash(state); } 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..13e5c2445ad 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 @@ -85,7 +85,7 @@ pub(super) struct Loop { } /// The queue of functions remaining to compile -type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; +type FunctionQueue = Vec<(FuncId, IrFunctionId)>; impl<'a> FunctionContext<'a> { /// Create a new FunctionContext to compile the first function in the shared_context's @@ -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 @@ -1005,14 +1005,14 @@ impl SharedContext { } /// Pops the next function from the shared function queue, returning None if the queue is empty. - pub(super) fn pop_next_function_in_queue(&self) -> Option<(ast::FuncId, IrFunctionId)> { + pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() } /// Return the matching id for the given function if known. If it is not known this /// will add the function to the queue of functions to compile, assign it a new id, /// and return this new id. - pub(super) fn get_or_queue_function(&self, id: ast::FuncId) -> IrFunctionId { + pub(super) fn get_or_queue_function(&self, id: FuncId) -> IrFunctionId { // Start a new block to guarantee the destructor for the map lock is released // before map needs to be acquired again in self.functions.write() below { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1373d1bc46e..6b19aff2674 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -396,11 +396,11 @@ impl<'a> FunctionContext<'a> { /// return a reference to each element, for use with the store instruction. fn codegen_array_index( &mut self, - array: super::ir::value::ValueId, - index: super::ir::value::ValueId, + array: ValueId, + index: ValueId, element_type: &ast::Type, location: Location, - length: Option, + length: Option, ) -> Result { // base_index = index * type_size let index = self.make_array_index(index); @@ -438,11 +438,7 @@ impl<'a> FunctionContext<'a> { /// Prepare a slice access. /// Check that the index being used to access a slice element /// is less than the dynamic slice length. - fn codegen_slice_access_check( - &mut self, - index: super::ir::value::ValueId, - length: Option, - ) { + fn codegen_slice_access_check(&mut self, index: ValueId, length: Option) { let index = self.make_array_index(index); // We convert the length as an array index type for comparison let array_len = self 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..1fd1e313c44 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, QuotedTypeId}; -use crate::token::{Attributes, Token, Tokens}; +use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -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 @@ -477,6 +478,20 @@ pub struct FunctionDefinition { pub return_visibility: Visibility, } +impl FunctionDefinition { + pub fn is_private(&self) -> bool { + self.visibility == ItemVisibility::Private + } + + pub fn is_test(&self) -> bool { + if let Some(attribute) = &self.attributes.function { + matches!(attribute, FunctionAttribute::Test(..)) + } else { + false + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Param { pub visibility: Visibility, @@ -587,6 +602,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/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index afa2e7fa7a8..2b78c02e53c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -267,6 +267,14 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_empty_fn(); let module = self.module_id(); self.interner.push_function(id, &function.def, module, location); + + if self.interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + self.interner.register_function(id, &function.def); + } + let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { file_id: self.file, 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..2e4a69c067f 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)); @@ -1290,6 +1297,12 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; + let name = if self.interner.is_in_lsp_mode() { + Some(let_stmt.pattern.name_ident().to_string()) + } else { + None + }; + if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { @@ -1312,8 +1325,9 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner - .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); + if let Some(name) = name { + self.interner.register_global(global_id, name, self.module_id()); + } self.local_module = old_module; self.file = old_file; @@ -1451,9 +1465,12 @@ impl<'context> Elaborator<'context> { /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { - self.current_item.map_or(true, |id| match id { - DependencyId::Function(id) => !self.interner.function_modifiers(&id).is_unconstrained, - _ => true, - }) + !self.in_comptime_context() + && self.current_item.map_or(true, |id| match id { + DependencyId::Function(id) => { + !self.interner.function_modifiers(&id).is_unconstrained + } + _ => true, + }) } } 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..33f8c9d8332 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. @@ -896,6 +897,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "=="), }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -908,6 +910,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "!="), }, BinaryOpKind::Less => match (lhs, rhs) { 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..cec4b1fd434 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}; @@ -17,13 +17,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, UnresolvedType, - UnresolvedTypeData, Visibility, + ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnaryOp, + UnresolvedType, UnresolvedTypeData, Visibility, }, 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, @@ -47,7 +47,15 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), + "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), + "expr_as_if" => expr_as_if(arguments, return_type, location), + "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_integer" => expr_as_integer(arguments, return_type, location), + "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -86,6 +94,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 +105,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 +522,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 +642,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 { @@ -688,6 +758,21 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn as_bool(self) -> Option +fn expr_as_bool( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + Some(Value::Bool(bool)) + } else { + None + } + }) +} + // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( arguments: Vec<(Value, Location)>, @@ -708,6 +793,172 @@ fn expr_as_function_call( }) } +// fn as_if(self) -> Option<(Expr, Expr, Option)> +fn expr_as_if( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::If(if_expr) = expr { + // Get the type of `Option` + let option_type = extract_option_generic_type(return_type.clone()); + let Type::Tuple(option_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(option_types.len(), 3); + let alternative_option_type = option_types[2].clone(); + + let alternative = + option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + + Some(Value::Tuple(vec![ + Value::Expr(if_expr.condition.kind), + Value::Expr(if_expr.consequence.kind), + alternative.ok()?, + ])) + } else { + None + } + }) +} + +// fn as_index(self) -> Option +fn expr_as_index( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Index(index_expr) = expr { + Some(Value::Tuple(vec![ + Value::Expr(index_expr.collection.kind), + Value::Expr(index_expr.index.kind), + ])) + } else { + None + } + }) +} + +// fn as_integer(self) -> Option<(Field, bool)> +fn expr_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + }) +} + +// fn as_member_access(self) -> Option<(Expr, Quoted)> +fn expr_as_member_access( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::MemberAccess(member_access) = expr { + let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); + Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + } else { + None + } + }) +} + +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Expr]> +fn expr_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Tuple(expressions) = expr { + let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(expressions, typ)) + } else { + None + } + }) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -719,8 +970,12 @@ where F: FnOnce(ExpressionKind) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let expr = get_expr(self_argument)?; - let option_value = f(expr); + let mut expression_kind = get_expr(self_argument)?; + while let ExpressionKind::Parenthesized(expression) = expression_kind { + expression_kind = expression.kind; + } + + let option_value = f(expression_kind); option(return_type, option_value) } 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/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index be2afd13507..6436c2562bc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::ModuleAttributes; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -232,6 +232,13 @@ impl<'a> ModCollector<'a> { let location = Location::new(function.span(), self.file_id); context.def_interner.push_function(func_id, &function.def, module, location); + if context.def_interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + context.def_interner.register_function(func_id, &function.def); + } + // Now link this func_id to a crate level map with the noir function and the module id // Encountering a NoirFunction, we retrieve it's module_data to get the namespace // Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner @@ -307,8 +314,8 @@ impl<'a> ModCollector<'a> { }; // Add the struct to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_struct(name, id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_struct(name.clone(), id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -322,10 +329,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Struct(id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_struct(id, name.to_string(), parent_module_id); + } } definition_errors } @@ -383,7 +390,7 @@ impl<'a> ModCollector<'a> { // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_type_alias(name, type_alias_id); + .declare_type_alias(name.clone(), type_alias_id); if let Err((first_def, second_def)) = result { let err = DefCollectorErrorKind::Duplicate { @@ -396,10 +403,11 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Alias(type_alias_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + let name = name.to_string(); + context.def_interner.register_type_alias(type_alias_id, name, parent_module_id); + } } errors } @@ -432,8 +440,8 @@ impl<'a> ModCollector<'a> { }; // Add the trait to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, trait_id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_trait(name.clone(), trait_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -567,10 +575,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location( - ReferenceId::Trait(trait_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_trait(trait_id, name.to_string(), parent_module_id); + } self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -766,6 +774,10 @@ impl<'a> ModCollector<'a> { parent: self.module_id, }, ); + + if context.def_interner.is_in_lsp_mode() { + context.def_interner.register_module(mod_id, mod_name.0.contents.clone()); + } } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs index 54d092f9515..a487bda81b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs @@ -3,7 +3,7 @@ use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; use super::ModuleId; /// A generic ID that references either a module, function, type, interface or global -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), 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/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index c437676b605..0ac13a58ecf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,9 +1,10 @@ use fm::FileId; use noirc_errors::Location; use rangemap::RangeMap; -use rustc_hash::FxHashMap; +use rustc_hash::FxHashMap as HashMap; use crate::{ + ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, macros_api::{NodeInterner, StructId}, node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, @@ -12,7 +13,7 @@ use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] pub(crate) struct LocationIndices { - map_file_to_range: FxHashMap>, + map_file_to_range: HashMap>, } impl LocationIndices { @@ -275,4 +276,75 @@ impl NodeInterner { .neighbors_directed(reference_index, petgraph::Direction::Outgoing) .next() } + + pub(crate) fn register_module(&mut self, id: ModuleId, name: String) { + self.register_name_for_auto_import(name, ModuleDefId::ModuleId(id), ItemVisibility::Public); + } + + pub(crate) fn register_global( + &mut self, + id: GlobalId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Global(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::GlobalId(id), visibility); + } + + pub(crate) fn register_struct( + &mut self, + id: StructId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Struct(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeId(id), visibility); + } + + pub(crate) fn register_trait(&mut self, id: TraitId, name: String, parent_module_id: ModuleId) { + self.add_definition_location(ReferenceId::Trait(id), Some(parent_module_id)); + + self.register_name_for_auto_import(name, ModuleDefId::TraitId(id), ItemVisibility::Public); + } + + pub(crate) fn register_type_alias( + &mut self, + id: TypeAliasId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Alias(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeAliasId(id), visibility); + } + + pub(crate) fn register_function(&mut self, id: FuncId, func_def: &FunctionDefinition) { + self.register_name_for_auto_import( + func_def.name.0.contents.clone(), + ModuleDefId::FunctionId(id), + func_def.visibility, + ); + } + + fn register_name_for_auto_import( + &mut self, + name: String, + module_def_id: ModuleDefId, + visibility: ItemVisibility, + ) { + if !self.lsp_mode { + return; + } + + self.auto_import_names.entry(name).or_default().push((module_def_id, visibility)); + } + + pub fn get_auto_import_names(&self) -> &HashMap> { + &self.auto_import_names + } } 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..46ec2676930 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -19,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::ModuleDefId; use crate::macros_api::UnaryOp; use crate::QuotedType; @@ -179,10 +180,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, @@ -231,6 +232,11 @@ pub struct NodeInterner { // (ReferenceId::Reference and ReferenceId::Local aren't included here) pub(crate) reference_modules: HashMap, + // All names (and their definitions) that can be offered for auto_import. + // These include top-level functions, global variables and types, but excludes + // impl and trait-impl methods. + pub(crate) auto_import_names: HashMap>, + /// Each value currently in scope in the comptime interpreter. /// Each element of the Vec represents a scope with every scope together making /// up all currently visible definitions. The first scope is always the global scope. @@ -602,6 +608,7 @@ impl Default for NodeInterner { reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::default(), reference_modules: HashMap::default(), + auto_import_names: HashMap::default(), comptime_scopes: vec![HashMap::default()], } } @@ -910,6 +917,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored self.add_definition_location(ReferenceId::Function(id), Some(module)); + definition_id } @@ -951,12 +959,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 +1139,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 +1212,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 +1318,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 +1336,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 +1672,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 +1697,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 +1706,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 +1803,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 +1827,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 +2006,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 +2015,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 +2046,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 +2056,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 +2107,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/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index 5140f5a5a8c..8bb56703e8a 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index 20272dfeecb..c8b8c3bb06e 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -6,8 +6,10 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index e939cd583e8..18417b376e5 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -216,7 +216,8 @@ "wasi", "wasmer", "Weierstraß", - "zshell" + "zshell", + "Linea" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md index c8a42c379e6..3ef6e014a2f 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -86,7 +86,7 @@ A Noir program makes a statement that can be verified. It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure + - Conceptually a tree structure - Leaves (inputs) are the `Field` type - Nodes contain arithmetic operations to combine them (gates) - The root is the final result (return value) @@ -99,12 +99,16 @@ You can dig deeper and use the `--print-acir` param to take a closer look at ind ### Use the `Field` type Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. +Some things to be mindful of when using a Field type for a regular integer value: +- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) + - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 +- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size +- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to :::tip Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates ::: -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. ### Use Arithmetic over non-arithmetic operations diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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.32.0/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 54681632543..55c49531017 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,6 +1,32 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_integer)] + fn as_integer(self) -> Option<(Field, bool)> {} + + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + + #[builtin(expr_as_bool)] + fn as_bool(self) -> Option {} + #[builtin(expr_as_function_call)] fn as_function_call(self) -> Option<(Expr, [Expr])> {} + + #[builtin(expr_as_if)] + fn as_if(self) -> Option<(Expr, Expr, Option)> {} + + #[builtin(expr_as_index)] + fn as_index(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_member_access)] + fn as_member_access(self) -> Option<(Expr, Quoted)> {} + + #[builtin(expr_as_tuple)] + fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..d16a8648bc2 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -5,9 +5,11 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +33,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); @@ -41,7 +45,7 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted result } -unconstrained pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { HANDLERS.insert(t, f); } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..ebd89677c50 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -0,0 +1,92 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + 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_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr index 8b6f7b480c7..7b25dcb5097 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -1,8 +1,87 @@ +use std::meta::op::UnaryOp; +use std::meta::op::BinaryOp; + fn main() { comptime { + // Check Expr::as_function_call let expr = quote { foo(bar) }.as_expr().unwrap(); let (_function, args) = expr.as_function_call().unwrap(); assert_eq(args.len(), 1); + + // Check Expr::as_index + let expr = quote { foo[bar] }.as_expr().unwrap(); + let _ = expr.as_index().unwrap(); + + // Check Expr::as_tuple + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + + // Check Expr::as_if + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + + // Check parenthesized expression is automatically unwrapped + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + + // Check Expr::as_bool + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + + // Check Expr::as_unary_op + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + + // Check Expr::as_binary_op + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + + // Check Expr::as_integer + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + + // Check Expr::as_member_access + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); } } + +comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op +} + +comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op +} 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..c5cc7880112 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,14 +4,16 @@ use std::meta::unquote; fn main() { comptime { - foo::<3>(5); + unsafe { + foo::<3>(5) + }; submodule::bar(); } } // Call a different function from the interpreter, then have the // elaborator switch to the middle of foo from its previous scope in main -unconstrained comptime fn foo(x: Field) { +comptime fn foo(x: Field) { assert(modulus_num_bits() != 0); let cond = quote { modulus_num_bits() != 0 }; 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/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3..06dd5e676bd 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `acvm_cli` to `acvm` diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 540d6d11bc0..b9b83d86836 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [build-dependencies] build-data.workspace = true diff --git a/noir/noir-repo/tooling/fuzzer/Cargo.toml b/noir/noir-repo/tooling/fuzzer/Cargo.toml index 106d8abead1..31701991844 100644 --- a/noir/noir-repo/tooling/fuzzer/Cargo.toml +++ b/noir/noir-repo/tooling/fuzzer/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 03c6c9105ba..c5203478f5a 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true# rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ca34d7686fd..366b158ad89 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -23,14 +23,14 @@ use fxhash::FxHashSet; use lsp_types::{ request::{ Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, - References, Rename, + References, Rename, SignatureHelpRequest, }, CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, - workspace::{self, Workspace}, + workspace::Workspace, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -55,7 +55,7 @@ use requests::{ on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, - on_test_run_request, on_tests_request, LspInitializationOptions, + on_signature_help_request, on_test_run_request, on_tests_request, LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -143,6 +143,7 @@ impl NargoLspService { .request::(on_hover_request) .request::(on_inlay_hint_request) .request::(on_completion_request) + .request::(on_signature_help_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -383,7 +384,7 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { pub fn insert_all_files_for_workspace_into_file_manager( state: &LspState, - workspace: &workspace::Workspace, + workspace: &Workspace, file_manager: &mut FileManager, ) { // First add files we cached: these have the source code of files that are modified @@ -410,7 +411,7 @@ fn prepare_package_from_source_string() { let client = ClientSocket::new_closed(); let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); - let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); + let (mut context, crate_id) = prepare_source(source.to_string(), &mut state); let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3a60de15c4a..4d2186badc3 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -223,7 +223,7 @@ mod notification_tests { use super::*; use lsp_types::{ - InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + InlayHintLabel, InlayHintParams, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, WorkDoneProgressParams, }; @@ -270,9 +270,9 @@ mod notification_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: 0, character: 0 }, - end: lsp_types::Position { line: 1, character: 0 }, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 1, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..ad0cf0c5b9b 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,74 +4,46 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; -use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, +use completion_items::{ + crate_completion_item, field_completion_item, simple_completion_item, + struct_field_completion_item, }; +use fm::{FileId, FileMap, PathString}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - ArrayLiteral, AsTraitPath, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, Expression, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, PathSegment, Pattern, - Statement, TraitImplItem, TraitItem, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UseTree, UseTreeKind, + AsTraitPath, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement, + MemberAccessExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, + PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, }, graph::{CrateId, Dependency}, hir::{ 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}, + hir_def::traits::Trait, + 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 crate::{requests::to_lsp_location, utils, LspState}; use super::process_request; +mod auto_import; 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; +mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -94,7 +66,9 @@ pub(crate) fn on_completion_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut finder = NodeFinder::new( + args.files, file_id, + source, byte_index, byte, args.crate_id, @@ -110,7 +84,9 @@ pub(crate) fn on_completion_request( } struct NodeFinder<'a> { + files: &'a FileMap, file: FileId, + lines: Vec<&'a str>, byte_index: usize, byte: Option, /// The module ID of the current file. @@ -129,11 +105,20 @@ struct NodeFinder<'a> { /// Type parameters in the current scope. These are collected when entering /// a struct, a function, etc., and cleared afterwards. type_parameters: HashSet, + /// ModuleDefIds we already suggested, so we don't offer these for auto-import. + suggested_module_def_ids: HashSet, + /// How many nested `mod` we are in deep + nesting: usize, + /// The line where an auto_import must be inserted + auto_import_line: usize, } impl<'a> NodeFinder<'a> { + #[allow(clippy::too_many_arguments)] fn new( + files: &'a FileMap, file: FileId, + source: &'a str, byte_index: usize, byte: Option, krate: CrateId, @@ -153,7 +138,9 @@ impl<'a> NodeFinder<'a> { }; let module_id = ModuleId { krate, local_id }; Self { + files, file, + lines: source.lines().collect(), byte_index, byte, root_module_id, @@ -164,6 +151,9 @@ impl<'a> NodeFinder<'a> { completion_items: Vec::new(), local_variables: HashMap::new(), type_parameters: HashSet::new(), + suggested_module_def_ids: HashSet::new(), + nesting: 0, + auto_import_line: 0, } } @@ -173,17 +163,26 @@ 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()); + } + } - fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); + Some(CompletionResponse::Array(items)) } } fn find_in_item(&mut self, item: &Item) { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; + } + } + if !self.includes_span(item.span) { return; } @@ -206,10 +205,19 @@ impl<'a> NodeFinder<'a> { ModuleId { krate: self.module_id.krate, local_id: *child_module }; } + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + self.find_in_parsed_module(&parsed_sub_module.contents); // Restore the old module before continuing self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; } ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), @@ -253,14 +261,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); @@ -277,10 +277,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_struct.generics); @@ -292,12 +288,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Function { @@ -357,22 +347,22 @@ impl<'a> NodeFinder<'a> { fn find_in_statement(&mut self, statement: &Statement) { match &statement.kind { - noirc_frontend::ast::StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement) => { self.find_in_let_statement(let_statement, true); } - noirc_frontend::ast::StatementKind::Constrain(constrain_statement) => { + StatementKind::Constrain(constrain_statement) => { self.find_in_constrain_statement(constrain_statement); } - noirc_frontend::ast::StatementKind::Expression(expression) => { + StatementKind::Expression(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement) => { self.find_in_assign_statement(assign_statement); } - noirc_frontend::ast::StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement) => { self.find_in_for_loop_statement(for_loop_statement); } - noirc_frontend::ast::StatementKind::Comptime(statement) => { + StatementKind::Comptime(statement) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -381,12 +371,10 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::StatementKind::Semi(expression) => { + StatementKind::Semi(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Break - | noirc_frontend::ast::StatementKind::Continue - | noirc_frontend::ast::StatementKind::Error => (), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } @@ -403,22 +391,6 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - fn find_in_assign_statement( - &mut self, - assign_statement: &noirc_frontend::ast::AssignStatement, - ) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { let old_local_variables = self.local_variables.clone(); let ident = &for_loop_statement.identifier; @@ -432,7 +404,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); @@ -442,69 +425,53 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - fn find_in_expression(&mut self, expression: &Expression) { match &expression.kind { - noirc_frontend::ast::ExpressionKind::Literal(literal) => self.find_in_literal(literal), - noirc_frontend::ast::ExpressionKind::Block(block_expression) => { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { self.find_in_block_expression(block_expression); } - noirc_frontend::ast::ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(prefix_expression) => { self.find_in_expression(&prefix_expression.rhs); } - noirc_frontend::ast::ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(index_expression) => { self.find_in_index_expression(index_expression); } - noirc_frontend::ast::ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(call_expression) => { self.find_in_call_expression(call_expression); } - noirc_frontend::ast::ExpressionKind::MethodCall(method_call_expression) => { + ExpressionKind::MethodCall(method_call_expression) => { self.find_in_method_call_expression(method_call_expression); } - noirc_frontend::ast::ExpressionKind::Constructor(constructor_expression) => { + ExpressionKind::Constructor(constructor_expression) => { self.find_in_constructor_expression(constructor_expression); } - noirc_frontend::ast::ExpressionKind::MemberAccess(member_access_expression) => { + ExpressionKind::MemberAccess(member_access_expression) => { self.find_in_member_access_expression(member_access_expression); } - noirc_frontend::ast::ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(cast_expression) => { self.find_in_cast_expression(cast_expression); } - noirc_frontend::ast::ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(infix_expression) => { self.find_in_infix_expression(infix_expression); } - noirc_frontend::ast::ExpressionKind::If(if_expression) => { + ExpressionKind::If(if_expression) => { self.find_in_if_expression(if_expression); } - noirc_frontend::ast::ExpressionKind::Variable(path) => { + ExpressionKind::Variable(path) => { self.find_in_path(path, RequestedItems::AnyItems); } - noirc_frontend::ast::ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions) => { self.find_in_expressions(expressions); } - noirc_frontend::ast::ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - noirc_frontend::ast::ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Comptime(block_expression, _) => { + ExpressionKind::Comptime(block_expression, _) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -513,58 +480,75 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::ExpressionKind::Quote(_) - | noirc_frontend::ast::ExpressionKind::Resolved(_) - | noirc_frontend::ast::ExpressionKind::Error => (), + ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), } - } - fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), + // "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_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } + fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return; } - } - fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } } - fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - } + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { + return; + }; - fn find_in_method_call_expression(&mut self, method_call_expression: &MethodCallExpression) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); + } + + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } } @@ -572,16 +556,20 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } + 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; + } + } - fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); + self.find_in_expression(&member_access_expression.lhs); } fn find_in_if_expression(&mut self, if_expression: &IfExpression) { @@ -617,21 +605,6 @@ impl<'a> NodeFinder<'a> { self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { if let Some(span) = unresolved_type.span { if !self.includes_span(span) { @@ -640,48 +613,48 @@ impl<'a> NodeFinder<'a> { } match &unresolved_type.typ { - noirc_frontend::ast::UnresolvedTypeData::Array(_, unresolved_type) => { + UnresolvedTypeData::Array(_, unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Slice(unresolved_type) => { + UnresolvedTypeData::Slice(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Parenthesized(unresolved_type) => { + UnresolvedTypeData::Parenthesized(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Named(path, unresolved_types, _) => { + UnresolvedTypeData::Named(path, unresolved_types, _) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::MutableReference(unresolved_type) => { + UnresolvedTypeData::MutableReference(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { + UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); } - noirc_frontend::ast::UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::UnresolvedTypeData::Expression(_) - | noirc_frontend::ast::UnresolvedTypeData::FormatString(_, _) - | noirc_frontend::ast::UnresolvedTypeData::String(_) - | noirc_frontend::ast::UnresolvedTypeData::Unspecified - | noirc_frontend::ast::UnresolvedTypeData::Quoted(_) - | noirc_frontend::ast::UnresolvedTypeData::FieldElement - | noirc_frontend::ast::UnresolvedTypeData::Integer(_, _) - | noirc_frontend::ast::UnresolvedTypeData::Bool - | noirc_frontend::ast::UnresolvedTypeData::Unit - | noirc_frontend::ast::UnresolvedTypeData::Resolved(_) - | noirc_frontend::ast::UnresolvedTypeData::Error => (), + UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Error => (), } } @@ -707,19 +680,51 @@ 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(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods(trait_, &prefix, FunctionKind::Any); + 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, @@ -743,6 +748,7 @@ impl<'a> NodeFinder<'a> { self.type_parameters_completion(&prefix); } } + self.complete_auto_imports(&prefix, requested_items); } } @@ -827,7 +833,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 +919,103 @@ 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::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); + } + } + } + } + } + + fn complete_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + ) { + for (name, func_id) in &trait_.method_ids { + 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); + } + } + } + } + + 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(struct_field_completion_item(name, typ)); + } + } + } + + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +1024,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 +1054,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,9 +1071,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } @@ -977,9 +1084,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } } @@ -1011,131 +1120,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 +1129,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 +1159,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/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs new file mode 100644 index 00000000000..c5710d30b5c --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -0,0 +1,195 @@ +use lsp_types::{Position, Range, TextEdit}; +use noirc_frontend::{ + ast::ItemVisibility, + graph::{CrateId, Dependency}, + hir::def_map::ModuleId, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, +}; + +use super::{ + kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, + name_matches, + sort_text::auto_import_sort_text, + NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn complete_auto_imports(&mut self, prefix: &str, requested_items: RequestedItems) { + for (name, entries) in self.interner.get_auto_import_names() { + if !name_matches(name, prefix) { + continue; + } + + for (module_def_id, visibility) in entries { + if self.suggested_module_def_ids.contains(module_def_id) { + continue; + } + + let Some(mut completion_item) = self.module_def_id_completion_item( + *module_def_id, + name.clone(), + FunctionCompletionKind::NameAndParameters, + FunctionKind::Any, + requested_items, + ) else { + continue; + }; + + let module_full_path; + if let ModuleDefId::ModuleId(module_id) = module_def_id { + module_full_path = module_id_path( + module_id, + &self.module_id, + self.interner, + self.dependencies, + ); + } else { + let Some(parent_module) = get_parent_module(self.interner, *module_def_id) + else { + continue; + }; + + match *visibility { + ItemVisibility::Public => (), + ItemVisibility::Private => { + // Technically this can't be reached because we don't record private items for auto-import, + // but this is here for completeness. + continue; + } + ItemVisibility::PublicCrate => { + if self.module_id.krate != parent_module.krate { + continue; + } + } + } + + module_full_path = module_id_path( + parent_module, + &self.module_id, + self.interner, + self.dependencies, + ); + } + + let full_path = if let ModuleDefId::ModuleId(..) = module_def_id { + module_full_path + } else { + format!("{}::{}", module_full_path, name) + }; + + let mut label_details = completion_item.label_details.unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + + let line = self.auto_import_line as u32; + let character = (self.nesting * 4) as u32; + let indent = " ".repeat(self.nesting * 4); + let mut newlines = "\n"; + + // If the line we are inserting into is not an empty line, insert an extra line to make some room + if let Some(line_text) = self.lines.get(line as usize) { + if !line_text.trim().is_empty() { + newlines = "\n\n"; + } + } + + completion_item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + new_text: format!("use {};{}{}", full_path, newlines, indent), + }]); + + completion_item.sort_text = Some(auto_import_sort_text()); + + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(*module_def_id); + } + } + } +} + +fn get_parent_module(interner: &NodeInterner, module_def_id: ModuleDefId) -> Option<&ModuleId> { + let reference_id = module_def_id_to_reference_id(module_def_id); + interner.reference_module(reference_id) +} + +fn module_def_id_to_reference_id(module_def_id: ModuleDefId) -> ReferenceId { + match module_def_id { + ModuleDefId::ModuleId(id) => ReferenceId::Module(id), + ModuleDefId::FunctionId(id) => ReferenceId::Function(id), + ModuleDefId::TypeId(id) => ReferenceId::Struct(id), + ModuleDefId::TypeAliasId(id) => ReferenceId::Alias(id), + ModuleDefId::TraitId(id) => ReferenceId::Trait(id), + ModuleDefId::GlobalId(id) => ReferenceId::Global(id), + } +} + +/// Computes the path of `module_id` relative to `current_module_id`. +/// If it's not relative, the full path is returned. +fn module_id_path( + module_id: &ModuleId, + current_module_id: &ModuleId, + interner: &NodeInterner, + dependencies: &[Dependency], +) -> String { + let mut string = String::new(); + + let crate_id = module_id.krate; + let crate_name = match crate_id { + CrateId::Root(_) => Some("crate".to_string()), + CrateId::Crate(_) => dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| format!("{}", dep.name)), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Dummy => None, + }; + + let wrote_crate = if let Some(crate_name) = crate_name { + string.push_str(&crate_name); + true + } else { + false + }; + + let Some(module_attributes) = interner.try_module_attributes(module_id) else { + return string; + }; + + if wrote_crate { + string.push_str("::"); + } + + let mut segments = Vec::new(); + let mut current_attributes = module_attributes; + loop { + let parent_module_id = + &ModuleId { krate: module_id.krate, local_id: current_attributes.parent }; + + // If the parent module is the current module we stop because we want a relative path to the module + if current_module_id == parent_module_id { + // When the path is relative we don't want the "crate::" prefix anymore + string = string.strip_prefix("crate::").unwrap_or(&string).to_string(); + break; + } + + let Some(parent_attributes) = interner.try_module_attributes(parent_module_id) else { + break; + }; + + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + + for segment in segments.iter().rev() { + string.push_str(segment); + string.push_str("::"); + } + + string.push_str(&module_attributes.name); + + string +} 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..70afc43fe55 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,382 @@ +use lsp_types::{ + Command, 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.first() { + 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 if let Type::Tuple(self_tuple_types) = self_type { + // Tuple types of different lengths seem to also have methods defined on all of them, + // so here we reject methods for tuples where the length doesn't match. + if let Type::Tuple(func_self_tuple_types) = func_self_type { + if self_tuple_types.len() != func_self_tuple_types.len() { + 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 + }; + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => completion_item, + FunctionCompletionKind::NameAndParameters => { + completion_item_with_trigger_parameter_hints_command(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 struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { + field_completion_item(field, typ.to_string()) +} + +pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +} + +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 } +} + +pub(super) fn completion_item_with_trigger_parameter_hints_command( + completion_item: CompletionItem, +) -> CompletionItem { + CompletionItem { + command: Some(Command { + title: "Trigger parameter hints".to_string(), + command: "editor.action.triggerParameterHints".to_string(), + arguments: None, + }), + ..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..a1dd0ba5e94 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -0,0 +1,33 @@ +/// 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() +} + +/// Sort text for auto-import items. We want these to show up after local definitions. +pub(super) fn auto_import_sort_text() -> String { + "6".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..825a7dd0081 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1559 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, + completion_item_with_trigger_parameter_hints_command, crate_completion_item, + field_completion_item, module_completion_item, simple_completion_item, + snippet_completion_item, + }, + sort_text::{auto_import_sort_text, self_mismatch_sort_text}, + }, + on_completion_request, + }, + test_utils, + }; + + use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, + CompletionResponse, DidOpenTextDocumentParams, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_completions(src: &str) -> 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(), + }, + }, + ); + + 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"); + + if let Some(CompletionResponse::Array(items)) = response { + items + } else { + vec![] + } + } + + fn assert_items_match(mut items: Vec, mut expected: Vec) { + items.sort_by_key(|item| item.label.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); + } + + async fn assert_completion(src: &str, expected: Vec) { + let items = get_completions(src).await; + assert_items_match(items, expected); + } + + async fn assert_completion_excluding_auto_import(src: &str, expected: Vec) { + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.additional_text_edits.is_none()).collect(); + assert_items_match(items, expected); + } + + pub(super) fn function_completion_item( + label: impl Into, + insert_text: impl Into, + description: impl Into, + ) -> CompletionItem { + completion_item_with_trigger_parameter_hints_command(snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + Some(description.into()), + )) + } + + #[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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + src, + vec![function_completion_item("hello()", "hello()", "fn()")], + ) + .await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "hello(…)", + "hello(${1:x}, ${2:y})", + "fn(i32, Field)".to_string(), + )], + ) + .await; + } + + #[test] + async fn test_complete_builtin_functions() { + let src = r#" + fn main() { + a>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![ + snippet_completion_item( + "assert(…)", + CompletionItemKind::FUNCTION, + "assert(${1:predicate})", + Some("fn(T)".to_string()), + ), + function_completion_item("assert_constant(…)", "assert_constant(${1:x})", "fn(T)"), + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("bar", "i32")]).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![ + function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)"), + function_completion_item("foobar2(…)", "foobar2(${1:x})", "fn(&mut self, i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![field_completion_item("foo", "i32")]).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![field_completion_item("bar", "Bar")]).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![function_completion_item("foo()", "foo()", "fn(self) -> Foo")]) + .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![field_completion_item("bar", "i32")]).await; + } + + #[test] + async fn test_completes_tuple_fields() { + let src = r#" + fn main() { + let tuple = (1, true); + tuple.>|< + } + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.kind == Some(CompletionItemKind::FIELD)); + let items = items.collect(); + + assert_items_match( + items, + vec![field_completion_item("0", "Field"), field_completion_item("1", "bool")], + ); + } + + #[test] + async fn test_completes_constructor_fields() { + let src = r#" + mod foobar { + struct Foo { + bb: i32, + bbb: Field, + bbbb: bool, + bbbbb: str<6>, + } + } + + fn main() { + foobar::Foo { bbb: 1, b>|<, bbbbb } + } + "#; + assert_completion( + src, + vec![field_completion_item("bb", "i32"), field_completion_item("bbbb", "bool")], + ) + .await; + } + + #[test] + async fn test_completes_trait_methods() { + let src = r#" + trait One { + fn one() -> Self; + } + + fn main() { + One::>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) + .await; + } + + #[test] + async fn test_auto_imports() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + + struct Foo { + } + + impl Foo { + // This is here to make sure it's not offered for completion + fn hello_world() {} + } + } + } + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 0, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + + assert_eq!(item.sort_text, Some(auto_import_sort_text())); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + fn foo() { + hel>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 2, character: 4 }, + end: Position { line: 2, character: 4 }, + }, + new_text: "use bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_not_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + mod baz { + fn foo() { + hel>|< + } + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use crate::foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 7, character: 8 }, + end: Position { line: 7, character: 8 }, + }, + new_text: "use crate::foo::bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_import_inserts_after_last_use() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 8, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + } + + #[test] + async fn test_does_not_auto_import_test_functions() { + let src = r#" + mod foo { + mod bar { + #[test] + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_does_not_auto_import_private_functions() { + let src = r#" + mod foo { + mod bar { + fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_auto_import_suggests_modules_too() { + let src = r#" + mod foo { + mod barbaz { + fn hello_world() {} + } + } + + fn main() { + barb>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "barbaz"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::barbaz)".to_string()), + description: None + }) + ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs new file mode 100644 index 00000000000..b487c8baf36 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs @@ -0,0 +1,132 @@ +/// This file includes the completion logic that's just about +/// traversing the AST without any additional logic. +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, CallExpression, CastExpression, ConstrainStatement, + Expression, ForRange, FunctionReturnType, IndexExpression, InfixExpression, Literal, + MethodCallExpression, NoirTrait, NoirTypeAlias, TraitImplItem, UnresolvedType, + }, + ParsedModule, +}; + +use super::NodeFinder; + +impl<'a> NodeFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + } + + pub(super) fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { + for unresolved_type in unresolved_type { + self.find_in_unresolved_type(unresolved_type); + } + } + + pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { + match return_type { + noirc_frontend::ast::FunctionReturnType::Default(_) => (), + noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { + self.find_in_unresolved_type(unresolved_type); + } + } + } + + pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { + self.find_in_unresolved_type(&noir_type_alias.typ); + } +} 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..a1e083187d3 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, } } @@ -714,9 +722,9 @@ mod inlay_hints_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: start_line, character: 0 }, - end: lsp_types::Position { line: end_line, character: 0 }, + range: Range { + start: Position { line: start_line, character: 0 }, + end: Position { line: end_line, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index e138f839600..e88c7f11465 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -46,6 +46,7 @@ mod inlay_hint; mod profile_run; mod references; mod rename; +mod signature_help; mod test_run; mod tests; @@ -56,7 +57,8 @@ pub(crate) use { goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + rename::on_rename_request, signature_help::on_signature_help_request, + test_run::on_test_run_request, tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -234,13 +236,22 @@ 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, }, completion_item: None, })), + signature_help_provider: Some(lsp_types::OneOf::Right( + lsp_types::SignatureHelpOptions { + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs new file mode 100644 index 00000000000..c2c69185547 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -0,0 +1,291 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, PathString}; +use lsp_types::{ + ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, +}; +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::NodeInterner, + node_interner::ReferenceId, + ParsedModule, Type, +}; + +use crate::{utils, LspState}; + +use super::process_request; + +mod tests; +mod traversal; + +pub(crate) fn on_signature_help_request( + state: &mut LspState, + params: SignatureHelpParams, +) -> impl Future, ResponseError>> { + let uri = params.text_document_position_params.clone().text_document.uri; + + let result = process_request(state, params.text_document_position_params.clone(), |args| { + let path = PathString::from_path(uri.to_file_path().unwrap()); + args.files.get_file_id(&path).and_then(|file_id| { + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) + }) + }) + }); + future::ready(result) +} + +struct SignatureFinder<'a> { + file: FileId, + byte_index: usize, + interner: &'a NodeInterner, + signature_help: Option, +} + +impl<'a> SignatureFinder<'a> { + fn new(file: FileId, byte_index: usize, interner: &'a NodeInterner) -> Self { + Self { file, byte_index, interner, signature_help: None } + } + + fn find(&mut self, parsed_module: &ParsedModule) -> Option { + self.find_in_parsed_module(parsed_module); + + self.signature_help.clone() + } + + fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn try_compute_signature_help( + &mut self, + arguments: &[Expression], + arguments_span: Span, + name_span: Span, + has_self: bool, + ) { + if self.signature_help.is_some() { + return; + } + + if !self.includes_span(arguments_span) { + return; + } + + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + let location = Location::new(name_span, self.file); + + // Check if the call references a named function + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(location) { + let name = self.interner.function_name(&func_id); + let func_meta = self.interner.function_meta(&func_id); + + let signature_information = + self.func_meta_signature_information(func_meta, name, active_parameter, has_self); + self.set_signature_help(signature_information); + return; + } + + // Otherwise, the call must be a reference to an fn type + if let Some(mut typ) = self.interner.type_at_location(location) { + if let Type::Forall(_, forall_typ) = typ { + typ = *forall_typ; + } + if let Type::Function(args, return_type, _, unconstrained) = typ { + let signature_information = self.function_type_signature_information( + &args, + &return_type, + unconstrained, + active_parameter, + ); + self.set_signature_help(signature_information); + } + } + } + + fn func_meta_signature_information( + &self, + func_meta: &FuncMeta, + name: &str, + active_parameter: Option, + has_self: bool, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str("fn "); + label.push_str(name); + label.push('('); + for (index, (pattern, typ, _)) in func_meta.parameters.0.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + if has_self && index == 0 { + if let Type::MutableReference(..) = typ { + label.push_str("&mut self"); + } else { + label.push_str("self"); + } + } else { + let parameter_start = label.chars().count(); + + self.hir_pattern_to_argument(pattern, &mut label); + label.push_str(": "); + label.push_str(&typ.to_string()); + + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([ + parameter_start as u32, + parameter_end as u32, + ]), + documentation: None, + }); + } + } + label.push(')'); + + match &func_meta.return_type { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(typ) => { + label.push_str(" -> "); + label.push_str(&typ.to_string()); + } + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + fn function_type_signature_information( + &self, + args: &[Type], + return_type: &Type, + unconstrained: bool, + active_parameter: Option, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + if unconstrained { + label.push_str("unconstrained "); + } + label.push_str("fn("); + for (index, typ) in args.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(&typ.to_string()); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + if let Type::Unit = return_type { + // Nothing + } else { + label.push_str(" -> "); + label.push_str(&return_type.to_string()); + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + 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 set_signature_help(&mut self, signature_information: SignatureInformation) { + let signature_help = SignatureHelp { + active_parameter: signature_information.active_parameter, + signatures: vec![signature_information], + active_signature: Some(0), + }; + self.signature_help = Some(signature_help); + } + + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs new file mode 100644 index 00000000000..c48ee159084 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -0,0 +1,196 @@ +#[cfg(test)] +mod signature_help_tests { + use crate::{ + notifications::on_did_open_text_document, requests::on_signature_help_request, test_utils, + }; + + use lsp_types::{ + DidOpenTextDocumentParams, ParameterLabel, Position, SignatureHelp, SignatureHelpParams, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_signature_help(src: &str) -> SignatureHelp { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .find_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .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(), + }, + }, + ); + + on_signature_help_request( + &mut state, + SignatureHelpParams { + context: None, + text_document_position_params: 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 }, + }, + ) + .await + .expect("Could not execute on_signature_help_request") + .unwrap() + } + + fn check_label(signature_label: &str, parameter_label: &ParameterLabel, expected_string: &str) { + let ParameterLabel::LabelOffsets(offsets) = parameter_label else { + panic!("Expected label to be LabelOffsets, got {:?}", parameter_label); + }; + + assert_eq!(&signature_label[offsets[0] as usize..offsets[1] as usize], expected_string); + } + + #[test] + async fn test_signature_help_for_call_at_first_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + fn wrapper(x: u32) {} + + fn bar() { + wrapper(foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_call_between_arguments() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1,>|< 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_at_second_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, >|<2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_past_last_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, 2, >|<); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(2)); + } + + #[test] + async fn test_signature_help_for_method_call() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self, x: i32, y: Field) -> u32 { 0 } + } + + fn wrapper(x: u32) {} + + fn bar(f: Foo) { + wrapper(f.foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(self, x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_fn_call() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + let f = foo; + f(>|<1, 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn(i32, Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "i32"); + check_label(&signature.label, ¶ms[1].label, "Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs new file mode 100644 index 00000000000..22f92a86124 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs @@ -0,0 +1,304 @@ +/// This file includes the signature help logic that's just about +/// traversing the AST without any additional logic. +use super::SignatureFinder; + +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, + ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, + IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, + TraitImplItem, TraitItem, TypeImpl, + }, + parser::{Item, ItemKind}, + ParsedModule, +}; + +impl<'a> SignatureFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_item(&mut self, item: &Item) { + if !self.includes_span(item.span) { + return; + } + + match &item.kind { + ItemKind::Submodules(parsed_sub_module) => { + self.find_in_parsed_module(&parsed_sub_module.contents); + } + ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), + ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), + ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), + ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), + ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), + ItemKind::Import(..) + | ItemKind::TypeAlias(_) + | ItemKind::Struct(_) + | ItemKind::ModuleDecl(_) => (), + } + } + + pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { + self.find_in_block_expression(&noir_function.def.body); + } + + pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + for item in &noir_trait_impl.items { + self.find_in_trait_impl_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + for (method, span) in &type_impl.methods { + if self.includes_span(*span) { + self.find_in_noir_function(method); + } + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { + match trait_item { + TraitItem::Function { body, .. } => { + if let Some(body) = body { + self.find_in_block_expression(body); + }; + } + TraitItem::Constant { default_value, .. } => { + if let Some(default_value) = default_value { + self.find_in_expression(default_value); + } + } + TraitItem::Type { .. } => (), + } + } + + pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { + for statement in &block_expression.statements { + if self.includes_span(statement.span) { + self.find_in_statement(statement); + } + } + } + + pub(super) fn find_in_statement(&mut self, statement: &Statement) { + if !self.includes_span(statement.span) { + return; + } + + match &statement.kind { + StatementKind::Let(let_statement) => { + self.find_in_let_statement(let_statement); + } + StatementKind::Constrain(constrain_statement) => { + self.find_in_constrain_statement(constrain_statement); + } + StatementKind::Expression(expression) => { + self.find_in_expression(expression); + } + StatementKind::Assign(assign_statement) => { + self.find_in_assign_statement(assign_statement); + } + StatementKind::For(for_loop_statement) => { + self.find_in_for_loop_statement(for_loop_statement); + } + StatementKind::Comptime(statement) => { + self.find_in_statement(statement); + } + StatementKind::Semi(expression) => { + self.find_in_expression(expression); + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } + } + + pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { + self.find_in_expression(&let_statement.expression); + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { + self.find_in_for_range(&for_loop_statement.range); + self.find_in_expression(&for_loop_statement.block); + } + + pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { + match lvalue { + LValue::Ident(_) => (), + LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), + LValue::Index { array, index, span: _ } => { + self.find_in_lvalue(array); + self.find_in_expression(index); + } + LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), + } + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_expression(&mut self, expression: &Expression) { + match &expression.kind { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Prefix(prefix_expression) => { + self.find_in_expression(&prefix_expression.rhs); + } + ExpressionKind::Index(index_expression) => { + self.find_in_index_expression(index_expression); + } + ExpressionKind::Call(call_expression) => { + self.find_in_call_expression(call_expression, expression.span); + } + ExpressionKind::MethodCall(method_call_expression) => { + self.find_in_method_call_expression(method_call_expression, expression.span); + } + ExpressionKind::Constructor(constructor_expression) => { + self.find_in_constructor_expression(constructor_expression); + } + ExpressionKind::MemberAccess(member_access_expression) => { + self.find_in_member_access_expression(member_access_expression); + } + ExpressionKind::Cast(cast_expression) => { + self.find_in_cast_expression(cast_expression); + } + ExpressionKind::Infix(infix_expression) => { + self.find_in_infix_expression(infix_expression); + } + ExpressionKind::If(if_expression) => { + self.find_in_if_expression(if_expression); + } + ExpressionKind::Tuple(expressions) => { + self.find_in_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Unquote(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Comptime(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Variable(_) + | ExpressionKind::AsTraitPath(_) + | ExpressionKind::Quote(_) + | ExpressionKind::Resolved(_) + | ExpressionKind::Error => (), + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_constructor_expression( + &mut self, + constructor_expression: &ConstructorExpression, + ) { + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_member_access_expression( + &mut self, + member_access_expression: &MemberAccessExpression, + ) { + self.find_in_expression(&member_access_expression.lhs); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { + self.find_in_expression(&if_expression.condition); + self.find_in_expression(&if_expression.consequence); + + if let Some(alternative) = &if_expression.alternative { + self.find_in_expression(alternative); + } + } + + pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { + self.find_in_expression(&lambda.body); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 5afda0d292a..3ac1f35e18e 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ CompletionOptions, DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, - HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, + HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, SignatureHelpOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; @@ -161,6 +161,10 @@ pub(crate) struct ServerCapabilities { /// The server provides completion support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) completion_provider: Option>, + + /// The server provides signature help support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) signature_help_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 56e88dacf2d..046eca88099 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index dabb779ae8d..f3d9f92caaa 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -8,6 +8,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `nargo_cli` to `nargo` diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index a6395d1c8c9..2fd189172d8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -100,8 +100,7 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = - table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]); for program_info in info_report.programs { let program_rows: Vec = program_info.into(); @@ -176,18 +175,31 @@ struct ProgramInfo { #[serde(skip)] expression_width: ExpressionWidth, functions: Vec, + unconstrained_functions_opcodes: usize, + unconstrained_functions: Vec, } impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { + let mut main = vecmap(program_info.functions, |function| { row![ Fm->format!("{}", program_info.package_name), Fc->format!("{}", function.name), format!("{:?}", program_info.expression_width), Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", program_info.unconstrained_functions_opcodes), ] - }) + }); + main.extend(vecmap(program_info.unconstrained_functions, |function| { + row![ + Fm->format!("{}", program_info.package_name), + Fc->format!("{}", function.name), + format!("N/A", ), + Fc->format!("N/A"), + Fc->format!("{}", function.acir_opcodes), + ] + })); + main } } @@ -235,5 +247,31 @@ fn count_opcodes_and_gates_in_program( }) .collect(); - ProgramInfo { package_name: package.name.to_string(), expression_width, functions } + let opcodes_len: Vec = compiled_program + .bytecode + .unconstrained_functions + .iter() + .map(|func| func.bytecode.len()) + .collect(); + let unconstrained_functions_opcodes = compiled_program + .bytecode + .unconstrained_functions + .into_par_iter() + .map(|function| function.bytecode.len()) + .sum(); + let unconstrained_info: Vec = compiled_program + .brillig_names + .clone() + .iter() + .zip(opcodes_len) + .map(|(name, len)| FunctionInfo { name: name.clone(), acir_opcodes: len }) + .collect(); + + ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions, + unconstrained_functions_opcodes, + unconstrained_functions: unconstrained_info, + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 05b2fdb7d52..9868f259097 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] bytecount = "0.6.3" noirc_frontend.workspace = true 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/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index 7c9faa4562a..e4766e44859 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index 4c0c1f75e42..a7baf334bff 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs index aecb620b79d..b79f232d9e8 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs @@ -107,10 +107,8 @@ pub(super) fn arb_abi_type() -> BoxedStrategy { (1..10u32, inner.clone()) .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) .boxed(), - prop::collection::vec(inner.clone(), 1..10) - .prop_map(|fields| { AbiType::Tuple { fields } }) - .boxed(), - (".*", prop::collection::vec((".+", inner), 1..10)) + vec(inner.clone(), 1..10).prop_map(|fields| { AbiType::Tuple { fields } }).boxed(), + (".*", vec((".+", inner), 1..10)) .prop_map(|(path, mut fields)| { // Require that all field names are unique. ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); @@ -141,7 +139,7 @@ fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { prop_compose! { pub(super) fn arb_abi_and_input_map() - (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + (mut parameters_with_values in vec(arb_abi_param_and_value(), 0..100), return_type: Option) -> (Abi, InputMap) { // Require that all parameter names are unique. ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); 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/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index ef4a468b661..b0c11979d17 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -121,8 +121,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result::from_serde(&return_struct) - .map_err(|err| err.to_string().into()) + ::from_serde(&return_struct).map_err(|err| err.to_string().into()) } #[wasm_bindgen(js_name = serializeWitness)] @@ -155,7 +154,7 @@ pub fn abi_decode_error( AbiErrorType::Custom(typ) => { let input_value = decode_value(&mut raw_error.data.into_iter(), &typ)?; let json_types = JsonTypes::try_from_input_value(&input_value, &typ)?; - ::from_serde(&json_types) + ::from_serde(&json_types) .map_err(|err| err.to_string().into()) } } diff --git a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml index 4249604f949..13ff68e423a 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index 91f02157414..ffffc6345d5 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -37,6 +37,8 @@ pub struct ProgramArtifact { pub file_map: BTreeMap, pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } impl From for ProgramArtifact { @@ -49,6 +51,7 @@ impl From for ProgramArtifact { debug_symbols: ProgramDebugInfo { debug_infos: compiled_program.debug }, file_map: compiled_program.file_map, names: compiled_program.names, + brillig_names: compiled_program.brillig_names, } } } @@ -64,6 +67,7 @@ impl From for CompiledProgram { file_map: program.file_map, warnings: vec![], names: program.names, + brillig_names: program.brillig_names, } } } diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 0ccd56b791f..136775d5831 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e072c63f274..0fa12239d05 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -128,7 +128,7 @@ mod tests { } impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { + fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let response = self .mock_responses .get(artifact_path) @@ -173,6 +173,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index f91e50d8716..d7f3cbb9b85 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -188,6 +188,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file @@ -228,6 +229,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: vec!["main".to_string()], }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index 772c87ad1cb..edb3e1b0f08 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -100,7 +100,7 @@ fn format_binary_field_op(op: &BinaryFieldOp) -> String { } } -fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { +fn format_binary_int(op: &BinaryIntOp) -> String { match op { BinaryIntOp::Add => "add".to_string(), BinaryIntOp::Sub => "sub".to_string(), 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 From a06ae446d6682c51573b11d1410e340b27d4e438 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Sun, 18 Aug 2024 08:02:59 +0000 Subject: [PATCH 08/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 912174b943ba9d9904180db5551b134974e969aa Mon Sep 17 00:00:00 2001 From: AztecBot Date: Mon, 19 Aug 2024 08:02:10 +0000 Subject: [PATCH 09/21] [1 changes] feat: LSP auto-import completion (https://github.com/noir-lang/noir/pull/5741) fix: Allow comptime code to use break without also being `unconstrained` (https://github.com/noir-lang/noir/pull/5744) feat: add `Expr::as_any_integer` and `Expr::as_member_access` (https://github.com/noir-lang/noir/pull/5742) chore: clarify Field use (https://github.com/noir-lang/noir/pull/5740) feat: add `Expr::as_binary_op` (https://github.com/noir-lang/noir/pull/5734) chore(docs): expanding solidity verifier chain list (https://github.com/noir-lang/noir/pull/5587) chore: apply some new lints across workspace (https://github.com/noir-lang/noir/pull/5736) feat: suggest trait methods in LSP completion (https://github.com/noir-lang/noir/pull/5735) feat: LSP autocomplete constructor fields (https://github.com/noir-lang/noir/pull/5732) feat: add `Expr::as_unary` (https://github.com/noir-lang/noir/pull/5731) chore: count brillig opcodes in nargo info (https://github.com/noir-lang/noir/pull/5189) feat: suggest tuple fields in LSP completion (https://github.com/noir-lang/noir/pull/5730) feat: add `Expr::as_bool` (https://github.com/noir-lang/noir/pull/5729) feat: add `Expr` methods: `as_tuple`, `as_parenthesized`, `as_index`, `as_if` (https://github.com/noir-lang/noir/pull/5726) feat: LSP signature help (https://github.com/noir-lang/noir/pull/5725) chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- noir/noir-repo/Cargo.toml | 6 + noir/noir-repo/acvm-repo/acir/Cargo.toml | 3 + .../acvm-repo/acir/src/circuit/mod.rs | 8 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 3 + .../acvm-repo/acir_field/src/field_element.rs | 4 +- .../acvm-repo/acir_field/src/generic_ark.rs | 4 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../acvm_js/src/js_execution_error.rs | 8 +- .../acvm-repo/blackbox_solver/Cargo.toml | 3 + .../bn254_blackbox_solver/Cargo.toml | 3 + .../src/generator/generators.rs | 2 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 3 + .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 3 + noir/noir-repo/aztec_macros/Cargo.toml | 3 + .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- noir/noir-repo/compiler/fm/Cargo.toml | 3 + .../noir-repo/compiler/noirc_arena/Cargo.toml | 3 + .../compiler/noirc_driver/Cargo.toml | 3 + .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 11 +- .../compiler/noirc_driver/src/program.rs | 2 + .../compiler/noirc_errors/Cargo.toml | 3 + .../compiler/noirc_errors/src/position.rs | 2 +- .../compiler/noirc_evaluator/Cargo.toml | 3 + .../src/brillig/brillig_gen.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 2 + .../src/brillig/brillig_ir/artifact.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 9 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 31 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 9 +- .../noirc_evaluator/src/ssa/ir/map.rs | 2 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 +++- .../src/ssa/ssa_gen/context.rs | 8 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../noirc_frontend/src/ast/expression.rs | 18 +- .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../noirc_frontend/src/elaborator/comptime.rs | 8 + .../src/elaborator/expressions.rs | 21 +- .../noirc_frontend/src/elaborator/mod.rs | 31 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 60 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 3 + .../src/hir/comptime/interpreter/builtin.rs | 269 ++- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/def_collector/dc_mod.rs | 48 +- .../src/hir/def_map/module_def.rs | 2 +- .../src/hir/type_check/errors.rs | 5 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 121 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../compiler/noirc_frontend/src/locations.rs | 76 +- .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 107 +- .../noirc_frontend/src/parser/errors.rs | 4 + .../noirc_frontend/src/parser/parser.rs | 99 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 203 ++ .../compiler/noirc_printable_type/Cargo.toml | 3 + .../compiler/noirc_printable_type/src/lib.rs | 1 + noir/noir-repo/compiler/wasm/Cargo.toml | 4 +- noir/noir-repo/cspell.json | 3 +- .../docs/explainers/explainer-writing-noir.md | 8 +- .../docs/how_to/how-to-solidity-verifier.md | 8 +- .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../how_to/how-to-solidity-verifier.md | 8 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 4 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/expr.nr | 26 + noir/noir-repo/noir_stdlib/src/meta/mod.nr | 8 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 92 + .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_exp/src/main.nr | 79 + .../comptime_fmt_strings/src/main.nr | 2 +- .../comptime_trait_impl/Nargo.toml | 7 + .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 6 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ----- .../verify_honk_proof/src/main.nr | 16 - .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- noir/noir-repo/tooling/acvm_cli/Cargo.toml | 3 + noir/noir-repo/tooling/debugger/Cargo.toml | 3 + noir/noir-repo/tooling/fuzzer/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/src/lib.rs | 11 +- .../tooling/lsp/src/notifications/mod.rs | 8 +- .../tooling/lsp/src/requests/completion.rs | 1740 ++++------------- .../src/requests/completion/auto_import.rs | 195 ++ .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 382 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 33 + .../lsp/src/requests/completion/tests.rs | 1559 +++++++++++++++ .../lsp/src/requests/completion/traversal.rs | 132 ++ .../tooling/lsp/src/requests/hover.rs | 2 +- .../tooling/lsp/src/requests/inlay_hint.rs | 16 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 15 +- .../lsp/src/requests/signature_help.rs | 291 +++ .../lsp/src/requests/signature_help/tests.rs | 196 ++ .../src/requests/signature_help/traversal.rs | 304 +++ noir/noir-repo/tooling/lsp/src/types.rs | 6 +- noir/noir-repo/tooling/nargo/Cargo.toml | 3 + noir/noir-repo/tooling/nargo_cli/Cargo.toml | 3 + .../tooling/nargo_cli/src/cli/info_cmd.rs | 48 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 3 + .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + noir/noir-repo/tooling/nargo_toml/Cargo.toml | 3 + .../noir_js_backend_barretenberg/package.json | 2 +- noir/noir-repo/tooling/noirc_abi/Cargo.toml | 3 + .../tooling/noirc_abi/src/arbitrary.rs | 8 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/src/lib.rs | 5 +- .../tooling/noirc_artifacts/Cargo.toml | 3 + .../tooling/noirc_artifacts/src/program.rs | 4 + noir/noir-repo/tooling/profiler/Cargo.toml | 3 + .../profiler/src/cli/gates_flamegraph_cmd.rs | 3 +- .../src/cli/opcodes_flamegraph_cmd.rs | 2 + .../tooling/profiler/src/opcode_formatter.rs | 2 +- noir/noir-repo/yarn.lock | 13 +- 225 files changed, 6242 insertions(+), 2699 deletions(-) rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/op.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..336e1ccacbf 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +cdbb940a883ae32dd84c667ec06b0d155f2d7520 diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 63509170057..7d9b3254c53 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -50,6 +50,12 @@ rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" +[workspace.lints.rust] +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" +unused_qualifications = "warn" + [workspace.dependencies] # ACVM workspace dependencies diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 88616ccffb5..860b565544b 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 00d0933a3aa..43984e4a922 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -103,7 +103,7 @@ impl ErrorSelector { impl Serialize for ErrorSelector { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.to_string().serialize(serializer) } @@ -112,7 +112,7 @@ impl Serialize for ErrorSelector { impl<'de> Deserialize<'de> for ErrorSelector { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; let as_u64 = s.parse().map_err(serde::de::Error::custom)?; @@ -224,7 +224,7 @@ impl Circuit { } impl Program { - fn write(&self, writer: W) -> std::io::Result<()> { + fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); encoder.write_all(&buf)?; @@ -250,7 +250,7 @@ impl Program { } impl Deserialize<'a>> Program { - fn read(reader: R) -> std::io::Result { + fn read(reader: R) -> std::io::Result { let mut gz_decoder = flate2::read::GzDecoder::new(reader); let mut buf_d = Vec::new(); gz_decoder.read_to_end(&mut buf_d)?; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index a037a453348..c1cffc1334e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 92ccbc4e5f6..2323f008dbe 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -115,7 +115,7 @@ impl From for FieldElement { } } -impl Serialize for FieldElement { +impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -124,7 +124,7 @@ impl Serialize for FieldElement { } } -impl<'de, T: ark_ff::PrimeField> Deserialize<'de> for FieldElement { +impl<'de, T: PrimeField> Deserialize<'de> for FieldElement { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs index f7228e66314..74927c07a36 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs @@ -2,7 +2,7 @@ use num_bigint::BigUint; /// This trait is extremely unstable and WILL have breaking changes. pub trait AcirField: - std::marker::Sized + Sized + std::fmt::Display + std::fmt::Debug + Default @@ -24,7 +24,7 @@ pub trait AcirField: // + From + From + std::hash::Hash - + std::cmp::Eq + + Eq { fn one() -> Self; fn zero() -> Self; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 2ee4d529e5a..bf1170ce073 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 0d90b9ba54f..6b457a8f5f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] 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/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index 23da88247fc..f6a00af7942 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -54,16 +54,14 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; let assertion_payload = match assertion_payload { - Some(raw) => ::from_serde(&raw) + Some(raw) => ::from_serde(&raw) .expect("Cannot serialize assertion payload"), None => JsValue::UNDEFINED, }; let brillig_function_id = match brillig_function_id { - Some(function_id) => { - ::from_serde(&function_id) - .expect("Cannot serialize Brillig function id") - } + Some(function_id) => ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id"), None => JsValue::UNDEFINED, }; diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 10b491c7f67..d0473857e1c 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ab677396c22..9a23f503a06 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index bb51426b33b..a4125014d56 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -91,7 +91,7 @@ mod test { fn test_derive_generators() { let res = derive_generators("test domain".as_bytes(), 128, 0); - let is_unique = |y: Affine, j: usize| -> bool { + let is_unique = |y: Affine, j: usize| -> bool { for (i, res) in res.iter().enumerate() { if i != j && *res == y { return false; diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index 631acbd55d8..9f5a5ce0ee2 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ebd35f1579f..cd9f754ebc0 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed..c9d88e36e28 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index b48f445be36..c367333a6f4 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml index b94f997b7b9..68fc4d1c920 100644 --- a/noir/noir-repo/compiler/noirc_arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -5,3 +5,6 @@ authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a9949f5093a..b244018cc71 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] 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_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 72c95823553..467bda2ca88 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -274,11 +274,8 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = if options.disable_macros { - &[] - } else { - &[&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs( @@ -557,6 +554,7 @@ pub fn compile_no_check( let force_compile = force_compile || options.print_acir || options.show_brillig + || options.force_brillig || options.show_ssa || options.emit_ssa; @@ -578,7 +576,7 @@ pub fn compile_no_check( emit_ssa: if options.emit_ssa { Some(context.package_build_path.clone()) } else { None }, }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + let SsaProgramArtifact { program, debug, warnings, names, brillig_names, error_types, .. } = create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); @@ -593,5 +591,6 @@ pub fn compile_no_check( noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, names, + brillig_names, }) } diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 8e02de0b8b3..88460482928 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -29,4 +29,6 @@ pub struct CompiledProgram { pub warnings: Vec, /// Names of the functions in the program. These are used for more informative debugging and benchmarking. pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index 41b1cd0ff58..61b274c605f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 02b242e8b4d..1792197eab7 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -16,7 +16,7 @@ pub struct Spanned { /// This is important for tests. Two Spanned objects are equal if their content is equal /// They may not have the same span. Use into_span to test for Span being equal specifically -impl PartialEq> for Spanned { +impl PartialEq> for Spanned { fn eq(&self, other: &Spanned) -> bool { self.contents == other.contents } diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 66c770b5064..81feb0b7154 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..d6364cefc9a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -27,5 +27,7 @@ pub(crate) fn convert_ssa_function( BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); } - brillig_context.artifact() + let mut artifact = brillig_context.artifact(); + artifact.name = func.name().to_string(); + artifact } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index dff4da56c1e..fca1f60544d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -55,6 +55,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_invert".to_string(), } } @@ -109,5 +110,6 @@ pub(crate) fn directive_quotient() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_integer_quotient".to_string(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..53964f64170 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -22,6 +22,7 @@ pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, + pub(crate) name: String, } #[derive(Default, Debug, Clone)] @@ -49,6 +50,8 @@ pub(crate) struct BrilligArtifact { locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. call_stack: CallStack, + /// Name of the function, only used for debugging purposes. + pub(crate) name: String, } /// A pointer to a location in the opcode. @@ -81,6 +84,7 @@ impl BrilligArtifact { byte_code: self.byte_code, locations: self.locations, assert_messages: self.assert_messages, + name: self.name, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index b632958f37f..9daf98e606b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -151,6 +151,7 @@ pub struct SsaProgramArtifact { pub main_input_witnesses: Vec, pub main_return_witnesses: Vec, pub names: Vec, + pub brillig_names: Vec, pub error_types: BTreeMap, } @@ -167,6 +168,7 @@ impl SsaProgramArtifact { main_input_witnesses: Vec::default(), main_return_witnesses: Vec::default(), names: Vec::default(), + brillig_names: Vec::default(), error_types, } } @@ -202,8 +204,10 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = - optimize_into_acir(program, options)?; + let ArtifactsAndWarnings( + (generated_acirs, generated_brillig, brillig_function_names, error_types), + ssa_level_warnings, + ) = optimize_into_acir(program, options)?; if options.force_brillig_output { assert_eq!( generated_acirs.len(), @@ -236,6 +240,7 @@ pub fn create_program( program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } + program_artifact.brillig_names = brillig_function_names; Ok(program_artifact) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index b2a73106468..24b30b22ec4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -2005,7 +2005,7 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 22b13aeae79..e7465f62ece 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -235,7 +235,7 @@ impl Debug for AcirDynamicArray { #[derive(Debug, Clone)] pub(crate) enum AcirValue { Var(AcirVar, AcirType), - Array(im::Vector), + Array(Vector), DynamicArray(AcirDynamicArray), } @@ -282,6 +282,7 @@ impl AcirValue { pub(crate) type Artifacts = ( Vec>, Vec>, + Vec, BTreeMap, ); @@ -334,11 +335,13 @@ impl Ssa { } } - let brillig = vecmap(shared_context.generated_brillig, |brillig| BrilligBytecode { - bytecode: brillig.byte_code, - }); + let (brillig_bytecode, brillig_names) = shared_context + .generated_brillig + .into_iter() + .map(|brillig| (BrilligBytecode { bytecode: brillig.byte_code }, brillig.name)) + .unzip(); - Ok((acirs, brillig, self.error_selector_to_type)) + Ok((acirs, brillig_bytecode, brillig_names, self.error_selector_to_type)) } } @@ -955,6 +958,8 @@ impl<'a> Context<'a> { BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); + entry_point.name = func.name().to_string(); + // Link the entry point with all dependencies while let Some(unresolved_fn_label) = entry_point.first_unresolved_function_call() { let artifact = &brillig.find_by_function_label(unresolved_fn_label.clone()); @@ -1645,7 +1650,7 @@ impl<'a> Context<'a> { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - let array: im::Vector = init_values.into(); + let array: Vector = init_values.into(); self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } @@ -2966,7 +2971,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: @@ -3061,7 +3066,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` @@ -3151,7 +3156,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3265,7 +3270,7 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3329,7 +3334,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3403,7 +3408,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3491,7 +3496,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 34d7d595eb9..f06f46d7af8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -171,13 +171,14 @@ impl DataFlowGraph { ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - use InsertInstructionResult::*; match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { - SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification), + SimplifyResult::SimplifiedTo(simplification) => { + InsertInstructionResult::SimplifiedTo(simplification) + } SimplifyResult::SimplifiedToMultiple(simplification) => { - SimplifiedToMultiple(simplification) + InsertInstructionResult::SimplifiedToMultiple(simplification) } - SimplifyResult::Remove => InstructionRemoved, + SimplifyResult::Remove => InsertInstructionResult::InstructionRemoved, result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1c9a31f0c99..f1265553b83 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -50,7 +50,7 @@ impl Id { // Need to manually implement most impls on Id. // Otherwise rust assumes that Id: Hash only if T: Hash, // which isn't true since the T is not used internally. -impl std::hash::Hash for Id { +impl Hash for Id { fn hash(&self, state: &mut H) { self.index.hash(state); } 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..13e5c2445ad 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 @@ -85,7 +85,7 @@ pub(super) struct Loop { } /// The queue of functions remaining to compile -type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; +type FunctionQueue = Vec<(FuncId, IrFunctionId)>; impl<'a> FunctionContext<'a> { /// Create a new FunctionContext to compile the first function in the shared_context's @@ -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 @@ -1005,14 +1005,14 @@ impl SharedContext { } /// Pops the next function from the shared function queue, returning None if the queue is empty. - pub(super) fn pop_next_function_in_queue(&self) -> Option<(ast::FuncId, IrFunctionId)> { + pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() } /// Return the matching id for the given function if known. If it is not known this /// will add the function to the queue of functions to compile, assign it a new id, /// and return this new id. - pub(super) fn get_or_queue_function(&self, id: ast::FuncId) -> IrFunctionId { + pub(super) fn get_or_queue_function(&self, id: FuncId) -> IrFunctionId { // Start a new block to guarantee the destructor for the map lock is released // before map needs to be acquired again in self.functions.write() below { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1373d1bc46e..6b19aff2674 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -396,11 +396,11 @@ impl<'a> FunctionContext<'a> { /// return a reference to each element, for use with the store instruction. fn codegen_array_index( &mut self, - array: super::ir::value::ValueId, - index: super::ir::value::ValueId, + array: ValueId, + index: ValueId, element_type: &ast::Type, location: Location, - length: Option, + length: Option, ) -> Result { // base_index = index * type_size let index = self.make_array_index(index); @@ -438,11 +438,7 @@ impl<'a> FunctionContext<'a> { /// Prepare a slice access. /// Check that the index being used to access a slice element /// is less than the dynamic slice length. - fn codegen_slice_access_check( - &mut self, - index: super::ir::value::ValueId, - length: Option, - ) { + fn codegen_slice_access_check(&mut self, index: ValueId, length: Option) { let index = self.make_array_index(index); // We convert the length as an array index type for comparison let array_len = self 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..1fd1e313c44 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, QuotedTypeId}; -use crate::token::{Attributes, Token, Tokens}; +use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -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 @@ -477,6 +478,20 @@ pub struct FunctionDefinition { pub return_visibility: Visibility, } +impl FunctionDefinition { + pub fn is_private(&self) -> bool { + self.visibility == ItemVisibility::Private + } + + pub fn is_test(&self) -> bool { + if let Some(attribute) = &self.attributes.function { + matches!(attribute, FunctionAttribute::Test(..)) + } else { + false + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Param { pub visibility: Visibility, @@ -587,6 +602,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/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index afa2e7fa7a8..2b78c02e53c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -267,6 +267,14 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_empty_fn(); let module = self.module_id(); self.interner.push_function(id, &function.def, module, location); + + if self.interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + self.interner.register_function(id, &function.def); + } + let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { file_id: self.file, 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..2e4a69c067f 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)); @@ -1290,6 +1297,12 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; + let name = if self.interner.is_in_lsp_mode() { + Some(let_stmt.pattern.name_ident().to_string()) + } else { + None + }; + if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { @@ -1312,8 +1325,9 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner - .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); + if let Some(name) = name { + self.interner.register_global(global_id, name, self.module_id()); + } self.local_module = old_module; self.file = old_file; @@ -1451,9 +1465,12 @@ impl<'context> Elaborator<'context> { /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { - self.current_item.map_or(true, |id| match id { - DependencyId::Function(id) => !self.interner.function_modifiers(&id).is_unconstrained, - _ => true, - }) + !self.in_comptime_context() + && self.current_item.map_or(true, |id| match id { + DependencyId::Function(id) => { + !self.interner.function_modifiers(&id).is_unconstrained + } + _ => true, + }) } } 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..33f8c9d8332 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. @@ -896,6 +897,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "=="), }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -908,6 +910,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "!="), }, BinaryOpKind::Less => match (lhs, rhs) { 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..cec4b1fd434 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}; @@ -17,13 +17,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, UnresolvedType, - UnresolvedTypeData, Visibility, + ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, UnaryOp, + UnresolvedType, UnresolvedTypeData, Visibility, }, 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, @@ -47,7 +47,15 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), + "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), + "expr_as_if" => expr_as_if(arguments, return_type, location), + "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_integer" => expr_as_integer(arguments, return_type, location), + "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -86,6 +94,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 +105,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 +522,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 +642,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 { @@ -688,6 +758,21 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn as_bool(self) -> Option +fn expr_as_bool( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + Some(Value::Bool(bool)) + } else { + None + } + }) +} + // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( arguments: Vec<(Value, Location)>, @@ -708,6 +793,172 @@ fn expr_as_function_call( }) } +// fn as_if(self) -> Option<(Expr, Expr, Option)> +fn expr_as_if( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::If(if_expr) = expr { + // Get the type of `Option` + let option_type = extract_option_generic_type(return_type.clone()); + let Type::Tuple(option_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(option_types.len(), 3); + let alternative_option_type = option_types[2].clone(); + + let alternative = + option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + + Some(Value::Tuple(vec![ + Value::Expr(if_expr.condition.kind), + Value::Expr(if_expr.consequence.kind), + alternative.ok()?, + ])) + } else { + None + } + }) +} + +// fn as_index(self) -> Option +fn expr_as_index( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Index(index_expr) = expr { + Some(Value::Tuple(vec![ + Value::Expr(index_expr.collection.kind), + Value::Expr(index_expr.index.kind), + ])) + } else { + None + } + }) +} + +// fn as_integer(self) -> Option<(Field, bool)> +fn expr_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + }) +} + +// fn as_member_access(self) -> Option<(Expr, Quoted)> +fn expr_as_member_access( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::MemberAccess(member_access) = expr { + let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); + Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + } else { + None + } + }) +} + +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Expr]> +fn expr_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Tuple(expressions) = expr { + let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(expressions, typ)) + } else { + None + } + }) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -719,8 +970,12 @@ where F: FnOnce(ExpressionKind) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let expr = get_expr(self_argument)?; - let option_value = f(expr); + let mut expression_kind = get_expr(self_argument)?; + while let ExpressionKind::Parenthesized(expression) = expression_kind { + expression_kind = expression.kind; + } + + let option_value = f(expression_kind); option(return_type, option_value) } 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/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index be2afd13507..6436c2562bc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::ModuleAttributes; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -232,6 +232,13 @@ impl<'a> ModCollector<'a> { let location = Location::new(function.span(), self.file_id); context.def_interner.push_function(func_id, &function.def, module, location); + if context.def_interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + context.def_interner.register_function(func_id, &function.def); + } + // Now link this func_id to a crate level map with the noir function and the module id // Encountering a NoirFunction, we retrieve it's module_data to get the namespace // Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner @@ -307,8 +314,8 @@ impl<'a> ModCollector<'a> { }; // Add the struct to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_struct(name, id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_struct(name.clone(), id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -322,10 +329,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Struct(id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_struct(id, name.to_string(), parent_module_id); + } } definition_errors } @@ -383,7 +390,7 @@ impl<'a> ModCollector<'a> { // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_type_alias(name, type_alias_id); + .declare_type_alias(name.clone(), type_alias_id); if let Err((first_def, second_def)) = result { let err = DefCollectorErrorKind::Duplicate { @@ -396,10 +403,11 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Alias(type_alias_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + let name = name.to_string(); + context.def_interner.register_type_alias(type_alias_id, name, parent_module_id); + } } errors } @@ -432,8 +440,8 @@ impl<'a> ModCollector<'a> { }; // Add the trait to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, trait_id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_trait(name.clone(), trait_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -567,10 +575,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location( - ReferenceId::Trait(trait_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_trait(trait_id, name.to_string(), parent_module_id); + } self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -766,6 +774,10 @@ impl<'a> ModCollector<'a> { parent: self.module_id, }, ); + + if context.def_interner.is_in_lsp_mode() { + context.def_interner.register_module(mod_id, mod_name.0.contents.clone()); + } } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs index 54d092f9515..a487bda81b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs @@ -3,7 +3,7 @@ use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; use super::ModuleId; /// A generic ID that references either a module, function, type, interface or global -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), 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/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index c437676b605..0ac13a58ecf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,9 +1,10 @@ use fm::FileId; use noirc_errors::Location; use rangemap::RangeMap; -use rustc_hash::FxHashMap; +use rustc_hash::FxHashMap as HashMap; use crate::{ + ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, macros_api::{NodeInterner, StructId}, node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, @@ -12,7 +13,7 @@ use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] pub(crate) struct LocationIndices { - map_file_to_range: FxHashMap>, + map_file_to_range: HashMap>, } impl LocationIndices { @@ -275,4 +276,75 @@ impl NodeInterner { .neighbors_directed(reference_index, petgraph::Direction::Outgoing) .next() } + + pub(crate) fn register_module(&mut self, id: ModuleId, name: String) { + self.register_name_for_auto_import(name, ModuleDefId::ModuleId(id), ItemVisibility::Public); + } + + pub(crate) fn register_global( + &mut self, + id: GlobalId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Global(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::GlobalId(id), visibility); + } + + pub(crate) fn register_struct( + &mut self, + id: StructId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Struct(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeId(id), visibility); + } + + pub(crate) fn register_trait(&mut self, id: TraitId, name: String, parent_module_id: ModuleId) { + self.add_definition_location(ReferenceId::Trait(id), Some(parent_module_id)); + + self.register_name_for_auto_import(name, ModuleDefId::TraitId(id), ItemVisibility::Public); + } + + pub(crate) fn register_type_alias( + &mut self, + id: TypeAliasId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Alias(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeAliasId(id), visibility); + } + + pub(crate) fn register_function(&mut self, id: FuncId, func_def: &FunctionDefinition) { + self.register_name_for_auto_import( + func_def.name.0.contents.clone(), + ModuleDefId::FunctionId(id), + func_def.visibility, + ); + } + + fn register_name_for_auto_import( + &mut self, + name: String, + module_def_id: ModuleDefId, + visibility: ItemVisibility, + ) { + if !self.lsp_mode { + return; + } + + self.auto_import_names.entry(name).or_default().push((module_def_id, visibility)); + } + + pub fn get_auto_import_names(&self) -> &HashMap> { + &self.auto_import_names + } } 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..46ec2676930 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -19,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::ModuleDefId; use crate::macros_api::UnaryOp; use crate::QuotedType; @@ -179,10 +180,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, @@ -231,6 +232,11 @@ pub struct NodeInterner { // (ReferenceId::Reference and ReferenceId::Local aren't included here) pub(crate) reference_modules: HashMap, + // All names (and their definitions) that can be offered for auto_import. + // These include top-level functions, global variables and types, but excludes + // impl and trait-impl methods. + pub(crate) auto_import_names: HashMap>, + /// Each value currently in scope in the comptime interpreter. /// Each element of the Vec represents a scope with every scope together making /// up all currently visible definitions. The first scope is always the global scope. @@ -602,6 +608,7 @@ impl Default for NodeInterner { reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::default(), reference_modules: HashMap::default(), + auto_import_names: HashMap::default(), comptime_scopes: vec![HashMap::default()], } } @@ -910,6 +917,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored self.add_definition_location(ReferenceId::Function(id), Some(module)); + definition_id } @@ -951,12 +959,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 +1139,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 +1212,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 +1318,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 +1336,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 +1672,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 +1697,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 +1706,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 +1803,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 +1827,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 +2006,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 +2015,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 +2046,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 +2056,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 +2107,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/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index 5140f5a5a8c..8bb56703e8a 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index 20272dfeecb..c8b8c3bb06e 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -6,8 +6,10 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index e939cd583e8..18417b376e5 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -216,7 +216,8 @@ "wasi", "wasmer", "Weierstraß", - "zshell" + "zshell", + "Linea" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md index c8a42c379e6..3ef6e014a2f 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -86,7 +86,7 @@ A Noir program makes a statement that can be verified. It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure + - Conceptually a tree structure - Leaves (inputs) are the `Field` type - Nodes contain arithmetic operations to combine them (gates) - The root is the final result (return value) @@ -99,12 +99,16 @@ You can dig deeper and use the `--print-acir` param to take a closer look at ind ### Use the `Field` type Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. +Some things to be mindful of when using a Field type for a regular integer value: +- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) + - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 +- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size +- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to :::tip Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates ::: -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. ### Use Arithmetic over non-arithmetic operations diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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.32.0/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 54681632543..55c49531017 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,6 +1,32 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_integer)] + fn as_integer(self) -> Option<(Field, bool)> {} + + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + + #[builtin(expr_as_bool)] + fn as_bool(self) -> Option {} + #[builtin(expr_as_function_call)] fn as_function_call(self) -> Option<(Expr, [Expr])> {} + + #[builtin(expr_as_if)] + fn as_if(self) -> Option<(Expr, Expr, Option)> {} + + #[builtin(expr_as_index)] + fn as_index(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_member_access)] + fn as_member_access(self) -> Option<(Expr, Quoted)> {} + + #[builtin(expr_as_tuple)] + fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..d16a8648bc2 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -5,9 +5,11 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +33,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); @@ -41,7 +45,7 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted result } -unconstrained pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { HANDLERS.insert(t, f); } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..ebd89677c50 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -0,0 +1,92 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + 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_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr index 8b6f7b480c7..7b25dcb5097 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -1,8 +1,87 @@ +use std::meta::op::UnaryOp; +use std::meta::op::BinaryOp; + fn main() { comptime { + // Check Expr::as_function_call let expr = quote { foo(bar) }.as_expr().unwrap(); let (_function, args) = expr.as_function_call().unwrap(); assert_eq(args.len(), 1); + + // Check Expr::as_index + let expr = quote { foo[bar] }.as_expr().unwrap(); + let _ = expr.as_index().unwrap(); + + // Check Expr::as_tuple + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + + // Check Expr::as_if + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + + // Check parenthesized expression is automatically unwrapped + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + + // Check Expr::as_bool + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + + // Check Expr::as_unary_op + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + + // Check Expr::as_binary_op + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + + // Check Expr::as_integer + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + + // Check Expr::as_member_access + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); } } + +comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op +} + +comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op +} 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..c5cc7880112 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,14 +4,16 @@ use std::meta::unquote; fn main() { comptime { - foo::<3>(5); + unsafe { + foo::<3>(5) + }; submodule::bar(); } } // Call a different function from the interpreter, then have the // elaborator switch to the middle of foo from its previous scope in main -unconstrained comptime fn foo(x: Field) { +comptime fn foo(x: Field) { assert(modulus_num_bits() != 0); let cond = quote { modulus_num_bits() != 0 }; 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/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3..06dd5e676bd 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `acvm_cli` to `acvm` diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 540d6d11bc0..b9b83d86836 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [build-dependencies] build-data.workspace = true diff --git a/noir/noir-repo/tooling/fuzzer/Cargo.toml b/noir/noir-repo/tooling/fuzzer/Cargo.toml index 106d8abead1..31701991844 100644 --- a/noir/noir-repo/tooling/fuzzer/Cargo.toml +++ b/noir/noir-repo/tooling/fuzzer/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 03c6c9105ba..c5203478f5a 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true# rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ca34d7686fd..366b158ad89 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -23,14 +23,14 @@ use fxhash::FxHashSet; use lsp_types::{ request::{ Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, - References, Rename, + References, Rename, SignatureHelpRequest, }, CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, - workspace::{self, Workspace}, + workspace::Workspace, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -55,7 +55,7 @@ use requests::{ on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, - on_test_run_request, on_tests_request, LspInitializationOptions, + on_signature_help_request, on_test_run_request, on_tests_request, LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -143,6 +143,7 @@ impl NargoLspService { .request::(on_hover_request) .request::(on_inlay_hint_request) .request::(on_completion_request) + .request::(on_signature_help_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -383,7 +384,7 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { pub fn insert_all_files_for_workspace_into_file_manager( state: &LspState, - workspace: &workspace::Workspace, + workspace: &Workspace, file_manager: &mut FileManager, ) { // First add files we cached: these have the source code of files that are modified @@ -410,7 +411,7 @@ fn prepare_package_from_source_string() { let client = ClientSocket::new_closed(); let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); - let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); + let (mut context, crate_id) = prepare_source(source.to_string(), &mut state); let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3a60de15c4a..4d2186badc3 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -223,7 +223,7 @@ mod notification_tests { use super::*; use lsp_types::{ - InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + InlayHintLabel, InlayHintParams, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, WorkDoneProgressParams, }; @@ -270,9 +270,9 @@ mod notification_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: 0, character: 0 }, - end: lsp_types::Position { line: 1, character: 0 }, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 1, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..ad0cf0c5b9b 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,74 +4,46 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; -use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, +use completion_items::{ + crate_completion_item, field_completion_item, simple_completion_item, + struct_field_completion_item, }; +use fm::{FileId, FileMap, PathString}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - ArrayLiteral, AsTraitPath, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, Expression, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, PathSegment, Pattern, - Statement, TraitImplItem, TraitItem, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UseTree, UseTreeKind, + AsTraitPath, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement, + MemberAccessExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, + PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, }, graph::{CrateId, Dependency}, hir::{ 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}, + hir_def::traits::Trait, + 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 crate::{requests::to_lsp_location, utils, LspState}; use super::process_request; +mod auto_import; 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; +mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -94,7 +66,9 @@ pub(crate) fn on_completion_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut finder = NodeFinder::new( + args.files, file_id, + source, byte_index, byte, args.crate_id, @@ -110,7 +84,9 @@ pub(crate) fn on_completion_request( } struct NodeFinder<'a> { + files: &'a FileMap, file: FileId, + lines: Vec<&'a str>, byte_index: usize, byte: Option, /// The module ID of the current file. @@ -129,11 +105,20 @@ struct NodeFinder<'a> { /// Type parameters in the current scope. These are collected when entering /// a struct, a function, etc., and cleared afterwards. type_parameters: HashSet, + /// ModuleDefIds we already suggested, so we don't offer these for auto-import. + suggested_module_def_ids: HashSet, + /// How many nested `mod` we are in deep + nesting: usize, + /// The line where an auto_import must be inserted + auto_import_line: usize, } impl<'a> NodeFinder<'a> { + #[allow(clippy::too_many_arguments)] fn new( + files: &'a FileMap, file: FileId, + source: &'a str, byte_index: usize, byte: Option, krate: CrateId, @@ -153,7 +138,9 @@ impl<'a> NodeFinder<'a> { }; let module_id = ModuleId { krate, local_id }; Self { + files, file, + lines: source.lines().collect(), byte_index, byte, root_module_id, @@ -164,6 +151,9 @@ impl<'a> NodeFinder<'a> { completion_items: Vec::new(), local_variables: HashMap::new(), type_parameters: HashSet::new(), + suggested_module_def_ids: HashSet::new(), + nesting: 0, + auto_import_line: 0, } } @@ -173,17 +163,26 @@ 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()); + } + } - fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); + Some(CompletionResponse::Array(items)) } } fn find_in_item(&mut self, item: &Item) { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; + } + } + if !self.includes_span(item.span) { return; } @@ -206,10 +205,19 @@ impl<'a> NodeFinder<'a> { ModuleId { krate: self.module_id.krate, local_id: *child_module }; } + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + self.find_in_parsed_module(&parsed_sub_module.contents); // Restore the old module before continuing self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; } ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), @@ -253,14 +261,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); @@ -277,10 +277,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_struct.generics); @@ -292,12 +288,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Function { @@ -357,22 +347,22 @@ impl<'a> NodeFinder<'a> { fn find_in_statement(&mut self, statement: &Statement) { match &statement.kind { - noirc_frontend::ast::StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement) => { self.find_in_let_statement(let_statement, true); } - noirc_frontend::ast::StatementKind::Constrain(constrain_statement) => { + StatementKind::Constrain(constrain_statement) => { self.find_in_constrain_statement(constrain_statement); } - noirc_frontend::ast::StatementKind::Expression(expression) => { + StatementKind::Expression(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement) => { self.find_in_assign_statement(assign_statement); } - noirc_frontend::ast::StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement) => { self.find_in_for_loop_statement(for_loop_statement); } - noirc_frontend::ast::StatementKind::Comptime(statement) => { + StatementKind::Comptime(statement) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -381,12 +371,10 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::StatementKind::Semi(expression) => { + StatementKind::Semi(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Break - | noirc_frontend::ast::StatementKind::Continue - | noirc_frontend::ast::StatementKind::Error => (), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } @@ -403,22 +391,6 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - fn find_in_assign_statement( - &mut self, - assign_statement: &noirc_frontend::ast::AssignStatement, - ) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { let old_local_variables = self.local_variables.clone(); let ident = &for_loop_statement.identifier; @@ -432,7 +404,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); @@ -442,69 +425,53 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - fn find_in_expression(&mut self, expression: &Expression) { match &expression.kind { - noirc_frontend::ast::ExpressionKind::Literal(literal) => self.find_in_literal(literal), - noirc_frontend::ast::ExpressionKind::Block(block_expression) => { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { self.find_in_block_expression(block_expression); } - noirc_frontend::ast::ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(prefix_expression) => { self.find_in_expression(&prefix_expression.rhs); } - noirc_frontend::ast::ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(index_expression) => { self.find_in_index_expression(index_expression); } - noirc_frontend::ast::ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(call_expression) => { self.find_in_call_expression(call_expression); } - noirc_frontend::ast::ExpressionKind::MethodCall(method_call_expression) => { + ExpressionKind::MethodCall(method_call_expression) => { self.find_in_method_call_expression(method_call_expression); } - noirc_frontend::ast::ExpressionKind::Constructor(constructor_expression) => { + ExpressionKind::Constructor(constructor_expression) => { self.find_in_constructor_expression(constructor_expression); } - noirc_frontend::ast::ExpressionKind::MemberAccess(member_access_expression) => { + ExpressionKind::MemberAccess(member_access_expression) => { self.find_in_member_access_expression(member_access_expression); } - noirc_frontend::ast::ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(cast_expression) => { self.find_in_cast_expression(cast_expression); } - noirc_frontend::ast::ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(infix_expression) => { self.find_in_infix_expression(infix_expression); } - noirc_frontend::ast::ExpressionKind::If(if_expression) => { + ExpressionKind::If(if_expression) => { self.find_in_if_expression(if_expression); } - noirc_frontend::ast::ExpressionKind::Variable(path) => { + ExpressionKind::Variable(path) => { self.find_in_path(path, RequestedItems::AnyItems); } - noirc_frontend::ast::ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions) => { self.find_in_expressions(expressions); } - noirc_frontend::ast::ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - noirc_frontend::ast::ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Comptime(block_expression, _) => { + ExpressionKind::Comptime(block_expression, _) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -513,58 +480,75 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::ExpressionKind::Quote(_) - | noirc_frontend::ast::ExpressionKind::Resolved(_) - | noirc_frontend::ast::ExpressionKind::Error => (), + ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), } - } - fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), + // "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_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } + fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return; } - } - fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } } - fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - } + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { + return; + }; - fn find_in_method_call_expression(&mut self, method_call_expression: &MethodCallExpression) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); + } + + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } } @@ -572,16 +556,20 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } + 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; + } + } - fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); + self.find_in_expression(&member_access_expression.lhs); } fn find_in_if_expression(&mut self, if_expression: &IfExpression) { @@ -617,21 +605,6 @@ impl<'a> NodeFinder<'a> { self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { if let Some(span) = unresolved_type.span { if !self.includes_span(span) { @@ -640,48 +613,48 @@ impl<'a> NodeFinder<'a> { } match &unresolved_type.typ { - noirc_frontend::ast::UnresolvedTypeData::Array(_, unresolved_type) => { + UnresolvedTypeData::Array(_, unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Slice(unresolved_type) => { + UnresolvedTypeData::Slice(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Parenthesized(unresolved_type) => { + UnresolvedTypeData::Parenthesized(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Named(path, unresolved_types, _) => { + UnresolvedTypeData::Named(path, unresolved_types, _) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::MutableReference(unresolved_type) => { + UnresolvedTypeData::MutableReference(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { + UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); } - noirc_frontend::ast::UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::UnresolvedTypeData::Expression(_) - | noirc_frontend::ast::UnresolvedTypeData::FormatString(_, _) - | noirc_frontend::ast::UnresolvedTypeData::String(_) - | noirc_frontend::ast::UnresolvedTypeData::Unspecified - | noirc_frontend::ast::UnresolvedTypeData::Quoted(_) - | noirc_frontend::ast::UnresolvedTypeData::FieldElement - | noirc_frontend::ast::UnresolvedTypeData::Integer(_, _) - | noirc_frontend::ast::UnresolvedTypeData::Bool - | noirc_frontend::ast::UnresolvedTypeData::Unit - | noirc_frontend::ast::UnresolvedTypeData::Resolved(_) - | noirc_frontend::ast::UnresolvedTypeData::Error => (), + UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Error => (), } } @@ -707,19 +680,51 @@ 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(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods(trait_, &prefix, FunctionKind::Any); + 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, @@ -743,6 +748,7 @@ impl<'a> NodeFinder<'a> { self.type_parameters_completion(&prefix); } } + self.complete_auto_imports(&prefix, requested_items); } } @@ -827,7 +833,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 +919,103 @@ 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::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); + } + } + } + } + } + + fn complete_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + ) { + for (name, func_id) in &trait_.method_ids { + 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); + } + } + } + } + + 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(struct_field_completion_item(name, typ)); + } + } + } + + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +1024,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 +1054,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,9 +1071,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } @@ -977,9 +1084,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } } @@ -1011,131 +1120,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 +1129,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 +1159,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/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs new file mode 100644 index 00000000000..c5710d30b5c --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -0,0 +1,195 @@ +use lsp_types::{Position, Range, TextEdit}; +use noirc_frontend::{ + ast::ItemVisibility, + graph::{CrateId, Dependency}, + hir::def_map::ModuleId, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, +}; + +use super::{ + kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, + name_matches, + sort_text::auto_import_sort_text, + NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn complete_auto_imports(&mut self, prefix: &str, requested_items: RequestedItems) { + for (name, entries) in self.interner.get_auto_import_names() { + if !name_matches(name, prefix) { + continue; + } + + for (module_def_id, visibility) in entries { + if self.suggested_module_def_ids.contains(module_def_id) { + continue; + } + + let Some(mut completion_item) = self.module_def_id_completion_item( + *module_def_id, + name.clone(), + FunctionCompletionKind::NameAndParameters, + FunctionKind::Any, + requested_items, + ) else { + continue; + }; + + let module_full_path; + if let ModuleDefId::ModuleId(module_id) = module_def_id { + module_full_path = module_id_path( + module_id, + &self.module_id, + self.interner, + self.dependencies, + ); + } else { + let Some(parent_module) = get_parent_module(self.interner, *module_def_id) + else { + continue; + }; + + match *visibility { + ItemVisibility::Public => (), + ItemVisibility::Private => { + // Technically this can't be reached because we don't record private items for auto-import, + // but this is here for completeness. + continue; + } + ItemVisibility::PublicCrate => { + if self.module_id.krate != parent_module.krate { + continue; + } + } + } + + module_full_path = module_id_path( + parent_module, + &self.module_id, + self.interner, + self.dependencies, + ); + } + + let full_path = if let ModuleDefId::ModuleId(..) = module_def_id { + module_full_path + } else { + format!("{}::{}", module_full_path, name) + }; + + let mut label_details = completion_item.label_details.unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + + let line = self.auto_import_line as u32; + let character = (self.nesting * 4) as u32; + let indent = " ".repeat(self.nesting * 4); + let mut newlines = "\n"; + + // If the line we are inserting into is not an empty line, insert an extra line to make some room + if let Some(line_text) = self.lines.get(line as usize) { + if !line_text.trim().is_empty() { + newlines = "\n\n"; + } + } + + completion_item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + new_text: format!("use {};{}{}", full_path, newlines, indent), + }]); + + completion_item.sort_text = Some(auto_import_sort_text()); + + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(*module_def_id); + } + } + } +} + +fn get_parent_module(interner: &NodeInterner, module_def_id: ModuleDefId) -> Option<&ModuleId> { + let reference_id = module_def_id_to_reference_id(module_def_id); + interner.reference_module(reference_id) +} + +fn module_def_id_to_reference_id(module_def_id: ModuleDefId) -> ReferenceId { + match module_def_id { + ModuleDefId::ModuleId(id) => ReferenceId::Module(id), + ModuleDefId::FunctionId(id) => ReferenceId::Function(id), + ModuleDefId::TypeId(id) => ReferenceId::Struct(id), + ModuleDefId::TypeAliasId(id) => ReferenceId::Alias(id), + ModuleDefId::TraitId(id) => ReferenceId::Trait(id), + ModuleDefId::GlobalId(id) => ReferenceId::Global(id), + } +} + +/// Computes the path of `module_id` relative to `current_module_id`. +/// If it's not relative, the full path is returned. +fn module_id_path( + module_id: &ModuleId, + current_module_id: &ModuleId, + interner: &NodeInterner, + dependencies: &[Dependency], +) -> String { + let mut string = String::new(); + + let crate_id = module_id.krate; + let crate_name = match crate_id { + CrateId::Root(_) => Some("crate".to_string()), + CrateId::Crate(_) => dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| format!("{}", dep.name)), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Dummy => None, + }; + + let wrote_crate = if let Some(crate_name) = crate_name { + string.push_str(&crate_name); + true + } else { + false + }; + + let Some(module_attributes) = interner.try_module_attributes(module_id) else { + return string; + }; + + if wrote_crate { + string.push_str("::"); + } + + let mut segments = Vec::new(); + let mut current_attributes = module_attributes; + loop { + let parent_module_id = + &ModuleId { krate: module_id.krate, local_id: current_attributes.parent }; + + // If the parent module is the current module we stop because we want a relative path to the module + if current_module_id == parent_module_id { + // When the path is relative we don't want the "crate::" prefix anymore + string = string.strip_prefix("crate::").unwrap_or(&string).to_string(); + break; + } + + let Some(parent_attributes) = interner.try_module_attributes(parent_module_id) else { + break; + }; + + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + + for segment in segments.iter().rev() { + string.push_str(segment); + string.push_str("::"); + } + + string.push_str(&module_attributes.name); + + string +} 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..70afc43fe55 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,382 @@ +use lsp_types::{ + Command, 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.first() { + 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 if let Type::Tuple(self_tuple_types) = self_type { + // Tuple types of different lengths seem to also have methods defined on all of them, + // so here we reject methods for tuples where the length doesn't match. + if let Type::Tuple(func_self_tuple_types) = func_self_type { + if self_tuple_types.len() != func_self_tuple_types.len() { + 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 + }; + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => completion_item, + FunctionCompletionKind::NameAndParameters => { + completion_item_with_trigger_parameter_hints_command(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 struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { + field_completion_item(field, typ.to_string()) +} + +pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +} + +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 } +} + +pub(super) fn completion_item_with_trigger_parameter_hints_command( + completion_item: CompletionItem, +) -> CompletionItem { + CompletionItem { + command: Some(Command { + title: "Trigger parameter hints".to_string(), + command: "editor.action.triggerParameterHints".to_string(), + arguments: None, + }), + ..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..a1dd0ba5e94 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -0,0 +1,33 @@ +/// 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() +} + +/// Sort text for auto-import items. We want these to show up after local definitions. +pub(super) fn auto_import_sort_text() -> String { + "6".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..825a7dd0081 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1559 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, + completion_item_with_trigger_parameter_hints_command, crate_completion_item, + field_completion_item, module_completion_item, simple_completion_item, + snippet_completion_item, + }, + sort_text::{auto_import_sort_text, self_mismatch_sort_text}, + }, + on_completion_request, + }, + test_utils, + }; + + use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, + CompletionResponse, DidOpenTextDocumentParams, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_completions(src: &str) -> 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(), + }, + }, + ); + + 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"); + + if let Some(CompletionResponse::Array(items)) = response { + items + } else { + vec![] + } + } + + fn assert_items_match(mut items: Vec, mut expected: Vec) { + items.sort_by_key(|item| item.label.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); + } + + async fn assert_completion(src: &str, expected: Vec) { + let items = get_completions(src).await; + assert_items_match(items, expected); + } + + async fn assert_completion_excluding_auto_import(src: &str, expected: Vec) { + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.additional_text_edits.is_none()).collect(); + assert_items_match(items, expected); + } + + pub(super) fn function_completion_item( + label: impl Into, + insert_text: impl Into, + description: impl Into, + ) -> CompletionItem { + completion_item_with_trigger_parameter_hints_command(snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + Some(description.into()), + )) + } + + #[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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + src, + vec![function_completion_item("hello()", "hello()", "fn()")], + ) + .await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "hello(…)", + "hello(${1:x}, ${2:y})", + "fn(i32, Field)".to_string(), + )], + ) + .await; + } + + #[test] + async fn test_complete_builtin_functions() { + let src = r#" + fn main() { + a>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![ + snippet_completion_item( + "assert(…)", + CompletionItemKind::FUNCTION, + "assert(${1:predicate})", + Some("fn(T)".to_string()), + ), + function_completion_item("assert_constant(…)", "assert_constant(${1:x})", "fn(T)"), + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("bar", "i32")]).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![ + function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)"), + function_completion_item("foobar2(…)", "foobar2(${1:x})", "fn(&mut self, i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![field_completion_item("foo", "i32")]).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![field_completion_item("bar", "Bar")]).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![function_completion_item("foo()", "foo()", "fn(self) -> Foo")]) + .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![field_completion_item("bar", "i32")]).await; + } + + #[test] + async fn test_completes_tuple_fields() { + let src = r#" + fn main() { + let tuple = (1, true); + tuple.>|< + } + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.kind == Some(CompletionItemKind::FIELD)); + let items = items.collect(); + + assert_items_match( + items, + vec![field_completion_item("0", "Field"), field_completion_item("1", "bool")], + ); + } + + #[test] + async fn test_completes_constructor_fields() { + let src = r#" + mod foobar { + struct Foo { + bb: i32, + bbb: Field, + bbbb: bool, + bbbbb: str<6>, + } + } + + fn main() { + foobar::Foo { bbb: 1, b>|<, bbbbb } + } + "#; + assert_completion( + src, + vec![field_completion_item("bb", "i32"), field_completion_item("bbbb", "bool")], + ) + .await; + } + + #[test] + async fn test_completes_trait_methods() { + let src = r#" + trait One { + fn one() -> Self; + } + + fn main() { + One::>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) + .await; + } + + #[test] + async fn test_auto_imports() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + + struct Foo { + } + + impl Foo { + // This is here to make sure it's not offered for completion + fn hello_world() {} + } + } + } + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 0, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + + assert_eq!(item.sort_text, Some(auto_import_sort_text())); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + fn foo() { + hel>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 2, character: 4 }, + end: Position { line: 2, character: 4 }, + }, + new_text: "use bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_not_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + mod baz { + fn foo() { + hel>|< + } + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use crate::foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 7, character: 8 }, + end: Position { line: 7, character: 8 }, + }, + new_text: "use crate::foo::bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_import_inserts_after_last_use() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 8, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + } + + #[test] + async fn test_does_not_auto_import_test_functions() { + let src = r#" + mod foo { + mod bar { + #[test] + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_does_not_auto_import_private_functions() { + let src = r#" + mod foo { + mod bar { + fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_auto_import_suggests_modules_too() { + let src = r#" + mod foo { + mod barbaz { + fn hello_world() {} + } + } + + fn main() { + barb>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "barbaz"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::barbaz)".to_string()), + description: None + }) + ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs new file mode 100644 index 00000000000..b487c8baf36 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs @@ -0,0 +1,132 @@ +/// This file includes the completion logic that's just about +/// traversing the AST without any additional logic. +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, CallExpression, CastExpression, ConstrainStatement, + Expression, ForRange, FunctionReturnType, IndexExpression, InfixExpression, Literal, + MethodCallExpression, NoirTrait, NoirTypeAlias, TraitImplItem, UnresolvedType, + }, + ParsedModule, +}; + +use super::NodeFinder; + +impl<'a> NodeFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + } + + pub(super) fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { + for unresolved_type in unresolved_type { + self.find_in_unresolved_type(unresolved_type); + } + } + + pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { + match return_type { + noirc_frontend::ast::FunctionReturnType::Default(_) => (), + noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { + self.find_in_unresolved_type(unresolved_type); + } + } + } + + pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { + self.find_in_unresolved_type(&noir_type_alias.typ); + } +} 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..a1e083187d3 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, } } @@ -714,9 +722,9 @@ mod inlay_hints_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: start_line, character: 0 }, - end: lsp_types::Position { line: end_line, character: 0 }, + range: Range { + start: Position { line: start_line, character: 0 }, + end: Position { line: end_line, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index e138f839600..e88c7f11465 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -46,6 +46,7 @@ mod inlay_hint; mod profile_run; mod references; mod rename; +mod signature_help; mod test_run; mod tests; @@ -56,7 +57,8 @@ pub(crate) use { goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + rename::on_rename_request, signature_help::on_signature_help_request, + test_run::on_test_run_request, tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -234,13 +236,22 @@ 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, }, completion_item: None, })), + signature_help_provider: Some(lsp_types::OneOf::Right( + lsp_types::SignatureHelpOptions { + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs new file mode 100644 index 00000000000..c2c69185547 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -0,0 +1,291 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, PathString}; +use lsp_types::{ + ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, +}; +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::NodeInterner, + node_interner::ReferenceId, + ParsedModule, Type, +}; + +use crate::{utils, LspState}; + +use super::process_request; + +mod tests; +mod traversal; + +pub(crate) fn on_signature_help_request( + state: &mut LspState, + params: SignatureHelpParams, +) -> impl Future, ResponseError>> { + let uri = params.text_document_position_params.clone().text_document.uri; + + let result = process_request(state, params.text_document_position_params.clone(), |args| { + let path = PathString::from_path(uri.to_file_path().unwrap()); + args.files.get_file_id(&path).and_then(|file_id| { + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) + }) + }) + }); + future::ready(result) +} + +struct SignatureFinder<'a> { + file: FileId, + byte_index: usize, + interner: &'a NodeInterner, + signature_help: Option, +} + +impl<'a> SignatureFinder<'a> { + fn new(file: FileId, byte_index: usize, interner: &'a NodeInterner) -> Self { + Self { file, byte_index, interner, signature_help: None } + } + + fn find(&mut self, parsed_module: &ParsedModule) -> Option { + self.find_in_parsed_module(parsed_module); + + self.signature_help.clone() + } + + fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn try_compute_signature_help( + &mut self, + arguments: &[Expression], + arguments_span: Span, + name_span: Span, + has_self: bool, + ) { + if self.signature_help.is_some() { + return; + } + + if !self.includes_span(arguments_span) { + return; + } + + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + let location = Location::new(name_span, self.file); + + // Check if the call references a named function + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(location) { + let name = self.interner.function_name(&func_id); + let func_meta = self.interner.function_meta(&func_id); + + let signature_information = + self.func_meta_signature_information(func_meta, name, active_parameter, has_self); + self.set_signature_help(signature_information); + return; + } + + // Otherwise, the call must be a reference to an fn type + if let Some(mut typ) = self.interner.type_at_location(location) { + if let Type::Forall(_, forall_typ) = typ { + typ = *forall_typ; + } + if let Type::Function(args, return_type, _, unconstrained) = typ { + let signature_information = self.function_type_signature_information( + &args, + &return_type, + unconstrained, + active_parameter, + ); + self.set_signature_help(signature_information); + } + } + } + + fn func_meta_signature_information( + &self, + func_meta: &FuncMeta, + name: &str, + active_parameter: Option, + has_self: bool, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str("fn "); + label.push_str(name); + label.push('('); + for (index, (pattern, typ, _)) in func_meta.parameters.0.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + if has_self && index == 0 { + if let Type::MutableReference(..) = typ { + label.push_str("&mut self"); + } else { + label.push_str("self"); + } + } else { + let parameter_start = label.chars().count(); + + self.hir_pattern_to_argument(pattern, &mut label); + label.push_str(": "); + label.push_str(&typ.to_string()); + + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([ + parameter_start as u32, + parameter_end as u32, + ]), + documentation: None, + }); + } + } + label.push(')'); + + match &func_meta.return_type { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(typ) => { + label.push_str(" -> "); + label.push_str(&typ.to_string()); + } + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + fn function_type_signature_information( + &self, + args: &[Type], + return_type: &Type, + unconstrained: bool, + active_parameter: Option, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + if unconstrained { + label.push_str("unconstrained "); + } + label.push_str("fn("); + for (index, typ) in args.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(&typ.to_string()); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + if let Type::Unit = return_type { + // Nothing + } else { + label.push_str(" -> "); + label.push_str(&return_type.to_string()); + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + 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 set_signature_help(&mut self, signature_information: SignatureInformation) { + let signature_help = SignatureHelp { + active_parameter: signature_information.active_parameter, + signatures: vec![signature_information], + active_signature: Some(0), + }; + self.signature_help = Some(signature_help); + } + + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs new file mode 100644 index 00000000000..c48ee159084 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -0,0 +1,196 @@ +#[cfg(test)] +mod signature_help_tests { + use crate::{ + notifications::on_did_open_text_document, requests::on_signature_help_request, test_utils, + }; + + use lsp_types::{ + DidOpenTextDocumentParams, ParameterLabel, Position, SignatureHelp, SignatureHelpParams, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_signature_help(src: &str) -> SignatureHelp { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .find_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .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(), + }, + }, + ); + + on_signature_help_request( + &mut state, + SignatureHelpParams { + context: None, + text_document_position_params: 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 }, + }, + ) + .await + .expect("Could not execute on_signature_help_request") + .unwrap() + } + + fn check_label(signature_label: &str, parameter_label: &ParameterLabel, expected_string: &str) { + let ParameterLabel::LabelOffsets(offsets) = parameter_label else { + panic!("Expected label to be LabelOffsets, got {:?}", parameter_label); + }; + + assert_eq!(&signature_label[offsets[0] as usize..offsets[1] as usize], expected_string); + } + + #[test] + async fn test_signature_help_for_call_at_first_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + fn wrapper(x: u32) {} + + fn bar() { + wrapper(foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_call_between_arguments() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1,>|< 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_at_second_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, >|<2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_past_last_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, 2, >|<); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(2)); + } + + #[test] + async fn test_signature_help_for_method_call() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self, x: i32, y: Field) -> u32 { 0 } + } + + fn wrapper(x: u32) {} + + fn bar(f: Foo) { + wrapper(f.foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(self, x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_fn_call() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + let f = foo; + f(>|<1, 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn(i32, Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "i32"); + check_label(&signature.label, ¶ms[1].label, "Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs new file mode 100644 index 00000000000..22f92a86124 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs @@ -0,0 +1,304 @@ +/// This file includes the signature help logic that's just about +/// traversing the AST without any additional logic. +use super::SignatureFinder; + +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, + ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, + IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, + TraitImplItem, TraitItem, TypeImpl, + }, + parser::{Item, ItemKind}, + ParsedModule, +}; + +impl<'a> SignatureFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_item(&mut self, item: &Item) { + if !self.includes_span(item.span) { + return; + } + + match &item.kind { + ItemKind::Submodules(parsed_sub_module) => { + self.find_in_parsed_module(&parsed_sub_module.contents); + } + ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), + ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), + ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), + ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), + ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), + ItemKind::Import(..) + | ItemKind::TypeAlias(_) + | ItemKind::Struct(_) + | ItemKind::ModuleDecl(_) => (), + } + } + + pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { + self.find_in_block_expression(&noir_function.def.body); + } + + pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + for item in &noir_trait_impl.items { + self.find_in_trait_impl_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + for (method, span) in &type_impl.methods { + if self.includes_span(*span) { + self.find_in_noir_function(method); + } + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { + match trait_item { + TraitItem::Function { body, .. } => { + if let Some(body) = body { + self.find_in_block_expression(body); + }; + } + TraitItem::Constant { default_value, .. } => { + if let Some(default_value) = default_value { + self.find_in_expression(default_value); + } + } + TraitItem::Type { .. } => (), + } + } + + pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { + for statement in &block_expression.statements { + if self.includes_span(statement.span) { + self.find_in_statement(statement); + } + } + } + + pub(super) fn find_in_statement(&mut self, statement: &Statement) { + if !self.includes_span(statement.span) { + return; + } + + match &statement.kind { + StatementKind::Let(let_statement) => { + self.find_in_let_statement(let_statement); + } + StatementKind::Constrain(constrain_statement) => { + self.find_in_constrain_statement(constrain_statement); + } + StatementKind::Expression(expression) => { + self.find_in_expression(expression); + } + StatementKind::Assign(assign_statement) => { + self.find_in_assign_statement(assign_statement); + } + StatementKind::For(for_loop_statement) => { + self.find_in_for_loop_statement(for_loop_statement); + } + StatementKind::Comptime(statement) => { + self.find_in_statement(statement); + } + StatementKind::Semi(expression) => { + self.find_in_expression(expression); + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } + } + + pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { + self.find_in_expression(&let_statement.expression); + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { + self.find_in_for_range(&for_loop_statement.range); + self.find_in_expression(&for_loop_statement.block); + } + + pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { + match lvalue { + LValue::Ident(_) => (), + LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), + LValue::Index { array, index, span: _ } => { + self.find_in_lvalue(array); + self.find_in_expression(index); + } + LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), + } + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_expression(&mut self, expression: &Expression) { + match &expression.kind { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Prefix(prefix_expression) => { + self.find_in_expression(&prefix_expression.rhs); + } + ExpressionKind::Index(index_expression) => { + self.find_in_index_expression(index_expression); + } + ExpressionKind::Call(call_expression) => { + self.find_in_call_expression(call_expression, expression.span); + } + ExpressionKind::MethodCall(method_call_expression) => { + self.find_in_method_call_expression(method_call_expression, expression.span); + } + ExpressionKind::Constructor(constructor_expression) => { + self.find_in_constructor_expression(constructor_expression); + } + ExpressionKind::MemberAccess(member_access_expression) => { + self.find_in_member_access_expression(member_access_expression); + } + ExpressionKind::Cast(cast_expression) => { + self.find_in_cast_expression(cast_expression); + } + ExpressionKind::Infix(infix_expression) => { + self.find_in_infix_expression(infix_expression); + } + ExpressionKind::If(if_expression) => { + self.find_in_if_expression(if_expression); + } + ExpressionKind::Tuple(expressions) => { + self.find_in_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Unquote(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Comptime(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Variable(_) + | ExpressionKind::AsTraitPath(_) + | ExpressionKind::Quote(_) + | ExpressionKind::Resolved(_) + | ExpressionKind::Error => (), + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_constructor_expression( + &mut self, + constructor_expression: &ConstructorExpression, + ) { + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_member_access_expression( + &mut self, + member_access_expression: &MemberAccessExpression, + ) { + self.find_in_expression(&member_access_expression.lhs); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { + self.find_in_expression(&if_expression.condition); + self.find_in_expression(&if_expression.consequence); + + if let Some(alternative) = &if_expression.alternative { + self.find_in_expression(alternative); + } + } + + pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { + self.find_in_expression(&lambda.body); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 5afda0d292a..3ac1f35e18e 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ CompletionOptions, DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, - HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, + HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, SignatureHelpOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; @@ -161,6 +161,10 @@ pub(crate) struct ServerCapabilities { /// The server provides completion support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) completion_provider: Option>, + + /// The server provides signature help support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) signature_help_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 56e88dacf2d..046eca88099 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index dabb779ae8d..f3d9f92caaa 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -8,6 +8,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `nargo_cli` to `nargo` diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index a6395d1c8c9..2fd189172d8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -100,8 +100,7 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = - table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]); for program_info in info_report.programs { let program_rows: Vec = program_info.into(); @@ -176,18 +175,31 @@ struct ProgramInfo { #[serde(skip)] expression_width: ExpressionWidth, functions: Vec, + unconstrained_functions_opcodes: usize, + unconstrained_functions: Vec, } impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { + let mut main = vecmap(program_info.functions, |function| { row![ Fm->format!("{}", program_info.package_name), Fc->format!("{}", function.name), format!("{:?}", program_info.expression_width), Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", program_info.unconstrained_functions_opcodes), ] - }) + }); + main.extend(vecmap(program_info.unconstrained_functions, |function| { + row![ + Fm->format!("{}", program_info.package_name), + Fc->format!("{}", function.name), + format!("N/A", ), + Fc->format!("N/A"), + Fc->format!("{}", function.acir_opcodes), + ] + })); + main } } @@ -235,5 +247,31 @@ fn count_opcodes_and_gates_in_program( }) .collect(); - ProgramInfo { package_name: package.name.to_string(), expression_width, functions } + let opcodes_len: Vec = compiled_program + .bytecode + .unconstrained_functions + .iter() + .map(|func| func.bytecode.len()) + .collect(); + let unconstrained_functions_opcodes = compiled_program + .bytecode + .unconstrained_functions + .into_par_iter() + .map(|function| function.bytecode.len()) + .sum(); + let unconstrained_info: Vec = compiled_program + .brillig_names + .clone() + .iter() + .zip(opcodes_len) + .map(|(name, len)| FunctionInfo { name: name.clone(), acir_opcodes: len }) + .collect(); + + ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions, + unconstrained_functions_opcodes, + unconstrained_functions: unconstrained_info, + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 05b2fdb7d52..9868f259097 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] bytecount = "0.6.3" noirc_frontend.workspace = true 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/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index 7c9faa4562a..e4766e44859 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index 4c0c1f75e42..a7baf334bff 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs index aecb620b79d..b79f232d9e8 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs @@ -107,10 +107,8 @@ pub(super) fn arb_abi_type() -> BoxedStrategy { (1..10u32, inner.clone()) .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) .boxed(), - prop::collection::vec(inner.clone(), 1..10) - .prop_map(|fields| { AbiType::Tuple { fields } }) - .boxed(), - (".*", prop::collection::vec((".+", inner), 1..10)) + vec(inner.clone(), 1..10).prop_map(|fields| { AbiType::Tuple { fields } }).boxed(), + (".*", vec((".+", inner), 1..10)) .prop_map(|(path, mut fields)| { // Require that all field names are unique. ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); @@ -141,7 +139,7 @@ fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { prop_compose! { pub(super) fn arb_abi_and_input_map() - (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + (mut parameters_with_values in vec(arb_abi_param_and_value(), 0..100), return_type: Option) -> (Abi, InputMap) { // Require that all parameter names are unique. ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); 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/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index ef4a468b661..b0c11979d17 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -121,8 +121,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result::from_serde(&return_struct) - .map_err(|err| err.to_string().into()) + ::from_serde(&return_struct).map_err(|err| err.to_string().into()) } #[wasm_bindgen(js_name = serializeWitness)] @@ -155,7 +154,7 @@ pub fn abi_decode_error( AbiErrorType::Custom(typ) => { let input_value = decode_value(&mut raw_error.data.into_iter(), &typ)?; let json_types = JsonTypes::try_from_input_value(&input_value, &typ)?; - ::from_serde(&json_types) + ::from_serde(&json_types) .map_err(|err| err.to_string().into()) } } diff --git a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml index 4249604f949..13ff68e423a 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index 91f02157414..ffffc6345d5 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -37,6 +37,8 @@ pub struct ProgramArtifact { pub file_map: BTreeMap, pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } impl From for ProgramArtifact { @@ -49,6 +51,7 @@ impl From for ProgramArtifact { debug_symbols: ProgramDebugInfo { debug_infos: compiled_program.debug }, file_map: compiled_program.file_map, names: compiled_program.names, + brillig_names: compiled_program.brillig_names, } } } @@ -64,6 +67,7 @@ impl From for CompiledProgram { file_map: program.file_map, warnings: vec![], names: program.names, + brillig_names: program.brillig_names, } } } diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 0ccd56b791f..136775d5831 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e072c63f274..0fa12239d05 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -128,7 +128,7 @@ mod tests { } impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { + fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let response = self .mock_responses .get(artifact_path) @@ -173,6 +173,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index f91e50d8716..d7f3cbb9b85 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -188,6 +188,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file @@ -228,6 +229,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: vec!["main".to_string()], }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index 772c87ad1cb..edb3e1b0f08 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -100,7 +100,7 @@ fn format_binary_field_op(op: &BinaryFieldOp) -> String { } } -fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { +fn format_binary_int(op: &BinaryIntOp) -> String { match op { BinaryIntOp::Add => "add".to_string(), BinaryIntOp::Sub => "sub".to_string(), 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 From 31df5b57edbde455ad5890495bc6fe81d682fc51 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Mon, 19 Aug 2024 08:03:05 +0000 Subject: [PATCH 10/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 8abb411db7961affdb4b6966ea3ff5971ba7e75f Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 19 Aug 2024 17:53:39 -0300 Subject: [PATCH 11/21] Fix after merge --- .../tooling/lsp/src/requests/completion.rs | 324 ------------------ 1 file changed, 324 deletions(-) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 94e80ad22b8..ad0cf0c5b9b 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -426,19 +426,6 @@ impl<'a> NodeFinder<'a> { } fn find_in_expression(&mut self, expression: &Expression) { - // "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. - if 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); - return; - } - } - match &expression.kind { ExpressionKind::Literal(literal) => self.find_in_literal(literal), ExpressionKind::Block(block_expression) => { @@ -1184,315 +1171,4 @@ fn module_def_id_from_reference_id(reference_id: ReferenceId) -> Option None, } - - #[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; - } } From b62b53a62adbf94d11fe64dc2fdf48a14489c6c6 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 19 Aug 2024 17:59:49 -0300 Subject: [PATCH 12/21] nargo fmt --- .../execution_success/verify_honk_proof/src/main.nr | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) 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 index 667ed25f70b..d785dabffb0 100644 --- 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 @@ -13,10 +13,5 @@ fn main( // I believe we want to eventually make it public too though. key_hash: Field ) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); + std::verify_proof(verification_key, proof, public_inputs, key_hash); } From 55baef2976a01624ef6eb2e72485ae930d1e8898 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 19 Aug 2024 18:08:35 -0300 Subject: [PATCH 13/21] zeroed moved to another module --- .../aztec/src/state_vars/shared_mutable/shared_mutable.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr index 21385e134a9..71cd7ba2a2b 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr @@ -9,7 +9,7 @@ use crate::state_vars::{ shared_mutable::{scheduled_value_change::ScheduledValueChange, scheduled_delay_change::ScheduledDelayChange} }; use crate::oracle::storage::storage_read; -use dep::std::unsafe::zeroed; +use dep::std::mem::zeroed; mod test; From bdc0c65e309c8ab0126e63a6e610f8880181a3b4 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 19 Aug 2024 18:09:24 -0300 Subject: [PATCH 14/21] Fix more usages of zeroed --- .../aztec-nr/aztec/src/state_vars/shared_mutable/test.nr | 2 +- noir-projects/aztec-nr/aztec/src/utils/mod.nr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr index 8d5923a7f20..cf2b44f23b5 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/test.nr @@ -8,7 +8,7 @@ use crate::{ }; use dep::protocol_types::address::AztecAddress; -use dep::std::{test::OracleMock, unsafe::zeroed}; +use dep::std::{test::OracleMock, mem::zeroed}; global new_value = 17; diff --git a/noir-projects/aztec-nr/aztec/src/utils/mod.nr b/noir-projects/aztec-nr/aztec/src/utils/mod.nr index aeda6a44399..6d79c195f33 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/mod.nr @@ -59,7 +59,7 @@ fn verify_collapse_hints( } else { // BoundedVec assumes that the unused parts of the storage are zeroed out (e.g. in the Eq impl), so we make // sure that this property holds. - assert_eq(collapsed.get_unchecked(i), std::unsafe::zeroed(), "Dirty collapsed vec storage"); + assert_eq(collapsed.get_unchecked(i), std::mem::zeroed(), "Dirty collapsed vec storage"); } } // We now know that: From babc7b92c9b70fbd33ba546f08e3a29e9a66ec45 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 20 Aug 2024 14:58:51 +0000 Subject: [PATCH 15/21] [1 changes] feat: user `super::` in LSP autocompletion if possible (https://github.com/noir-lang/noir/pull/5751) fix: unconstrained fn mismatch is now a warning (https://github.com/noir-lang/noir/pull/5764) feat(perf): mem2reg function state for value loads to optimize across blocks (https://github.com/noir-lang/noir/pull/5757) feat: add `Expr::as_array`, `Expr::as_repeated_element_array` and same for slice (https://github.com/noir-lang/noir/pull/5750) chore: Add Brillig loop bytecode size regression and update noir-gates-diff report (https://github.com/noir-lang/noir/pull/5747) feat: fault-tolerant parsing of `fn` and `impl` (https://github.com/noir-lang/noir/pull/5753) fix: add missing trait impls for integer types to stdlib (https://github.com/noir-lang/noir/pull/5738) chore: refactor ACIR function IDs from raw integers to struct (https://github.com/noir-lang/noir/pull/5748) feat: (LSP) suggest names that match any part of the current prefix (https://github.com/noir-lang/noir/pull/5752) feat: LSP auto-import completion (https://github.com/noir-lang/noir/pull/5741) fix: Allow comptime code to use break without also being `unconstrained` (https://github.com/noir-lang/noir/pull/5744) feat: add `Expr::as_any_integer` and `Expr::as_member_access` (https://github.com/noir-lang/noir/pull/5742) chore: clarify Field use (https://github.com/noir-lang/noir/pull/5740) feat: add `Expr::as_binary_op` (https://github.com/noir-lang/noir/pull/5734) chore(docs): expanding solidity verifier chain list (https://github.com/noir-lang/noir/pull/5587) chore: apply some new lints across workspace (https://github.com/noir-lang/noir/pull/5736) feat: suggest trait methods in LSP completion (https://github.com/noir-lang/noir/pull/5735) feat: LSP autocomplete constructor fields (https://github.com/noir-lang/noir/pull/5732) feat: add `Expr::as_unary` (https://github.com/noir-lang/noir/pull/5731) chore: count brillig opcodes in nargo info (https://github.com/noir-lang/noir/pull/5189) feat: suggest tuple fields in LSP completion (https://github.com/noir-lang/noir/pull/5730) feat: add `Expr::as_bool` (https://github.com/noir-lang/noir/pull/5729) feat: add `Expr` methods: `as_tuple`, `as_parenthesized`, `as_index`, `as_if` (https://github.com/noir-lang/noir/pull/5726) feat: LSP signature help (https://github.com/noir-lang/noir/pull/5725) chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- .../.github/workflows/gates_report.yml | 2 +- .../workflows/gates_report_brillig.yml | 92 + noir/noir-repo/.gitignore | 1 + noir/noir-repo/Cargo.lock | 1 + noir/noir-repo/Cargo.toml | 6 + noir/noir-repo/acvm-repo/acir/Cargo.toml | 3 + .../acvm-repo/acir/src/circuit/mod.rs | 8 +- .../acvm-repo/acir/src/circuit/opcodes.rs | 6 +- .../acir/src/circuit/opcodes/function_id.rs | 17 + .../acir/tests/test_program_serialization.rs | 8 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 3 + .../acvm-repo/acir_field/src/field_element.rs | 4 +- .../acvm-repo/acir_field/src/generic_ark.rs | 4 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs | 6 +- noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../acvm-repo/acvm_js/src/execute.rs | 4 +- .../acvm_js/src/js_execution_error.rs | 8 +- .../acvm-repo/blackbox_solver/Cargo.toml | 3 + .../bn254_blackbox_solver/Cargo.toml | 3 + .../src/generator/generators.rs | 2 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 3 + .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 3 + noir/noir-repo/aztec_macros/Cargo.toml | 3 + .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- noir/noir-repo/compiler/fm/Cargo.toml | 3 + .../noir-repo/compiler/noirc_arena/Cargo.toml | 3 + .../compiler/noirc_driver/Cargo.toml | 3 + .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 11 +- .../compiler/noirc_driver/src/program.rs | 2 + .../compiler/noirc_errors/Cargo.toml | 3 + .../compiler/noirc_errors/src/position.rs | 2 +- .../compiler/noirc_evaluator/Cargo.toml | 3 + .../src/brillig/brillig_gen.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 2 + .../src/brillig/brillig_ir/artifact.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 9 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 6 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 87 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 9 +- .../noirc_evaluator/src/ssa/ir/map.rs | 2 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 +++- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 47 +- .../src/ssa/ssa_gen/context.rs | 8 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../noirc_frontend/src/ast/expression.rs | 18 +- .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../noirc_frontend/src/elaborator/comptime.rs | 8 + .../src/elaborator/expressions.rs | 23 +- .../noirc_frontend/src/elaborator/mod.rs | 31 +- .../src/elaborator/statements.rs | 4 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 80 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 3 + .../src/hir/comptime/interpreter/builtin.rs | 349 +++- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/def_collector/dc_mod.rs | 48 +- .../src/hir/def_map/module_def.rs | 2 +- .../src/hir/type_check/errors.rs | 10 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 138 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../compiler/noirc_frontend/src/locations.rs | 76 +- .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 107 +- .../noirc_frontend/src/parser/errors.rs | 12 + .../noirc_frontend/src/parser/parser.rs | 136 +- .../src/parser/parser/function.rs | 35 +- .../src/parser/parser/traits.rs | 108 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 195 ++ .../compiler/noirc_printable_type/Cargo.toml | 3 + .../compiler/noirc_printable_type/src/lib.rs | 1 + noir/noir-repo/compiler/wasm/Cargo.toml | 4 +- noir/noir-repo/cspell.json | 3 +- .../docs/explainers/explainer-writing-noir.md | 8 +- .../docs/how_to/how-to-solidity-verifier.md | 8 +- .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../how_to/how-to-solidity-verifier.md | 8 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- noir/noir-repo/noir_stdlib/src/cmp.nr | 26 + .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/default.nr | 3 + noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 22 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/expr.nr | 222 ++ noir/noir-repo/noir_stdlib/src/meta/mod.nr | 8 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 92 + .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/ops/arith.nr | 5 + noir/noir-repo/noir_stdlib/src/ops/bit.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_exp/src/main.nr | 8 - .../comptime_fmt_strings/src/main.nr | 2 +- .../Nargo.toml | 2 +- .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 6 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_loop_size_regression/Nargo.toml | 7 + .../brillig_loop_size_regression/Prover.toml | 0 .../brillig_loop_size_regression/src/main.nr | 16 + .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ----- .../verify_honk_proof/src/main.nr | 16 - .../test_programs/gates_report_brillig.sh | 33 + .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- noir/noir-repo/tooling/acvm_cli/Cargo.toml | 3 + noir/noir-repo/tooling/debugger/Cargo.toml | 3 + .../noir-repo/tooling/debugger/src/context.rs | 15 +- noir/noir-repo/tooling/fuzzer/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/Cargo.toml | 4 + noir/noir-repo/tooling/lsp/src/lib.rs | 11 +- .../tooling/lsp/src/notifications/mod.rs | 8 +- .../tooling/lsp/src/requests/completion.rs | 1792 ++++------------- .../src/requests/completion/auto_import.rs | 210 ++ .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 391 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 39 + .../lsp/src/requests/completion/tests.rs | 1641 +++++++++++++++ .../lsp/src/requests/completion/traversal.rs | 132 ++ .../tooling/lsp/src/requests/hover.rs | 56 +- .../tooling/lsp/src/requests/inlay_hint.rs | 16 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 15 +- .../lsp/src/requests/signature_help.rs | 291 +++ .../lsp/src/requests/signature_help/tests.rs | 196 ++ .../src/requests/signature_help/traversal.rs | 304 +++ noir/noir-repo/tooling/lsp/src/types.rs | 6 +- noir/noir-repo/tooling/nargo/Cargo.toml | 3 + .../tooling/nargo/src/ops/execute.rs | 6 +- noir/noir-repo/tooling/nargo_cli/Cargo.toml | 3 + noir/noir-repo/tooling/nargo_cli/build.rs | 2 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 57 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 3 + .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + noir/noir-repo/tooling/nargo_toml/Cargo.toml | 3 + .../noir_js_backend_barretenberg/package.json | 2 +- noir/noir-repo/tooling/noirc_abi/Cargo.toml | 3 + .../tooling/noirc_abi/src/arbitrary.rs | 8 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/src/lib.rs | 5 +- .../tooling/noirc_artifacts/Cargo.toml | 3 + .../tooling/noirc_artifacts/src/program.rs | 4 + noir/noir-repo/tooling/profiler/Cargo.toml | 3 + .../profiler/src/cli/gates_flamegraph_cmd.rs | 3 +- .../src/cli/opcodes_flamegraph_cmd.rs | 2 + .../tooling/profiler/src/opcode_formatter.rs | 2 +- noir/noir-repo/yarn.lock | 13 +- 249 files changed, 7171 insertions(+), 2804 deletions(-) create mode 100644 noir/noir-repo/.github/workflows/gates_report_brillig.yml create mode 100644 noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/op.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr rename noir/noir-repo/test_programs/compile_success_empty/{comptime_exp => comptime_trait_impl}/Nargo.toml (74%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/test_programs/gates_report_brillig.sh create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..b7fb2d8b4d0 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +5192e537708fc9ec51f53bb6a6629c9d682532d5 diff --git a/noir/noir-repo/.github/workflows/gates_report.yml b/noir/noir-repo/.github/workflows/gates_report.yml index 0cc94a1a04d..0b0a527b69e 100644 --- a/noir/noir-repo/.github/workflows/gates_report.yml +++ b/noir/noir-repo/.github/workflows/gates_report.yml @@ -80,7 +80,7 @@ jobs: - name: Compare gates reports id: gates_diff - uses: vezenovm/noir-gates-diff@acf12797860f237117e15c0d6e08d64253af52b6 + uses: noir-lang/noir-gates-diff@1931aaaa848a1a009363d6115293f7b7fc72bb87 with: report: gates_report.json summaryQuantile: 0.9 # only display the 10% most significant circuit size diffs in the summary (defaults to 20%) diff --git a/noir/noir-repo/.github/workflows/gates_report_brillig.yml b/noir/noir-repo/.github/workflows/gates_report_brillig.yml new file mode 100644 index 00000000000..4e12a6fcbca --- /dev/null +++ b/noir/noir-repo/.github/workflows/gates_report_brillig.yml @@ -0,0 +1,92 @@ +name: Report Brillig bytecode size diff + +on: + push: + branches: + - master + pull_request: + +jobs: + build-nargo: + runs-on: ubuntu-latest + strategy: + matrix: + target: [x86_64-unknown-linux-gnu] + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.74.1 + + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build Nargo + run: cargo build --package nargo_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/nargo ./dist/nargo + 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: nargo + path: ./dist/* + retention-days: 3 + + compare_brillig_bytecode_size_reports: + needs: [build-nargo] + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Download nargo binary + uses: actions/download-artifact@v4 + with: + name: nargo + path: ./nargo + + - name: Set nargo on PATH + run: | + nargo_binary="${{ github.workspace }}/nargo/nargo" + chmod +x $nargo_binary + echo "$(dirname $nargo_binary)" >> $GITHUB_PATH + export PATH="$PATH:$(dirname $nargo_binary)" + nargo -V + + - name: Generate Brillig bytecode size report + working-directory: ./test_programs + run: | + chmod +x gates_report_brillig.sh + ./gates_report_brillig.sh + mv gates_report_brillig.json ../gates_report_brillig.json + + - name: Compare Brillig bytecode size reports + id: brillig_bytecode_diff + uses: noir-lang/noir-gates-diff@3fb844067b25d1b59727ea600b614503b33503f4 + with: + report: gates_report_brillig.json + header: | + # Changes to Brillig bytecode sizes + brillig_report: true + summaryQuantile: 0.9 # only display the 10% most significant bytecode size diffs in the summary (defaults to 20%) + + - name: Add bytecode size diff to sticky comment + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: brillig + # delete the comment in case changes no longer impact brillig bytecode sizes + delete: ${{ !steps.brillig_bytecode_diff.outputs.markdown }} + message: ${{ steps.brillig_bytecode_diff.outputs.markdown }} \ No newline at end of file diff --git a/noir/noir-repo/.gitignore b/noir/noir-repo/.gitignore index 2c877a4d02c..aeb7d8757c4 100644 --- a/noir/noir-repo/.gitignore +++ b/noir/noir-repo/.gitignore @@ -33,6 +33,7 @@ tooling/noir_js/lib !compiler/wasm/noir-script/target gates_report.json +gates_report_brillig.json # Github Actions scratch space # This gives a location to download artifacts into the repository in CI without making git dirty. diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index bacbf939786..e1159c71201 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -2722,6 +2722,7 @@ dependencies = [ "acvm", "async-lsp", "codespan-lsp", + "convert_case 0.6.0", "fm", "fxhash", "lsp-types 0.94.1", diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 63509170057..7d9b3254c53 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -50,6 +50,12 @@ rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" +[workspace.lints.rust] +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" +unused_qualifications = "warn" + [workspace.dependencies] # ACVM workspace dependencies diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 88616ccffb5..860b565544b 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 00d0933a3aa..43984e4a922 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -103,7 +103,7 @@ impl ErrorSelector { impl Serialize for ErrorSelector { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.to_string().serialize(serializer) } @@ -112,7 +112,7 @@ impl Serialize for ErrorSelector { impl<'de> Deserialize<'de> for ErrorSelector { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; let as_u64 = s.parse().map_err(serde::de::Error::custom)?; @@ -224,7 +224,7 @@ impl Circuit { } impl Program { - fn write(&self, writer: W) -> std::io::Result<()> { + fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); encoder.write_all(&buf)?; @@ -250,7 +250,7 @@ impl Program { } impl Deserialize<'a>> Program { - fn read(reader: R) -> std::io::Result { + fn read(reader: R) -> std::io::Result { let mut gz_decoder = flate2::read::GzDecoder::new(reader); let mut buf_d = Vec::new(); gz_decoder.read_to_end(&mut buf_d)?; diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs index b1fdc5e0080..d7f0f5f6f1f 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs @@ -2,6 +2,10 @@ use super::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, directives::Directive, }; + +pub mod function_id; +pub use function_id::AcirFunctionId; + use crate::native_types::{Expression, Witness}; use acir_field::AcirField; use serde::{Deserialize, Serialize}; @@ -125,7 +129,7 @@ pub enum Opcode { Call { /// Id for the function being called. It is the responsibility of the executor /// to fetch the appropriate circuit from this id. - id: u32, + id: AcirFunctionId, /// Inputs to the function call inputs: Vec, /// Outputs of the function call diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs new file mode 100644 index 00000000000..b5abb1b3942 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)] +#[serde(transparent)] +pub struct AcirFunctionId(pub u32); + +impl AcirFunctionId { + pub fn as_usize(&self) -> usize { + self.0 as usize + } +} + +impl std::fmt::Display for AcirFunctionId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 1a634eeea9c..ce28d47021c 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -14,7 +14,7 @@ use std::collections::BTreeSet; use acir::{ circuit::{ brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, + opcodes::{AcirFunctionId, BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, @@ -381,13 +381,13 @@ fn nested_acir_call_circuit() { // x // } let nested_call = Opcode::Call { - id: 1, + id: AcirFunctionId(1), inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(2)], predicate: None, }; let nested_call_two = Opcode::Call { - id: 1, + id: AcirFunctionId(1), inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(3)], predicate: None, @@ -419,7 +419,7 @@ fn nested_acir_call_circuit() { q_c: FieldElement::one() + FieldElement::one(), }); let call = Opcode::Call { - id: 2, + id: AcirFunctionId(2), inputs: vec![Witness(2), Witness(1)], outputs: vec![Witness(3)], predicate: None, diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index a037a453348..c1cffc1334e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 92ccbc4e5f6..2323f008dbe 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -115,7 +115,7 @@ impl From for FieldElement { } } -impl Serialize for FieldElement { +impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -124,7 +124,7 @@ impl Serialize for FieldElement { } } -impl<'de, T: ark_ff::PrimeField> Deserialize<'de> for FieldElement { +impl<'de, T: PrimeField> Deserialize<'de> for FieldElement { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs index f7228e66314..74927c07a36 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs @@ -2,7 +2,7 @@ use num_bigint::BigUint; /// This trait is extremely unstable and WILL have breaking changes. pub trait AcirField: - std::marker::Sized + Sized + std::fmt::Display + std::fmt::Debug + Default @@ -24,7 +24,7 @@ pub trait AcirField: // + From + From + std::hash::Hash - + std::cmp::Eq + + Eq { fn one() -> Self; fn zero() -> Self; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 2ee4d529e5a..bf1170ce073 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index 83c5aeb6296..647c11bd3c3 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -6,7 +6,7 @@ use acir::{ brillig::ForeignCallResult, circuit::{ brillig::{BrilligBytecode, BrilligFunctionId}, - opcodes::{BlockId, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput}, AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, STRING_ERROR_SELECTOR, }, @@ -575,7 +575,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { else { unreachable!("Not executing a Call opcode"); }; - if *id == 0 { + if *id == AcirFunctionId(0) { return Err(OpcodeResolutionError::AcirMainCallAttempted { opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir( self.instruction_pointer(), @@ -716,7 +716,7 @@ pub(crate) fn is_predicate_false( #[derive(Debug, Clone, PartialEq)] pub struct AcirCallWaitInfo { /// Index in the list of ACIR function's that should be called - pub id: u32, + pub id: AcirFunctionId, /// Initial witness for the given circuit to be called pub initial_witness: WitnessMap, } diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 0d90b9ba54f..6b457a8f5f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] 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/acvm-repo/acvm_js/src/execute.rs b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs index c596dcf9614..98a0c4c3abe 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs @@ -250,7 +250,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { acvm.resolve_pending_foreign_call(result); } ACVMStatus::RequiresAcirCall(call_info) => { - let acir_to_call = &self.functions[call_info.id as usize]; + let acir_to_call = &self.functions[call_info.id.as_usize()]; let initial_witness = call_info.initial_witness; let call_solved_witness = self .execute_circuit(acir_to_call, initial_witness, witness_stack) @@ -267,7 +267,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { } } acvm.resolve_pending_acir_call(call_resolved_outputs); - witness_stack.push(call_info.id, call_solved_witness.clone()); + witness_stack.push(call_info.id.0, call_solved_witness.clone()); } } } diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index 23da88247fc..f6a00af7942 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -54,16 +54,14 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; let assertion_payload = match assertion_payload { - Some(raw) => ::from_serde(&raw) + Some(raw) => ::from_serde(&raw) .expect("Cannot serialize assertion payload"), None => JsValue::UNDEFINED, }; let brillig_function_id = match brillig_function_id { - Some(function_id) => { - ::from_serde(&function_id) - .expect("Cannot serialize Brillig function id") - } + Some(function_id) => ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id"), None => JsValue::UNDEFINED, }; diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 10b491c7f67..d0473857e1c 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ab677396c22..9a23f503a06 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index bb51426b33b..a4125014d56 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -91,7 +91,7 @@ mod test { fn test_derive_generators() { let res = derive_generators("test domain".as_bytes(), 128, 0); - let is_unique = |y: Affine, j: usize| -> bool { + let is_unique = |y: Affine, j: usize| -> bool { for (i, res) in res.iter().enumerate() { if i != j && *res == y { return false; diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index 631acbd55d8..9f5a5ce0ee2 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ebd35f1579f..cd9f754ebc0 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed..c9d88e36e28 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index b48f445be36..c367333a6f4 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml index b94f997b7b9..68fc4d1c920 100644 --- a/noir/noir-repo/compiler/noirc_arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -5,3 +5,6 @@ authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a9949f5093a..b244018cc71 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] 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_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 72c95823553..467bda2ca88 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -274,11 +274,8 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = if options.disable_macros { - &[] - } else { - &[&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs( @@ -557,6 +554,7 @@ pub fn compile_no_check( let force_compile = force_compile || options.print_acir || options.show_brillig + || options.force_brillig || options.show_ssa || options.emit_ssa; @@ -578,7 +576,7 @@ pub fn compile_no_check( emit_ssa: if options.emit_ssa { Some(context.package_build_path.clone()) } else { None }, }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + let SsaProgramArtifact { program, debug, warnings, names, brillig_names, error_types, .. } = create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); @@ -593,5 +591,6 @@ pub fn compile_no_check( noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, names, + brillig_names, }) } diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 8e02de0b8b3..88460482928 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -29,4 +29,6 @@ pub struct CompiledProgram { pub warnings: Vec, /// Names of the functions in the program. These are used for more informative debugging and benchmarking. pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index 41b1cd0ff58..61b274c605f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 02b242e8b4d..1792197eab7 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -16,7 +16,7 @@ pub struct Spanned { /// This is important for tests. Two Spanned objects are equal if their content is equal /// They may not have the same span. Use into_span to test for Span being equal specifically -impl PartialEq> for Spanned { +impl PartialEq> for Spanned { fn eq(&self, other: &Spanned) -> bool { self.contents == other.contents } diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 66c770b5064..81feb0b7154 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..d6364cefc9a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -27,5 +27,7 @@ pub(crate) fn convert_ssa_function( BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); } - brillig_context.artifact() + let mut artifact = brillig_context.artifact(); + artifact.name = func.name().to_string(); + artifact } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index dff4da56c1e..fca1f60544d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -55,6 +55,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_invert".to_string(), } } @@ -109,5 +110,6 @@ pub(crate) fn directive_quotient() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_integer_quotient".to_string(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..53964f64170 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -22,6 +22,7 @@ pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, + pub(crate) name: String, } #[derive(Default, Debug, Clone)] @@ -49,6 +50,8 @@ pub(crate) struct BrilligArtifact { locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. call_stack: CallStack, + /// Name of the function, only used for debugging purposes. + pub(crate) name: String, } /// A pointer to a location in the opcode. @@ -81,6 +84,7 @@ impl BrilligArtifact { byte_code: self.byte_code, locations: self.locations, assert_messages: self.assert_messages, + name: self.name, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index b632958f37f..9daf98e606b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -151,6 +151,7 @@ pub struct SsaProgramArtifact { pub main_input_witnesses: Vec, pub main_return_witnesses: Vec, pub names: Vec, + pub brillig_names: Vec, pub error_types: BTreeMap, } @@ -167,6 +168,7 @@ impl SsaProgramArtifact { main_input_witnesses: Vec::default(), main_return_witnesses: Vec::default(), names: Vec::default(), + brillig_names: Vec::default(), error_types, } } @@ -202,8 +204,10 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = - optimize_into_acir(program, options)?; + let ArtifactsAndWarnings( + (generated_acirs, generated_brillig, brillig_function_names, error_types), + ssa_level_warnings, + ) = optimize_into_acir(program, options)?; if options.force_brillig_output { assert_eq!( generated_acirs.len(), @@ -236,6 +240,7 @@ pub fn create_program( program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } + program_artifact.brillig_names = brillig_function_names; Ok(program_artifact) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index b2a73106468..a8324c11592 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -8,7 +8,7 @@ use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; use acvm::acir::circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}; -use acvm::acir::circuit::opcodes::{BlockId, BlockType, MemOp}; +use acvm::acir::circuit::opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}; use acvm::acir::circuit::{AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode}; use acvm::blackbox_solver; use acvm::brillig_vm::{MemoryValue, VMStatus, VM}; @@ -1959,7 +1959,7 @@ impl AcirContext { pub(crate) fn call_acir_function( &mut self, - id: u32, + id: AcirFunctionId, inputs: Vec, output_count: usize, predicate: AcirVar, @@ -2005,7 +2005,7 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 22b13aeae79..3e36459a0d6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -29,7 +29,7 @@ use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; pub(crate) use acir_ir::generated_acir::GeneratedAcir; -use acvm::acir::circuit::opcodes::BlockType; +use acvm::acir::circuit::opcodes::{AcirFunctionId, BlockType}; use noirc_frontend::monomorphization::ast::InlineType; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; @@ -235,7 +235,7 @@ impl Debug for AcirDynamicArray { #[derive(Debug, Clone)] pub(crate) enum AcirValue { Var(AcirVar, AcirType), - Array(im::Vector), + Array(Vector), DynamicArray(AcirDynamicArray), } @@ -282,6 +282,7 @@ impl AcirValue { pub(crate) type Artifacts = ( Vec>, Vec>, + Vec, BTreeMap, ); @@ -334,11 +335,13 @@ impl Ssa { } } - let brillig = vecmap(shared_context.generated_brillig, |brillig| BrilligBytecode { - bytecode: brillig.byte_code, - }); + let (brillig_bytecode, brillig_names) = shared_context + .generated_brillig + .into_iter() + .map(|brillig| (BrilligBytecode { bytecode: brillig.byte_code }, brillig.name)) + .unzip(); - Ok((acirs, brillig, self.error_selector_to_type)) + Ok((acirs, brillig_bytecode, brillig_names, self.error_selector_to_type)) } } @@ -772,7 +775,7 @@ impl<'a> Context<'a> { .get(id) .expect("ICE: should have an associated final index"); let output_vars = self.acir_context.call_acir_function( - *acir_function_id, + AcirFunctionId(*acir_function_id), inputs, output_count, self.current_side_effects_enabled_var, @@ -955,6 +958,8 @@ impl<'a> Context<'a> { BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); + entry_point.name = func.name().to_string(); + // Link the entry point with all dependencies while let Some(unresolved_fn_label) = entry_point.first_unresolved_function_call() { let artifact = &brillig.find_by_function_label(unresolved_fn_label.clone()); @@ -1645,7 +1650,7 @@ impl<'a> Context<'a> { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - let array: im::Vector = init_values.into(); + let array: Vector = init_values.into(); self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } @@ -2862,9 +2867,13 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { #[cfg(test)] mod test { + use acvm::{ acir::{ - circuit::{brillig::BrilligFunctionId, ExpressionWidth, Opcode, OpcodeLocation}, + circuit::{ + brillig::BrilligFunctionId, opcodes::AcirFunctionId, ExpressionWidth, Opcode, + OpcodeLocation, + }, native_types::Witness, }, FieldElement, @@ -2966,7 +2975,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: @@ -3015,8 +3024,18 @@ mod test { let main_opcodes = main_acir.opcodes(); assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo`"); - check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); - check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + check_call_opcode( + &main_opcodes[0], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(2)], + ); + check_call_opcode( + &main_opcodes[1], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(3)], + ); if let Opcode::AssertZero(expr) = &main_opcodes[2] { assert_eq!(expr.linear_combinations[0].0, FieldElement::from(1u128)); @@ -3061,7 +3080,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` @@ -3071,9 +3090,19 @@ mod test { let main_opcodes = main_acir.opcodes(); assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); - check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + check_call_opcode( + &main_opcodes[0], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(2)], + ); // The output of the first call should be the input of the second call - check_call_opcode(&main_opcodes[1], 1, vec![Witness(2), Witness(1)], vec![Witness(3)]); + check_call_opcode( + &main_opcodes[1], + AcirFunctionId(1), + vec![Witness(2), Witness(1)], + vec![Witness(3)], + ); } fn basic_nested_call(inline_type: InlineType) { @@ -3151,7 +3180,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3162,9 +3191,19 @@ mod test { assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); // Both of these should call func_with_nested_foo_call f1 - check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + check_call_opcode( + &main_opcodes[0], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(2)], + ); // The output of the first call should be the input of the second call - check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + check_call_opcode( + &main_opcodes[1], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(3)], + ); let func_with_nested_call_acir = &acir_functions[1]; let func_with_nested_call_opcodes = func_with_nested_call_acir.opcodes(); @@ -3177,7 +3216,7 @@ mod test { // Should call foo f2 check_call_opcode( &func_with_nested_call_opcodes[1], - 2, + AcirFunctionId(2), vec![Witness(3), Witness(1)], vec![Witness(4)], ); @@ -3185,7 +3224,7 @@ mod test { fn check_call_opcode( opcode: &Opcode, - expected_id: u32, + expected_id: AcirFunctionId, expected_inputs: Vec, expected_outputs: Vec, ) { @@ -3265,7 +3304,7 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3329,7 +3368,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3403,7 +3442,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3491,7 +3530,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 34d7d595eb9..f06f46d7af8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -171,13 +171,14 @@ impl DataFlowGraph { ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - use InsertInstructionResult::*; match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { - SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification), + SimplifyResult::SimplifiedTo(simplification) => { + InsertInstructionResult::SimplifiedTo(simplification) + } SimplifyResult::SimplifiedToMultiple(simplification) => { - SimplifiedToMultiple(simplification) + InsertInstructionResult::SimplifiedToMultiple(simplification) } - SimplifyResult::Remove => InstructionRemoved, + SimplifyResult::Remove => InsertInstructionResult::InstructionRemoved, result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1c9a31f0c99..f1265553b83 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -50,7 +50,7 @@ impl Id { // Need to manually implement most impls on Id. // Otherwise rust assumes that Id: Hash only if T: Hash, // which isn't true since the T is not used internally. -impl std::hash::Hash for Id { +impl Hash for Id { fn hash(&self, state: &mut H) { self.index.hash(state); } 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/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 5b1139e5b9c..e5a25dcfef1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -66,6 +66,8 @@ mod block; use std::collections::{BTreeMap, BTreeSet}; +use fxhash::FxHashMap as HashMap; + use crate::ssa::{ ir::{ basic_block::BasicBlockId, @@ -111,6 +113,10 @@ struct PerFunctionContext<'f> { /// We avoid removing individual instructions as we go since removing elements /// from the middle of Vecs many times will be slower than a single call to `retain`. instructions_to_remove: BTreeSet, + + /// Track a value's last load across all blocks. + /// If a value is not used in anymore loads we can remove the last store to that value. + last_loads: HashMap, } impl<'f> PerFunctionContext<'f> { @@ -124,6 +130,7 @@ impl<'f> PerFunctionContext<'f> { inserter: FunctionInserter::new(function), blocks: BTreeMap::new(), instructions_to_remove: BTreeSet::new(), + last_loads: HashMap::default(), } } @@ -140,6 +147,18 @@ impl<'f> PerFunctionContext<'f> { let references = self.find_starting_references(block); self.analyze_block(block, references); } + + // If we never load from an address within a function we can remove all stores to that address. + // This rule does not apply to reference parameters, which we must also check for before removing these stores. + for (block_id, block) in self.blocks.iter() { + let block_params = self.inserter.function.dfg.block_parameters(*block_id); + for (value, store_instruction) in block.last_stores.iter() { + let is_reference_param = block_params.contains(value); + if self.last_loads.get(value).is_none() && !is_reference_param { + self.instructions_to_remove.insert(*store_instruction); + } + } + } } /// The value of each reference at the start of the given block is the unification @@ -239,6 +258,8 @@ impl<'f> PerFunctionContext<'f> { self.instructions_to_remove.insert(instruction); } else { references.mark_value_used(address, self.inserter.function); + + self.last_loads.insert(address, instruction); } } Instruction::Store { address, value } => { @@ -594,10 +615,8 @@ mod tests { // acir fn main f0 { // b0(): // v7 = allocate - // store Field 5 at v7 // jmp b1(Field 5) // b1(v3: Field): - // store Field 6 at v7 // return v3, Field 5, Field 6 // } let ssa = ssa.mem2reg(); @@ -609,9 +628,9 @@ mod tests { assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); assert_eq!(count_loads(b1, &main.dfg), 0); - // Neither store is removed since they are each the last in the block and there are multiple blocks - assert_eq!(count_stores(main.entry_block(), &main.dfg), 1); - assert_eq!(count_stores(b1, &main.dfg), 1); + // All stores are removed as there are no loads to the values being stored anywhere in the function. + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); // The jmp to b1 should also be a constant 5 now match main.dfg[main.entry_block()].terminator() { @@ -641,8 +660,8 @@ mod tests { // b1(): // store Field 1 at v3 // store Field 2 at v4 - // v8 = load v3 - // v9 = eq v8, Field 2 + // v7 = load v3 + // v8 = eq v7, Field 2 // return // } let main_id = Id::test_new(0); @@ -681,12 +700,9 @@ mod tests { // acir fn main f0 { // b0(): // v9 = allocate - // store Field 0 at v9 // v10 = allocate - // store v9 at v10 // jmp b1() // b1(): - // store Field 2 at v9 // return // } let ssa = ssa.mem2reg(); @@ -698,14 +714,17 @@ mod tests { assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); assert_eq!(count_loads(b1, &main.dfg), 0); - // Only the first store in b1 is removed since there is another store to the same reference + // All stores should be removed. + // The first store in b1 is removed since there is another store to the same reference // in the same block, and the store is not needed before the later store. - assert_eq!(count_stores(main.entry_block(), &main.dfg), 2); - assert_eq!(count_stores(b1, &main.dfg), 1); + // The rest of the stores are also removed as no loads are done within any blocks + // to the stored values. + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); let b1_instructions = main.dfg[b1].instructions(); // We expect the last eq to be optimized out - assert_eq!(b1_instructions.len(), 1); + assert_eq!(b1_instructions.len(), 0); } } 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..13e5c2445ad 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 @@ -85,7 +85,7 @@ pub(super) struct Loop { } /// The queue of functions remaining to compile -type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; +type FunctionQueue = Vec<(FuncId, IrFunctionId)>; impl<'a> FunctionContext<'a> { /// Create a new FunctionContext to compile the first function in the shared_context's @@ -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 @@ -1005,14 +1005,14 @@ impl SharedContext { } /// Pops the next function from the shared function queue, returning None if the queue is empty. - pub(super) fn pop_next_function_in_queue(&self) -> Option<(ast::FuncId, IrFunctionId)> { + pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() } /// Return the matching id for the given function if known. If it is not known this /// will add the function to the queue of functions to compile, assign it a new id, /// and return this new id. - pub(super) fn get_or_queue_function(&self, id: ast::FuncId) -> IrFunctionId { + pub(super) fn get_or_queue_function(&self, id: FuncId) -> IrFunctionId { // Start a new block to guarantee the destructor for the map lock is released // before map needs to be acquired again in self.functions.write() below { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1373d1bc46e..6b19aff2674 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -396,11 +396,11 @@ impl<'a> FunctionContext<'a> { /// return a reference to each element, for use with the store instruction. fn codegen_array_index( &mut self, - array: super::ir::value::ValueId, - index: super::ir::value::ValueId, + array: ValueId, + index: ValueId, element_type: &ast::Type, location: Location, - length: Option, + length: Option, ) -> Result { // base_index = index * type_size let index = self.make_array_index(index); @@ -438,11 +438,7 @@ impl<'a> FunctionContext<'a> { /// Prepare a slice access. /// Check that the index being used to access a slice element /// is less than the dynamic slice length. - fn codegen_slice_access_check( - &mut self, - index: super::ir::value::ValueId, - length: Option, - ) { + fn codegen_slice_access_check(&mut self, index: ValueId, length: Option) { let index = self.make_array_index(index); // We convert the length as an array index type for comparison let array_len = self 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..1fd1e313c44 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, QuotedTypeId}; -use crate::token::{Attributes, Token, Tokens}; +use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -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 @@ -477,6 +478,20 @@ pub struct FunctionDefinition { pub return_visibility: Visibility, } +impl FunctionDefinition { + pub fn is_private(&self) -> bool { + self.visibility == ItemVisibility::Private + } + + pub fn is_test(&self) -> bool { + if let Some(attribute) = &self.attributes.function { + matches!(attribute, FunctionAttribute::Test(..)) + } else { + false + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Param { pub visibility: Visibility, @@ -587,6 +602,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/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index afa2e7fa7a8..2b78c02e53c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -267,6 +267,14 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_empty_fn(); let module = self.module_id(); self.interner.push_function(id, &function.def, module, location); + + if self.interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + self.interner.register_function(id, &function.def); + } + let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { file_id: self.file, 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..65e94c4fcf4 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; @@ -489,7 +506,7 @@ impl<'context> Elaborator<'context> { unseen_fields.remove(&field_name); seen_fields.insert(field_name.clone()); - self.unify_with_coercions(&field_type, expected_type, resolved, || { + self.unify_with_coercions(&field_type, expected_type, resolved, field_span, || { TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: field_type.to_string(), @@ -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..2e4a69c067f 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)); @@ -1290,6 +1297,12 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; + let name = if self.interner.is_in_lsp_mode() { + Some(let_stmt.pattern.name_ident().to_string()) + } else { + None + }; + if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { @@ -1312,8 +1325,9 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner - .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); + if let Some(name) = name { + self.interner.register_global(global_id, name, self.module_id()); + } self.local_module = old_module; self.file = old_file; @@ -1451,9 +1465,12 @@ impl<'context> Elaborator<'context> { /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { - self.current_item.map_or(true, |id| match id { - DependencyId::Function(id) => !self.interner.function_modifiers(&id).is_unconstrained, - _ => true, - }) + !self.in_comptime_context() + && self.current_item.map_or(true, |id| match id { + DependencyId::Function(id) => { + !self.interner.function_modifiers(&id).is_unconstrained + } + _ => true, + }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 48380383eb0..da4492eb211 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -78,7 +78,7 @@ impl<'context> Elaborator<'context> { let r#type = if annotated_type != Type::Error { // Now check if LHS is the same type as the RHS // Importantly, we do not coerce any types implicitly - self.unify_with_coercions(&expr_type, &annotated_type, expression, || { + self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_span, || { TypeCheckError::TypeMismatch { expected_typ: annotated_type.to_string(), expr_typ: expr_type.to_string(), @@ -136,7 +136,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::VariableMustBeMutable { name, span }); } - self.unify_with_coercions(&expr_type, &lvalue_type, expression, || { + self.unify_with_coercions(&expr_type, &lvalue_type, expression, span, || { TypeCheckError::TypeMismatchWithSource { actual: expr_type.clone(), expected: lvalue_type.clone(), 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..80c5aad7fd2 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 { @@ -641,10 +641,18 @@ impl<'context> Elaborator<'context> { actual: &Type, expected: &Type, expression: ExprId, + span: Span, make_error: impl FnOnce() -> TypeCheckError, ) { let mut errors = Vec::new(); - actual.unify_with_coercions(expected, expression, self.interner, &mut errors, make_error); + actual.unify_with_coercions( + expected, + expression, + span, + self.interner, + &mut errors, + make_error, + ); self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); } @@ -735,11 +743,13 @@ 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 { - expected_typ: param.to_string(), - expr_typ: arg.to_string(), - expr_span: *arg_span, + for (param, (arg, arg_expr_id, arg_span)) in fn_params.iter().zip(callsite_args) { + self.unify_with_coercions(arg, param, *arg_expr_id, *arg_span, || { + TypeCheckError::TypeMismatch { + expected_typ: param.to_string(), + expr_typ: arg.to_string(), + expr_span: *arg_span, + } }); } @@ -763,7 +773,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 +783,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 +1139,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 +1325,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 +1395,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}"), @@ -1437,7 +1459,7 @@ impl<'context> Elaborator<'context> { }); } } else { - self.unify_with_coercions(&body_type, declared_return_type, body_id, || { + self.unify_with_coercions(&body_type, declared_return_type, body_id, func_span, || { let mut error = TypeCheckError::TypeMismatchWithSource { expected: declared_return_type.clone(), actual: body_type.clone(), @@ -1577,7 +1599,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..33f8c9d8332 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. @@ -896,6 +897,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "=="), }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -908,6 +910,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "!="), }, BinaryOpKind::Less => match (lhs, rhs) { 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..f65c5ae8cdf 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}; @@ -17,13 +17,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, UnresolvedType, - UnresolvedTypeData, Visibility, + ArrayLiteral, ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, + UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }, 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, @@ -47,7 +47,23 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_array" => expr_as_array(arguments, return_type, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), + "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), + "expr_as_if" => expr_as_if(arguments, return_type, location), + "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_integer" => expr_as_integer(arguments, return_type, location), + "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), + "expr_as_repeated_element_array" => { + expr_as_repeated_element_array(arguments, return_type, location) + } + "expr_as_repeated_element_slice" => { + expr_as_repeated_element_slice(arguments, return_type, location) + } + "expr_as_slice" => expr_as_slice(arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -86,6 +102,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 +113,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 +530,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 +650,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 { @@ -688,6 +766,71 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn as_array(self) -> Option<[Expr]> +fn expr_as_array( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr { + let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(exprs, typ)) + } else { + None + } + }) +} + +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_bool(self) -> Option +fn expr_as_bool( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + Some(Value::Bool(bool)) + } else { + None + } + }) +} + // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( arguments: Vec<(Value, Location)>, @@ -708,6 +851,194 @@ fn expr_as_function_call( }) } +// fn as_if(self) -> Option<(Expr, Expr, Option)> +fn expr_as_if( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::If(if_expr) = expr { + // Get the type of `Option` + let option_type = extract_option_generic_type(return_type.clone()); + let Type::Tuple(option_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(option_types.len(), 3); + let alternative_option_type = option_types[2].clone(); + + let alternative = + option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + + Some(Value::Tuple(vec![ + Value::Expr(if_expr.condition.kind), + Value::Expr(if_expr.consequence.kind), + alternative.ok()?, + ])) + } else { + None + } + }) +} + +// fn as_index(self) -> Option +fn expr_as_index( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Index(index_expr) = expr { + Some(Value::Tuple(vec![ + Value::Expr(index_expr.collection.kind), + Value::Expr(index_expr.index.kind), + ])) + } else { + None + } + }) +} + +// fn as_integer(self) -> Option<(Field, bool)> +fn expr_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + }) +} + +// fn as_member_access(self) -> Option<(Expr, Quoted)> +fn expr_as_member_access( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::MemberAccess(member_access) = expr { + let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); + Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + } else { + None + } + }) +} + +// fn as_repeated_element_array(self) -> Option<(Expr, Expr)> +fn expr_as_repeated_element_array( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { + repeated_element, + length, + })) = expr + { + Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)])) + } else { + None + } + }) +} + +// fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> +fn expr_as_repeated_element_slice( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated { + repeated_element, + length, + })) = expr + { + Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)])) + } else { + None + } + }) +} + +// fn as_slice(self) -> Option<[Expr]> +fn expr_as_slice( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr { + let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(exprs, typ)) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Expr]> +fn expr_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Tuple(expressions) = expr { + let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(expressions, typ)) + } else { + None + } + }) +} + +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -719,8 +1050,12 @@ where F: FnOnce(ExpressionKind) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let expr = get_expr(self_argument)?; - let option_value = f(expr); + let mut expression_kind = get_expr(self_argument)?; + while let ExpressionKind::Parenthesized(expression) = expression_kind { + expression_kind = expression.kind; + } + + let option_value = f(expression_kind); option(return_type, option_value) } 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/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index be2afd13507..6436c2562bc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::ModuleAttributes; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -232,6 +232,13 @@ impl<'a> ModCollector<'a> { let location = Location::new(function.span(), self.file_id); context.def_interner.push_function(func_id, &function.def, module, location); + if context.def_interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + context.def_interner.register_function(func_id, &function.def); + } + // Now link this func_id to a crate level map with the noir function and the module id // Encountering a NoirFunction, we retrieve it's module_data to get the namespace // Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner @@ -307,8 +314,8 @@ impl<'a> ModCollector<'a> { }; // Add the struct to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_struct(name, id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_struct(name.clone(), id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -322,10 +329,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Struct(id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_struct(id, name.to_string(), parent_module_id); + } } definition_errors } @@ -383,7 +390,7 @@ impl<'a> ModCollector<'a> { // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_type_alias(name, type_alias_id); + .declare_type_alias(name.clone(), type_alias_id); if let Err((first_def, second_def)) = result { let err = DefCollectorErrorKind::Duplicate { @@ -396,10 +403,11 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Alias(type_alias_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + let name = name.to_string(); + context.def_interner.register_type_alias(type_alias_id, name, parent_module_id); + } } errors } @@ -432,8 +440,8 @@ impl<'a> ModCollector<'a> { }; // Add the trait to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, trait_id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_trait(name.clone(), trait_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -567,10 +575,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location( - ReferenceId::Trait(trait_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_trait(trait_id, name.to_string(), parent_module_id); + } self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -766,6 +774,10 @@ impl<'a> ModCollector<'a> { parent: self.module_id, }, ); + + if context.def_interner.is_in_lsp_mode() { + context.def_interner.register_module(mod_id, mod_name.0.contents.clone()); + } } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs index 54d092f9515..a487bda81b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs @@ -3,7 +3,7 @@ use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; use super::ModuleId; /// A generic ID that references either a module, function, type, interface or global -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), 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..d51760f9d20 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,10 @@ 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("Converting an unconstrained fn to a non-unconstrained fn is unsafe")] + UnsafeFn { 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 +360,12 @@ 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) + } + TypeCheckError::UnsafeFn { 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..d6d114c7075 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, } @@ -199,6 +205,12 @@ impl ResolvedGeneric { } } +enum FunctionCoercionResult { + NoCoercion, + Coerced(Type), + UnconstrainedMismatch(Type), +} + impl std::hash::Hash for StructType { fn hash(&self, state: &mut H) { self.id.hash(state); @@ -662,15 +674,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 +743,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 +870,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 +950,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 +1007,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) | Type::Quoted(_) @@ -1039,7 +1056,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 +1094,7 @@ impl Type { | Type::Slice(_) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::FmtString(_, _) | Type::InfixExpr(..) | Type::Error => true, @@ -1208,7 +1225,7 @@ impl Type { | Type::TypeVariable(_, _) | Type::TraitAsType(..) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) @@ -1576,8 +1593,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)?; } @@ -1762,18 +1782,57 @@ impl Type { &self, expected: &Type, expression: ExprId, + span: Span, interner: &mut NodeInterner, errors: &mut Vec, make_error: impl FnOnce() -> TypeCheckError, ) { 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()); + 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` + match self.try_fn_to_unconstrained_fn_coercion(expected) { + FunctionCoercionResult::NoCoercion => errors.push(make_error()), + FunctionCoercionResult::Coerced(coerced_self) => { + coerced_self + .unify_with_coercions(expected, expression, span, interner, errors, make_error); + } + FunctionCoercionResult::UnconstrainedMismatch(coerced_self) => { + errors.push(TypeCheckError::UnsafeFn { span }); + + coerced_self + .unify_with_coercions(expected, expression, span, interner, errors, 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) -> FunctionCoercionResult { + // 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()) + { + let coerced_type = Type::Function(params, ret, env, unconstrained_expected); + + match (unconstrained_self, unconstrained_expected) { + (true, true) | (false, false) => FunctionCoercionResult::NoCoercion, + (false, true) => FunctionCoercionResult::Coerced(coerced_type), + (true, false) => FunctionCoercionResult::UnconstrainedMismatch(coerced_type), } } else { - Type::apply_type_bindings(bindings); + FunctionCoercionResult::NoCoercion } } @@ -1789,13 +1848,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 +2133,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 +2195,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 +2249,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 +2345,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 +2374,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 +2397,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 +2490,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 +2579,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/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index c437676b605..0ac13a58ecf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,9 +1,10 @@ use fm::FileId; use noirc_errors::Location; use rangemap::RangeMap; -use rustc_hash::FxHashMap; +use rustc_hash::FxHashMap as HashMap; use crate::{ + ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, macros_api::{NodeInterner, StructId}, node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, @@ -12,7 +13,7 @@ use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] pub(crate) struct LocationIndices { - map_file_to_range: FxHashMap>, + map_file_to_range: HashMap>, } impl LocationIndices { @@ -275,4 +276,75 @@ impl NodeInterner { .neighbors_directed(reference_index, petgraph::Direction::Outgoing) .next() } + + pub(crate) fn register_module(&mut self, id: ModuleId, name: String) { + self.register_name_for_auto_import(name, ModuleDefId::ModuleId(id), ItemVisibility::Public); + } + + pub(crate) fn register_global( + &mut self, + id: GlobalId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Global(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::GlobalId(id), visibility); + } + + pub(crate) fn register_struct( + &mut self, + id: StructId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Struct(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeId(id), visibility); + } + + pub(crate) fn register_trait(&mut self, id: TraitId, name: String, parent_module_id: ModuleId) { + self.add_definition_location(ReferenceId::Trait(id), Some(parent_module_id)); + + self.register_name_for_auto_import(name, ModuleDefId::TraitId(id), ItemVisibility::Public); + } + + pub(crate) fn register_type_alias( + &mut self, + id: TypeAliasId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Alias(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeAliasId(id), visibility); + } + + pub(crate) fn register_function(&mut self, id: FuncId, func_def: &FunctionDefinition) { + self.register_name_for_auto_import( + func_def.name.0.contents.clone(), + ModuleDefId::FunctionId(id), + func_def.visibility, + ); + } + + fn register_name_for_auto_import( + &mut self, + name: String, + module_def_id: ModuleDefId, + visibility: ItemVisibility, + ) { + if !self.lsp_mode { + return; + } + + self.auto_import_names.entry(name).or_default().push((module_def_id, visibility)); + } + + pub fn get_auto_import_names(&self) -> &HashMap> { + &self.auto_import_names + } } 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..46ec2676930 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -19,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::ModuleDefId; use crate::macros_api::UnaryOp; use crate::QuotedType; @@ -179,10 +180,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, @@ -231,6 +232,11 @@ pub struct NodeInterner { // (ReferenceId::Reference and ReferenceId::Local aren't included here) pub(crate) reference_modules: HashMap, + // All names (and their definitions) that can be offered for auto_import. + // These include top-level functions, global variables and types, but excludes + // impl and trait-impl methods. + pub(crate) auto_import_names: HashMap>, + /// Each value currently in scope in the comptime interpreter. /// Each element of the Vec represents a scope with every scope together making /// up all currently visible definitions. The first scope is always the global scope. @@ -602,6 +608,7 @@ impl Default for NodeInterner { reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::default(), reference_modules: HashMap::default(), + auto_import_names: HashMap::default(), comptime_scopes: vec![HashMap::default()], } } @@ -910,6 +917,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored self.add_definition_location(ReferenceId::Function(id), Some(module)); + definition_id } @@ -951,12 +959,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 +1139,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 +1212,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 +1318,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 +1336,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 +1672,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 +1697,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 +1706,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 +1803,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 +1827,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 +2006,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 +2015,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 +2046,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 +2056,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 +2107,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..ebb58ddc224 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,20 @@ 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 {{ or -> after function parameters")] + ExpectedLeftBraceOrArrowAfterFunctionParameters, + #[error("expected {{ after if condition")] + ExpectedLeftBraceAfterIfCondition, + #[error("expected <, where or {{ after trait name")] + ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitName, + #[error("expected <, where or {{ after impl type")] + ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType, + #[error("expected <, where or {{ after trait impl for type")] + ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, #[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..eddf85becfe 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -219,13 +219,27 @@ fn top_level_statement<'a>( /// /// implementation: 'impl' generics type '{' function_definition ... '}' fn implementation() -> impl NoirParser { + let methods_or_error = just(Token::LeftBrace) + .ignore_then(spanned(function::function_definition(true)).repeated()) + .then_ignore(just(Token::RightBrace)) + .or_not() + .validate(|methods, span, emit| { + if let Some(methods) = methods { + methods + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType, + span, + )); + vec![] + } + }); + keyword(Keyword::Impl) .ignore_then(function::generics()) .then(parse_type().map_with_span(|typ, span| (typ, span))) .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(spanned(function::function_definition(true)).repeated()) - .then_ignore(just(Token::RightBrace)) + .then(methods_or_error) .map(|args| { let ((other_args, where_clause), methods) = args; let (generics, (object_type, type_span)) = other_args; @@ -377,6 +391,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 +534,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 +871,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 +917,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 +940,7 @@ where UnaryRhs::MemberAccess(field) => { Expression::member_access_or_method_call(lhs, field, span) } + UnaryRhs::JustADot => lhs, }, ) } @@ -931,10 +968,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 +1139,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 +1467,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 +1751,44 @@ 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"); + } + + #[test] + fn parse_recover_impl_without_body() { + let src = "impl Foo"; + + let (top_level_statement, errors) = parse_recover(implementation(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected <, where or { after impl type"); + + let top_level_statement = top_level_statement.unwrap(); + let TopLevelStatement::Impl(impl_) = top_level_statement else { + panic!("Expected to parse an impl"); + }; + + assert_eq!(impl_.object_type.to_string(), "Foo"); + assert!(impl_.methods.is_empty()); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs index 3de48d2e02a..56760898374 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -6,7 +6,10 @@ use super::{ self_parameter, where_clause, NoirParser, }; use crate::token::{Keyword, Token, TokenKind}; -use crate::{ast::IntegerBitSize, parser::spanned}; +use crate::{ + ast::{BlockExpression, IntegerBitSize}, + parser::spanned, +}; use crate::{ ast::{ FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, @@ -20,10 +23,24 @@ use crate::{ }; use chumsky::prelude::*; +use noirc_errors::Span; /// function_definition: attribute function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block /// function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block pub(super) fn function_definition(allow_self: bool) -> impl NoirParser { + let body_or_error = + spanned(block(fresh_statement()).or_not()).validate(|(body, body_span), span, emit| { + if let Some(body) = body { + (body, body_span) + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBraceOrArrowAfterFunctionParameters, + span, + )); + (BlockExpression { statements: vec![] }, Span::from(span.end()..span.end())) + } + }); + attributes() .then(function_modifiers()) .then_ignore(keyword(Keyword::Fn)) @@ -32,7 +49,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser after function parameters"); + + let noir_function = noir_function.unwrap(); + assert_eq!(noir_function.name(), "foo"); + assert_eq!(noir_function.parameters().len(), 1); + assert!(noir_function.def.body.statements.is_empty()); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs index 0874cadd34e..0cf5e63f5f3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -23,14 +23,28 @@ use crate::{ use super::{generic_type_args, parse_type, primitives::ident}; pub(super) fn trait_definition() -> impl NoirParser { + let trait_body_or_error = just(Token::LeftBrace) + .ignore_then(trait_body()) + .then_ignore(just(Token::RightBrace)) + .or_not() + .validate(|items, span, emit| { + if let Some(items) = items { + items + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitName, + span, + )); + vec![] + } + }); + attributes() .then_ignore(keyword(Keyword::Trait)) .then(ident()) .then(function::generics()) .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_body()) - .then_ignore(just(Token::RightBrace)) + .then(trait_body_or_error) .validate(|((((attributes, name), generics), where_clause), items), span, emit| { let attributes = validate_secondary_attributes(attributes, span, emit); TopLevelStatement::Trait(NoirTrait { @@ -70,13 +84,26 @@ fn trait_function_declaration() -> impl NoirParser { let trait_function_body_or_semicolon = block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); + let trait_function_body_or_semicolon_or_error = + trait_function_body_or_semicolon.or_not().validate(|body, span, emit| { + if let Some(body) = body { + body + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBraceOrArrowAfterFunctionParameters, + span, + )); + None + } + }); + keyword(Keyword::Fn) .ignore_then(ident()) .then(function::generics()) .then(parenthesized(function_declaration_parameters())) .then(function_return_type().map(|(_, typ)| typ)) .then(where_clause()) - .then(trait_function_body_or_semicolon) + .then(trait_function_body_or_semicolon_or_error) .map(|(((((name, generics), parameters), return_type), where_clause), body)| { TraitItem::Function { name, generics, parameters, return_type, where_clause, body } }) @@ -101,6 +128,24 @@ fn trait_type_declaration() -> impl NoirParser { /// /// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' pub(super) fn trait_implementation() -> impl NoirParser { + let body_or_error = + just(Token::LeftBrace) + .ignore_then(trait_implementation_body()) + .then_ignore(just(Token::RightBrace)) + .or_not() + .validate(|items, span, emit| { + if let Some(items) = items { + items + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, + span, + )); + + vec![] + } + }); + keyword(Keyword::Impl) .ignore_then(function::generics()) .then(path_no_turbofish()) @@ -108,13 +153,10 @@ pub(super) fn trait_implementation() -> impl NoirParser { .then_ignore(keyword(Keyword::For)) .then(parse_type()) .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_implementation_body()) - .then_ignore(just(Token::RightBrace)) + .then(body_or_error) .map(|args| { let (((other_args, object_type), where_clause), items) = args; let ((impl_generics, trait_name), trait_generics) = other_args; - TopLevelStatement::TraitImpl(NoirTraitImpl { impl_generics, trait_name, @@ -227,4 +269,54 @@ mod test { vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], ); } + + #[test] + fn parse_recover_function_without_left_brace_or_semicolon() { + let src = "fn foo(x: i32)"; + + let (trait_item, errors) = parse_recover(trait_function_declaration(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected { or -> after function parameters"); + + let Some(TraitItem::Function { name, parameters, body, .. }) = trait_item else { + panic!("Expected to parser trait item as function"); + }; + + assert_eq!(name.to_string(), "foo"); + assert_eq!(parameters.len(), 1); + assert!(body.is_none()); + } + + #[test] + fn parse_recover_trait_without_body() { + let src = "trait Foo"; + + let (top_level_statement, errors) = parse_recover(trait_definition(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected <, where or { after trait name"); + + let top_level_statement = top_level_statement.unwrap(); + let TopLevelStatement::Trait(trait_) = top_level_statement else { + panic!("Expected to parse a trait"); + }; + + assert_eq!(trait_.name.to_string(), "Foo"); + assert!(trait_.items.is_empty()); + } + + #[test] + fn parse_recover_trait_impl_without_body() { + let src = "impl Foo for Bar"; + + let (top_level_statement, errors) = parse_recover(trait_implementation(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected <, where or { after trait impl for type"); + + let top_level_statement = top_level_statement.unwrap(); + let TopLevelStatement::TraitImpl(trait_impl) = top_level_statement else { + panic!("Expected to parse a trait impl"); + }; + + assert!(trait_impl.items.is_empty()); + } } 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..bba596ed19f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -2460,6 +2460,201 @@ 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); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn 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::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn 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/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index 5140f5a5a8c..8bb56703e8a 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index 20272dfeecb..c8b8c3bb06e 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -6,8 +6,10 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index e939cd583e8..18417b376e5 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -216,7 +216,8 @@ "wasi", "wasmer", "Weierstraß", - "zshell" + "zshell", + "Linea" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md index c8a42c379e6..3ef6e014a2f 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -86,7 +86,7 @@ A Noir program makes a statement that can be verified. It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure + - Conceptually a tree structure - Leaves (inputs) are the `Field` type - Nodes contain arithmetic operations to combine them (gates) - The root is the final result (return value) @@ -99,12 +99,16 @@ You can dig deeper and use the `--print-acir` param to take a closer look at ind ### Use the `Field` type Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. +Some things to be mindful of when using a Field type for a regular integer value: +- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) + - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 +- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size +- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to :::tip Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates ::: -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. ### Use Arithmetic over non-arithmetic operations diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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.32.0/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index 10182ca83b0..ec979d60753 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -18,10 +18,12 @@ impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } } impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other } } impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } } +impl Eq for u16 { fn eq(self, other: u16) -> bool { self == other } } impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } } impl Eq for u1 { fn eq(self, other: u1) -> bool { self == other } } impl Eq for i8 { fn eq(self, other: i8) -> bool { self == other } } +impl Eq for i16 { fn eq(self, other: i16) -> bool { self == other } } impl Eq for i32 { fn eq(self, other: i32) -> bool { self == other } } impl Eq for i64 { fn eq(self, other: i64) -> bool { self == other } } @@ -157,6 +159,18 @@ impl Ord for u32 { } } +impl Ord for u16 { + fn cmp(self, other: u16) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} + impl Ord for u8 { fn cmp(self, other: u8) -> Ordering { if self < other { @@ -181,6 +195,18 @@ impl Ord for i8 { } } +impl Ord for i16 { + fn cmp(self, other: i16) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} + impl Ord for i32 { fn cmp(self, other: i32) -> Ordering { if self < other { 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/default.nr b/noir/noir-repo/noir_stdlib/src/default.nr index f9399bfb865..3ac5fbb394e 100644 --- a/noir/noir-repo/noir_stdlib/src/default.nr +++ b/noir/noir-repo/noir_stdlib/src/default.nr @@ -17,11 +17,14 @@ comptime fn derive_default(s: StructDefinition) -> Quoted { impl Default for Field { fn default() -> Field { 0 } } +impl Default for u1 { fn default() -> u1 { 0 } } impl Default for u8 { fn default() -> u8 { 0 } } +impl Default for u16 { fn default() -> u16 { 0 } } impl Default for u32 { fn default() -> u32 { 0 } } impl Default for u64 { fn default() -> u64 { 0 } } impl Default for i8 { fn default() -> i8 { 0 } } +impl Default for i16 { fn default() -> i16 { 0 } } impl Default for i32 { fn default() -> i32 { 0 } } impl Default for i64 { fn default() -> i64 { 0 } } 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..3abc630ab10 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 } @@ -193,12 +195,24 @@ impl Hash for Field { } } +impl Hash for u1 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, self as Field); + } +} + impl Hash for u8 { fn hash(self, state: &mut H) where H: Hasher{ H::write(state, self as Field); } } +impl Hash for u16 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, self as Field); + } +} + impl Hash for u32 { fn hash(self, state: &mut H) where H: Hasher{ H::write(state, self as Field); @@ -217,6 +231,12 @@ impl Hash for i8 { } } +impl Hash for i16 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, self as Field); + } +} + impl Hash for i32 { fn hash(self, state: &mut H) where H: Hasher{ H::write(state, self as Field); 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/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 54681632543..94889b7c3dd 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,6 +1,228 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_array)] + fn as_array(self) -> Option<[Expr]> {} + + #[builtin(expr_as_integer)] + fn as_integer(self) -> Option<(Field, bool)> {} + + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + + #[builtin(expr_as_bool)] + fn as_bool(self) -> Option {} + #[builtin(expr_as_function_call)] fn as_function_call(self) -> Option<(Expr, [Expr])> {} + + #[builtin(expr_as_if)] + fn as_if(self) -> Option<(Expr, Expr, Option)> {} + + #[builtin(expr_as_index)] + fn as_index(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_member_access)] + fn as_member_access(self) -> Option<(Expr, Quoted)> {} + + #[builtin(expr_as_repeated_element_array)] + fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_repeated_element_slice)] + fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_slice)] + fn as_slice(self) -> Option<[Expr]> {} + + #[builtin(expr_as_tuple)] + fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} +} + +mod tests { + use crate::meta::op::UnaryOp; + use crate::meta::op::BinaryOp; + + #[test] + fn test_expr_as_array() { + comptime + { + let expr = quote { [1, 2, 4] }.as_expr().unwrap(); + let elems = expr.as_array().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (2, false)); + assert_eq(elems[2].as_integer().unwrap(), (4, false)); + } + } + + #[test] + fn test_expr_as_integer() { + comptime + { + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + } + } + + #[test] + fn test_expr_as_binary_op() { + comptime + { + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + } + } + + #[test] + fn test_expr_as_bool() { + comptime + { + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + } + } + + #[test] + fn test_expr_as_function_call() { + comptime + { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } + + #[test] + fn test_expr_as_if() { + comptime + { + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + } + } + + #[test] + fn test_expr_as_index() { + comptime + { + let expr = quote { foo[bar] }.as_expr().unwrap(); + assert(expr.as_index().is_some()); + } + } + + #[test] + fn test_expr_as_member_access() { + comptime + { + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); + } + } + + #[test] + fn test_expr_as_repeated_element_array() { + comptime + { + let expr = quote { [1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_array().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_repeated_element_slice() { + comptime + { + let expr = quote { &[1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_slice().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_slice() { + comptime + { + let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); + let elems = expr.as_slice().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (3, false)); + assert_eq(elems[2].as_integer().unwrap(), (5, false)); + } + } + + #[test] + fn test_expr_as_tuple() { + comptime + { + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + } + } + + #[test] + fn test_expr_as_unary_op() { + comptime + { + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + } + } + + #[test] + fn test_automatically_unwraps_parenthesized_expression() { + comptime + { + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + } + } + + comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op + } + + comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op + } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..d16a8648bc2 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -5,9 +5,11 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +33,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); @@ -41,7 +45,7 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted result } -unconstrained pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { HANDLERS.insert(t, f); } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..ebd89677c50 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -0,0 +1,92 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + 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/ops/arith.nr b/noir/noir-repo/noir_stdlib/src/ops/arith.nr index df0ff978a7c..918c8e9bc28 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/arith.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/arith.nr @@ -10,6 +10,7 @@ impl Add for u64 { fn add(self, other: u64) -> u64 { self + other } } impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } } impl Add for u16 { fn add(self, other: u16) -> u16 { self + other } } impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } } +impl Add for u1 { fn add(self, other: u1) -> u1 { self + other } } impl Add for i8 { fn add(self, other: i8) -> i8 { self + other } } impl Add for i16 { fn add(self, other: i16) -> i16 { self + other } } @@ -28,6 +29,7 @@ impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other } } impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } } impl Sub for u16 { fn sub(self, other: u16) -> u16 { self - other } } impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } } +impl Sub for u1 { fn sub(self, other: u1) -> u1 { self - other } } impl Sub for i8 { fn sub(self, other: i8) -> i8 { self - other } } impl Sub for i16 { fn sub(self, other: i16) -> i16 { self - other } } @@ -46,6 +48,7 @@ impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other } } impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } } impl Mul for u16 { fn mul(self, other: u16) -> u16 { self * other } } impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } } +impl Mul for u1 { fn mul(self, other: u1) -> u1 { self * other } } impl Mul for i8 { fn mul(self, other: i8) -> i8 { self * other } } impl Mul for i16 { fn mul(self, other: i16) -> i16 { self * other } } @@ -64,6 +67,7 @@ impl Div for u64 { fn div(self, other: u64) -> u64 { self / other } } impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } } impl Div for u16 { fn div(self, other: u16) -> u16 { self / other } } impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } } +impl Div for u1 { fn div(self, other: u1) -> u1 { self / other } } impl Div for i8 { fn div(self, other: i8) -> i8 { self / other } } impl Div for i16 { fn div(self, other: i16) -> i16 { self / other } } @@ -80,6 +84,7 @@ impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u1 { fn rem(self, other: u1) -> u1 { self % other } } impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } diff --git a/noir/noir-repo/noir_stdlib/src/ops/bit.nr b/noir/noir-repo/noir_stdlib/src/ops/bit.nr index a31cfee878c..015d0008e7a 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/bit.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/bit.nr @@ -31,6 +31,7 @@ impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u1 { fn bitor(self, other: u1) -> u1 { self | other } } impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } @@ -49,6 +50,7 @@ impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other } } impl BitAnd for u32 { fn bitand(self, other: u32) -> u32 { self & other } } impl BitAnd for u16 { fn bitand(self, other: u16) -> u16 { self & other } } impl BitAnd for u8 { fn bitand(self, other: u8) -> u8 { self & other } } +impl BitAnd for u1 { fn bitand(self, other: u1) -> u1 { self & other } } impl BitAnd for i8 { fn bitand(self, other: i8) -> i8 { self & other } } impl BitAnd for i16 { fn bitand(self, other: i16) -> i16 { self & other } } @@ -67,6 +69,7 @@ impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other } } impl BitXor for u32 { fn bitxor(self, other: u32) -> u32 { self ^ other } } impl BitXor for u16 { fn bitxor(self, other: u16) -> u16 { self ^ other } } impl BitXor for u8 { fn bitxor(self, other: u8) -> u8 { self ^ other } } +impl BitXor for u1 { fn bitxor(self, other: u1) -> u1 { self ^ other } } impl BitXor for i8 { fn bitxor(self, other: i8) -> i8 { self ^ other } } impl BitXor for i16 { fn bitxor(self, other: i16) -> i16 { self ^ other } } 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_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr deleted file mode 100644 index 8b6f7b480c7..00000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - comptime - { - let expr = quote { foo(bar) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - } -} 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_exp/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml similarity index 74% rename from noir/noir-repo/test_programs/compile_success_empty/comptime_exp/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml index df36e0e05b0..c9a6f1e150e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "comptime_exp" +name = "comptime_trait_impl" type = "bin" authors = [""] compiler_version = ">=0.31.0" 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..c5cc7880112 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,14 +4,16 @@ use std::meta::unquote; fn main() { comptime { - foo::<3>(5); + unsafe { + foo::<3>(5) + }; submodule::bar(); } } // Call a different function from the interpreter, then have the // elaborator switch to the middle of foo from its previous scope in main -unconstrained comptime fn foo(x: Field) { +comptime fn foo(x: Field) { assert(modulus_num_bits() != 0); let cond = quote { modulus_num_bits() != 0 }; 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_loop_size_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Nargo.toml new file mode 100644 index 00000000000..d2a98e19742 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_loop_size_regression" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr new file mode 100644 index 00000000000..488304114b9 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr @@ -0,0 +1,16 @@ +struct EnumEmulation { + a: Option, + b: Option, + c: Option, +} + +unconstrained fn main() -> pub Field { + let mut emulated_enum = EnumEmulation { a: Option::some(1), b: Option::none(), c: Option::none() }; + + for _ in 0..1 { + assert_eq(emulated_enum.a.unwrap(), 1); + } + + emulated_enum.a = Option::some(2); + emulated_enum.a.unwrap() +} 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/gates_report_brillig.sh b/noir/noir-repo/test_programs/gates_report_brillig.sh new file mode 100644 index 00000000000..d3f6344dbf4 --- /dev/null +++ b/noir/noir-repo/test_programs/gates_report_brillig.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e + +# These tests are incompatible with gas reporting +excluded_dirs=("workspace" "workspace_default_member" "double_verify_nested_proof" "overlapping_dep_and_mod" "comptime_println") + +current_dir=$(pwd) +base_path="$current_dir/execution_success" +test_dirs=$(ls $base_path) + +# We generate a Noir workspace which contains all of the test cases +# This allows us to generate a gates report using `nargo info` for all of them at once. + +echo "[workspace]" > Nargo.toml +echo "members = [" >> Nargo.toml + +for dir in $test_dirs; do + if [[ " ${excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + if [[ ${CI-false} = "true" ]] && [[ " ${ci_excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + echo " \"execution_success/$dir\"," >> Nargo.toml +done + +echo "]" >> Nargo.toml + +nargo info --force-brillig --json > gates_report_brillig.json + +rm Nargo.toml 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/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3..06dd5e676bd 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `acvm_cli` to `acvm` diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 540d6d11bc0..b9b83d86836 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [build-dependencies] build-data.workspace = true diff --git a/noir/noir-repo/tooling/debugger/src/context.rs b/noir/noir-repo/tooling/debugger/src/context.rs index 6ec1aff8325..890732b579c 100644 --- a/noir/noir-repo/tooling/debugger/src/context.rs +++ b/noir/noir-repo/tooling/debugger/src/context.rs @@ -566,7 +566,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &mut self, call_info: AcirCallWaitInfo, ) -> DebugCommandResult { - let callee_circuit = &self.circuits[call_info.id as usize]; + let callee_circuit = &self.circuits[call_info.id.as_usize()]; let callee_witness_map = call_info.initial_witness; let callee_acvm = ACVM::new( self.backend, @@ -578,7 +578,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { let caller_acvm = std::mem::replace(&mut self.acvm, callee_acvm); self.acvm_stack .push(ExecutionFrame { circuit_id: self.current_circuit_id, acvm: caller_acvm }); - self.current_circuit_id = call_info.id; + self.current_circuit_id = call_info.id.0; // Explicitly handling the new ACVM status here handles two edge cases: // 1. there is a breakpoint set at the beginning of a circuit @@ -596,7 +596,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { let ACVMStatus::RequiresAcirCall(call_info) = self.acvm.get_status() else { unreachable!("Resolving an ACIR call, the caller is in an invalid state"); }; - let acir_to_call = &self.circuits[call_info.id as usize]; + let acir_to_call = &self.circuits[call_info.id.as_usize()]; let mut call_resolved_outputs = Vec::new(); for return_witness_index in acir_to_call.return_values.indices() { @@ -946,7 +946,7 @@ mod tests { brillig::IntegerBitSize, circuit::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, - opcodes::{BlockId, BlockType}, + opcodes::{AcirFunctionId, BlockId, BlockType}, }, native_types::Expression, AcirField, @@ -1210,7 +1210,12 @@ mod tests { outputs: vec![], predicate: None, }, - Opcode::Call { id: 1, inputs: vec![], outputs: vec![], predicate: None }, + Opcode::Call { + id: AcirFunctionId(1), + inputs: vec![], + outputs: vec![], + predicate: None, + }, Opcode::AssertZero(Expression::default()), ], ..Circuit::default() diff --git a/noir/noir-repo/tooling/fuzzer/Cargo.toml b/noir/noir-repo/tooling/fuzzer/Cargo.toml index 106d8abead1..31701991844 100644 --- a/noir/noir-repo/tooling/fuzzer/Cargo.toml +++ b/noir/noir-repo/tooling/fuzzer/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 03c6c9105ba..353a6ade904 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true# rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -30,6 +33,7 @@ thiserror.workspace = true fm.workspace = true rayon = "1.8.0" fxhash.workspace = true +convert_case = "0.6.0" [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] wasm-bindgen.workspace = true diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ca34d7686fd..366b158ad89 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -23,14 +23,14 @@ use fxhash::FxHashSet; use lsp_types::{ request::{ Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, - References, Rename, + References, Rename, SignatureHelpRequest, }, CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, - workspace::{self, Workspace}, + workspace::Workspace, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -55,7 +55,7 @@ use requests::{ on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, - on_test_run_request, on_tests_request, LspInitializationOptions, + on_signature_help_request, on_test_run_request, on_tests_request, LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -143,6 +143,7 @@ impl NargoLspService { .request::(on_hover_request) .request::(on_inlay_hint_request) .request::(on_completion_request) + .request::(on_signature_help_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -383,7 +384,7 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { pub fn insert_all_files_for_workspace_into_file_manager( state: &LspState, - workspace: &workspace::Workspace, + workspace: &Workspace, file_manager: &mut FileManager, ) { // First add files we cached: these have the source code of files that are modified @@ -410,7 +411,7 @@ fn prepare_package_from_source_string() { let client = ClientSocket::new_closed(); let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); - let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); + let (mut context, crate_id) = prepare_source(source.to_string(), &mut state); let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3a60de15c4a..4d2186badc3 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -223,7 +223,7 @@ mod notification_tests { use super::*; use lsp_types::{ - InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + InlayHintLabel, InlayHintParams, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, WorkDoneProgressParams, }; @@ -270,9 +270,9 @@ mod notification_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: 0, character: 0 }, - end: lsp_types::Position { line: 1, character: 0 }, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 1, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..302152f0829 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,74 +4,47 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; -use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, +use completion_items::{ + crate_completion_item, field_completion_item, simple_completion_item, + struct_field_completion_item, }; +use convert_case::{Case, Casing}; +use fm::{FileId, FileMap, PathString}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - ArrayLiteral, AsTraitPath, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, Expression, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, PathSegment, Pattern, - Statement, TraitImplItem, TraitItem, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UseTree, UseTreeKind, + AsTraitPath, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement, + MemberAccessExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, + PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, }, graph::{CrateId, Dependency}, hir::{ 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}, + hir_def::traits::Trait, + 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 crate::{requests::to_lsp_location, utils, LspState}; use super::process_request; +mod auto_import; 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; +mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -94,7 +67,9 @@ pub(crate) fn on_completion_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut finder = NodeFinder::new( + args.files, file_id, + source, byte_index, byte, args.crate_id, @@ -110,7 +85,9 @@ pub(crate) fn on_completion_request( } struct NodeFinder<'a> { + files: &'a FileMap, file: FileId, + lines: Vec<&'a str>, byte_index: usize, byte: Option, /// The module ID of the current file. @@ -129,11 +106,20 @@ struct NodeFinder<'a> { /// Type parameters in the current scope. These are collected when entering /// a struct, a function, etc., and cleared afterwards. type_parameters: HashSet, + /// ModuleDefIds we already suggested, so we don't offer these for auto-import. + suggested_module_def_ids: HashSet, + /// How many nested `mod` we are in deep + nesting: usize, + /// The line where an auto_import must be inserted + auto_import_line: usize, } impl<'a> NodeFinder<'a> { + #[allow(clippy::too_many_arguments)] fn new( + files: &'a FileMap, file: FileId, + source: &'a str, byte_index: usize, byte: Option, krate: CrateId, @@ -153,7 +139,9 @@ impl<'a> NodeFinder<'a> { }; let module_id = ModuleId { krate, local_id }; Self { + files, file, + lines: source.lines().collect(), byte_index, byte, root_module_id, @@ -164,6 +152,9 @@ impl<'a> NodeFinder<'a> { completion_items: Vec::new(), local_variables: HashMap::new(), type_parameters: HashSet::new(), + suggested_module_def_ids: HashSet::new(), + nesting: 0, + auto_import_line: 0, } } @@ -173,17 +164,26 @@ 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()); + } + } - fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); + Some(CompletionResponse::Array(items)) } } fn find_in_item(&mut self, item: &Item) { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; + } + } + if !self.includes_span(item.span) { return; } @@ -206,10 +206,19 @@ impl<'a> NodeFinder<'a> { ModuleId { krate: self.module_id.krate, local_id: *child_module }; } + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + self.find_in_parsed_module(&parsed_sub_module.contents); // Restore the old module before continuing self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; } ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), @@ -243,6 +252,9 @@ impl<'a> NodeFinder<'a> { } fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + self.find_in_path(&noir_trait_impl.trait_name, RequestedItems::OnlyTypes); + self.find_in_unresolved_type(&noir_trait_impl.object_type); + self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_trait_impl.impl_generics); @@ -253,15 +265,9 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + self.find_in_unresolved_type(&type_impl.object_type); + self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); @@ -277,10 +283,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_struct.generics); @@ -292,12 +294,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Function { @@ -357,22 +353,22 @@ impl<'a> NodeFinder<'a> { fn find_in_statement(&mut self, statement: &Statement) { match &statement.kind { - noirc_frontend::ast::StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement) => { self.find_in_let_statement(let_statement, true); } - noirc_frontend::ast::StatementKind::Constrain(constrain_statement) => { + StatementKind::Constrain(constrain_statement) => { self.find_in_constrain_statement(constrain_statement); } - noirc_frontend::ast::StatementKind::Expression(expression) => { + StatementKind::Expression(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement) => { self.find_in_assign_statement(assign_statement); } - noirc_frontend::ast::StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement) => { self.find_in_for_loop_statement(for_loop_statement); } - noirc_frontend::ast::StatementKind::Comptime(statement) => { + StatementKind::Comptime(statement) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -381,12 +377,10 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::StatementKind::Semi(expression) => { + StatementKind::Semi(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Break - | noirc_frontend::ast::StatementKind::Continue - | noirc_frontend::ast::StatementKind::Error => (), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } @@ -403,22 +397,6 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - fn find_in_assign_statement( - &mut self, - assign_statement: &noirc_frontend::ast::AssignStatement, - ) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { let old_local_variables = self.local_variables.clone(); let ident = &for_loop_statement.identifier; @@ -432,7 +410,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); @@ -442,69 +431,53 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - fn find_in_expression(&mut self, expression: &Expression) { match &expression.kind { - noirc_frontend::ast::ExpressionKind::Literal(literal) => self.find_in_literal(literal), - noirc_frontend::ast::ExpressionKind::Block(block_expression) => { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { self.find_in_block_expression(block_expression); } - noirc_frontend::ast::ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(prefix_expression) => { self.find_in_expression(&prefix_expression.rhs); } - noirc_frontend::ast::ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(index_expression) => { self.find_in_index_expression(index_expression); } - noirc_frontend::ast::ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(call_expression) => { self.find_in_call_expression(call_expression); } - noirc_frontend::ast::ExpressionKind::MethodCall(method_call_expression) => { + ExpressionKind::MethodCall(method_call_expression) => { self.find_in_method_call_expression(method_call_expression); } - noirc_frontend::ast::ExpressionKind::Constructor(constructor_expression) => { + ExpressionKind::Constructor(constructor_expression) => { self.find_in_constructor_expression(constructor_expression); } - noirc_frontend::ast::ExpressionKind::MemberAccess(member_access_expression) => { + ExpressionKind::MemberAccess(member_access_expression) => { self.find_in_member_access_expression(member_access_expression); } - noirc_frontend::ast::ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(cast_expression) => { self.find_in_cast_expression(cast_expression); } - noirc_frontend::ast::ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(infix_expression) => { self.find_in_infix_expression(infix_expression); } - noirc_frontend::ast::ExpressionKind::If(if_expression) => { + ExpressionKind::If(if_expression) => { self.find_in_if_expression(if_expression); } - noirc_frontend::ast::ExpressionKind::Variable(path) => { + ExpressionKind::Variable(path) => { self.find_in_path(path, RequestedItems::AnyItems); } - noirc_frontend::ast::ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions) => { self.find_in_expressions(expressions); } - noirc_frontend::ast::ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - noirc_frontend::ast::ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Comptime(block_expression, _) => { + ExpressionKind::Comptime(block_expression, _) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -513,58 +486,75 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::ExpressionKind::Quote(_) - | noirc_frontend::ast::ExpressionKind::Resolved(_) - | noirc_frontend::ast::ExpressionKind::Error => (), + ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), } - } - fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), + // "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_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } + fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return; } - } - fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } } - fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - } + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { + return; + }; - fn find_in_method_call_expression(&mut self, method_call_expression: &MethodCallExpression) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); + } + + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } } @@ -572,16 +562,20 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } + 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().to_case(Case::Snake); + self.complete_type_fields_and_methods(&typ, &prefix); + return; + } + } - fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); + self.find_in_expression(&member_access_expression.lhs); } fn find_in_if_expression(&mut self, if_expression: &IfExpression) { @@ -617,21 +611,6 @@ impl<'a> NodeFinder<'a> { self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { if let Some(span) = unresolved_type.span { if !self.includes_span(span) { @@ -640,48 +619,48 @@ impl<'a> NodeFinder<'a> { } match &unresolved_type.typ { - noirc_frontend::ast::UnresolvedTypeData::Array(_, unresolved_type) => { + UnresolvedTypeData::Array(_, unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Slice(unresolved_type) => { + UnresolvedTypeData::Slice(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Parenthesized(unresolved_type) => { + UnresolvedTypeData::Parenthesized(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Named(path, unresolved_types, _) => { + UnresolvedTypeData::Named(path, unresolved_types, _) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::MutableReference(unresolved_type) => { + UnresolvedTypeData::MutableReference(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { + UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); } - noirc_frontend::ast::UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::UnresolvedTypeData::Expression(_) - | noirc_frontend::ast::UnresolvedTypeData::FormatString(_, _) - | noirc_frontend::ast::UnresolvedTypeData::String(_) - | noirc_frontend::ast::UnresolvedTypeData::Unspecified - | noirc_frontend::ast::UnresolvedTypeData::Quoted(_) - | noirc_frontend::ast::UnresolvedTypeData::FieldElement - | noirc_frontend::ast::UnresolvedTypeData::Integer(_, _) - | noirc_frontend::ast::UnresolvedTypeData::Bool - | noirc_frontend::ast::UnresolvedTypeData::Unit - | noirc_frontend::ast::UnresolvedTypeData::Resolved(_) - | noirc_frontend::ast::UnresolvedTypeData::Error => (), + UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Error => (), } } @@ -706,20 +685,54 @@ impl<'a> NodeFinder<'a> { at_root = idents.is_empty(); } + let prefix = prefix.to_case(Case::Snake); + 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(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods(trait_, &prefix, FunctionKind::Any); + 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, @@ -743,6 +756,7 @@ impl<'a> NodeFinder<'a> { self.type_parameters_completion(&prefix); } } + self.complete_auto_imports(&prefix, requested_items); } } @@ -827,7 +841,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 { @@ -835,11 +849,11 @@ impl<'a> NodeFinder<'a> { segments.push(ident.clone()); if let Some(module_id) = self.resolve_module(segments) { - let prefix = String::new(); + let prefix = ""; let at_root = false; self.complete_in_module( module_id, - &prefix, + prefix, path_kind, at_root, module_completion_kind, @@ -849,7 +863,7 @@ impl<'a> NodeFinder<'a> { }; } else { // We are right after the last segment - let prefix = ident.to_string(); + let prefix = ident.to_string().to_case(Case::Snake); if segments.is_empty() { let at_root = true; self.complete_in_module( @@ -913,6 +927,103 @@ 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::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); + } + } + } + } + } + + fn complete_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + ) { + for (name, func_id) in &trait_.method_ids { + 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); + } + } + } + } + + 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(struct_field_completion_item(name, typ)); + } + } + } + + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +1032,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 +1062,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,9 +1079,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } @@ -977,9 +1092,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } } @@ -1011,131 +1128,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 +1137,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, + if let Ok(path_resolution) = path_resolver.resolve(self.def_maps, path, &mut None) { + return Some(path_resolution.module_def_id); } - } - 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 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); } } - } - 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()), - )); - } - } - } - - 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 { @@ -1211,994 +1163,76 @@ impl<'a> NodeFinder<'a> { } } +/// Returns true if name matches a prefix written in code. +/// `prefix` must already be in snake case. +/// This method splits both name and prefix by underscore, +/// then checks that every part of name starts with a part of +/// prefix, in order. +/// +/// For example: +/// +/// // "merk" and "ro" match "merkle" and "root" and are in order +/// name_matches("compute_merkle_root", "merk_ro") == true +/// +/// // "ro" matches "root", but "merkle" comes before it, so no match +/// name_matches("compute_merkle_root", "ro_mer") == false +/// +/// // neither "compute" nor "merkle" nor "root" start with "oot" +/// name_matches("compute_merkle_root", "oot") == false fn name_matches(name: &str, prefix: &str) -> bool { - name.starts_with(prefix) -} + let name = name.to_case(Case::Snake); + let name_parts: Vec<&str> = name.split('_').collect(); -fn module_completion_item(name: impl Into) -> CompletionItem { - simple_completion_item(name, CompletionItemKind::MODULE, None) -} + let mut last_index: i32 = -1; + for prefix_part in prefix.split('_') { + // Look past parts we already matched + let offset = if last_index >= 0 { last_index as usize + 1 } else { 0 }; -fn crate_completion_item(name: impl Into) -> CompletionItem { - simple_completion_item(name, CompletionItemKind::MODULE, None) -} + if let Some(mut name_part_index) = + name_parts.iter().skip(offset).position(|name_part| name_part.starts_with(prefix_part)) + { + // Need to adjust the index if we skipped some segments + name_part_index += offset; -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, + if last_index >= name_part_index as i32 { + return false; + } + last_index = name_part_index as i32; + } else { + return false; + } } + + true } -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, +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, } } #[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; - } +mod completion_name_matches_tests { + use crate::requests::completion::name_matches; #[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 test_name_matches() { + assert!(name_matches("foo", "foo")); + assert!(name_matches("foo_bar", "bar")); + assert!(name_matches("FooBar", "foo")); + assert!(name_matches("FooBar", "bar")); + assert!(name_matches("FooBar", "foo_bar")); + assert!(name_matches("bar_baz", "bar_b")); + + assert!(!name_matches("foo_bar", "o_b")); } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs new file mode 100644 index 00000000000..8d7824502c1 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -0,0 +1,210 @@ +use std::collections::BTreeMap; + +use lsp_types::{Position, Range, TextEdit}; +use noirc_frontend::{ + ast::ItemVisibility, + graph::{CrateId, Dependency}, + hir::def_map::{CrateDefMap, ModuleId}, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, +}; + +use super::{ + kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, + name_matches, + sort_text::auto_import_sort_text, + NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn complete_auto_imports(&mut self, prefix: &str, requested_items: RequestedItems) { + let current_module_parent_id = get_parent_module_id(self.def_maps, self.module_id); + + for (name, entries) in self.interner.get_auto_import_names() { + if !name_matches(name, prefix) { + continue; + } + + for (module_def_id, visibility) in entries { + if self.suggested_module_def_ids.contains(module_def_id) { + continue; + } + + let Some(mut completion_item) = self.module_def_id_completion_item( + *module_def_id, + name.clone(), + FunctionCompletionKind::NameAndParameters, + FunctionKind::Any, + requested_items, + ) else { + continue; + }; + + let module_full_path; + if let ModuleDefId::ModuleId(module_id) = module_def_id { + module_full_path = module_id_path( + *module_id, + &self.module_id, + current_module_parent_id, + self.interner, + self.dependencies, + ); + } else { + let Some(parent_module) = get_parent_module(self.interner, *module_def_id) + else { + continue; + }; + + match *visibility { + ItemVisibility::Public => (), + ItemVisibility::Private => { + // Technically this can't be reached because we don't record private items for auto-import, + // but this is here for completeness. + continue; + } + ItemVisibility::PublicCrate => { + if self.module_id.krate != parent_module.krate { + continue; + } + } + } + + module_full_path = module_id_path( + parent_module, + &self.module_id, + current_module_parent_id, + self.interner, + self.dependencies, + ); + } + + let full_path = if let ModuleDefId::ModuleId(..) = module_def_id { + module_full_path + } else { + format!("{}::{}", module_full_path, name) + }; + + let mut label_details = completion_item.label_details.unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + + let line = self.auto_import_line as u32; + let character = (self.nesting * 4) as u32; + let indent = " ".repeat(self.nesting * 4); + let mut newlines = "\n"; + + // If the line we are inserting into is not an empty line, insert an extra line to make some room + if let Some(line_text) = self.lines.get(line as usize) { + if !line_text.trim().is_empty() { + newlines = "\n\n"; + } + } + + completion_item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + new_text: format!("use {};{}{}", full_path, newlines, indent), + }]); + + completion_item.sort_text = Some(auto_import_sort_text()); + + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(*module_def_id); + } + } + } +} + +fn get_parent_module(interner: &NodeInterner, module_def_id: ModuleDefId) -> Option { + let reference_id = module_def_id_to_reference_id(module_def_id); + interner.reference_module(reference_id).copied() +} + +fn get_parent_module_id( + def_maps: &BTreeMap, + module_id: ModuleId, +) -> Option { + let crate_def_map = &def_maps[&module_id.krate]; + let module_data = &crate_def_map.modules()[module_id.local_id.0]; + module_data.parent.map(|parent| ModuleId { krate: module_id.krate, local_id: parent }) +} + +fn module_def_id_to_reference_id(module_def_id: ModuleDefId) -> ReferenceId { + match module_def_id { + ModuleDefId::ModuleId(id) => ReferenceId::Module(id), + ModuleDefId::FunctionId(id) => ReferenceId::Function(id), + ModuleDefId::TypeId(id) => ReferenceId::Struct(id), + ModuleDefId::TypeAliasId(id) => ReferenceId::Alias(id), + ModuleDefId::TraitId(id) => ReferenceId::Trait(id), + ModuleDefId::GlobalId(id) => ReferenceId::Global(id), + } +} + +/// Returns the path to reach an item inside `target_module_id` from inside `current_module_id`. +/// Returns a relative path if possible. +fn module_id_path( + target_module_id: ModuleId, + current_module_id: &ModuleId, + current_module_parent_id: Option, + interner: &NodeInterner, + dependencies: &[Dependency], +) -> String { + if Some(target_module_id) == current_module_parent_id { + return "super".to_string(); + } + + let mut segments: Vec<&str> = Vec::new(); + let mut is_relative = false; + + if let Some(module_attributes) = interner.try_module_attributes(&target_module_id) { + segments.push(&module_attributes.name); + + let mut current_attributes = module_attributes; + loop { + let parent_module_id = + &ModuleId { krate: target_module_id.krate, local_id: current_attributes.parent }; + + if current_module_id == parent_module_id { + is_relative = true; + break; + } + + if current_module_parent_id == Some(*parent_module_id) { + segments.push("super"); + is_relative = true; + break; + } + + let Some(parent_attributes) = interner.try_module_attributes(parent_module_id) else { + break; + }; + + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + } + + let crate_id = target_module_id.krate; + let crate_name = if is_relative { + None + } else { + match crate_id { + CrateId::Root(_) => Some("crate".to_string()), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Crate(_) => dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| dep.name.to_string()), + CrateId::Dummy => unreachable!("ICE: A dummy CrateId should not be accessible"), + } + }; + + if let Some(crate_name) = &crate_name { + segments.push(crate_name); + }; + + segments.reverse(); + segments.join("::") +} 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..d4190f5241c --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,391 @@ +use lsp_types::{ + Command, 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::{ + crate_or_module_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.first() { + 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 if let Type::Tuple(self_tuple_types) = self_type { + // Tuple types of different lengths seem to also have methods defined on all of them, + // so here we reject methods for tuples where the length doesn't match. + if let Type::Tuple(func_self_tuple_types) = func_self_type { + if self_tuple_types.len() != func_self_tuple_types.len() { + 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 + }; + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => completion_item, + FunctionCompletionKind::NameAndParameters => { + completion_item_with_trigger_parameter_hints_command(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 { + completion_item_with_sort_text( + simple_completion_item(name, CompletionItemKind::MODULE, None), + crate_or_module_sort_text(), + ) +} + +pub(super) fn crate_completion_item(name: impl Into) -> CompletionItem { + completion_item_with_sort_text( + simple_completion_item(name, CompletionItemKind::MODULE, None), + crate_or_module_sort_text(), + ) +} + +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 struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { + field_completion_item(field, typ.to_string()) +} + +pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +} + +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 } +} + +pub(super) fn completion_item_with_trigger_parameter_hints_command( + completion_item: CompletionItem, +) -> CompletionItem { + CompletionItem { + command: Some(Command { + title: "Trigger parameter hints".to_string(), + command: "editor.action.triggerParameterHints".to_string(), + arguments: None, + }), + ..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..9bdc603660f --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -0,0 +1,39 @@ +/// 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 { + "a".to_string() +} + +/// This is the default sort text. +pub(super) fn default_sort_text() -> String { + "b".to_string() +} + +/// We want crates and modules to show up after other things (for example +/// local variables, functions or types) +pub(super) fn crate_or_module_sort_text() -> String { + "c".to_string() +} + +/// Sort text for auto-import items. We want these to show up after local definitions. +pub(super) fn auto_import_sort_text() -> String { + "d".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 { + "e".to_string() +} + +/// We want to show operator methods last. +pub(super) fn operator_sort_text() -> String { + "f".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 { + "g".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..8cf06adc027 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1641 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, + completion_item_with_trigger_parameter_hints_command, crate_completion_item, + field_completion_item, module_completion_item, simple_completion_item, + snippet_completion_item, + }, + sort_text::{auto_import_sort_text, self_mismatch_sort_text}, + }, + on_completion_request, + }, + test_utils, + }; + + use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, + CompletionResponse, DidOpenTextDocumentParams, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_completions(src: &str) -> 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(), + }, + }, + ); + + 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"); + + if let Some(CompletionResponse::Array(items)) = response { + items + } else { + vec![] + } + } + + fn assert_items_match(mut items: Vec, mut expected: Vec) { + items.sort_by_key(|item| item.label.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); + } + + async fn assert_completion(src: &str, expected: Vec) { + let items = get_completions(src).await; + assert_items_match(items, expected); + } + + async fn assert_completion_excluding_auto_import(src: &str, expected: Vec) { + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.additional_text_edits.is_none()).collect(); + assert_items_match(items, expected); + } + + pub(super) fn function_completion_item( + label: impl Into, + insert_text: impl Into, + description: impl Into, + ) -> CompletionItem { + completion_item_with_trigger_parameter_hints_command(snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + Some(description.into()), + )) + } + + #[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 qux {} + } + + fn main() { + foo::q>|< + } + "#; + assert_completion(src, vec![module_completion_item("qux")]).await; + } + + #[test] + async fn test_complete_path_with_local_variable() { + let src = r#" + fn main() { + let local = 1; + l>|< + } + "#; + assert_completion_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + src, + vec![function_completion_item("hello()", "hello()", "fn()")], + ) + .await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "hello(…)", + "hello(${1:x}, ${2:y})", + "fn(i32, Field)".to_string(), + )], + ) + .await; + } + + #[test] + async fn test_complete_builtin_functions() { + let src = r#" + fn main() { + a>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![ + snippet_completion_item( + "assert(…)", + CompletionItemKind::FUNCTION, + "assert(${1:predicate})", + Some("fn(T)".to_string()), + ), + function_completion_item("assert_constant(…)", "assert_constant(${1:x})", "fn(T)"), + 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() { + So>|< + } + } + "#; + assert_completion_excluding_auto_import( + 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() { + So>|< + } + } + "#; + assert_completion_excluding_auto_import( + 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 { + ind>|< + } + } + "#; + assert_completion_excluding_auto_import( + 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(|lambda_var| lambda_v>|<) + } + "#; + assert_completion_excluding_auto_import( + src, + vec![simple_completion_item( + "lambda_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>|<) {} + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.label.starts_with('i')).collect(); + + assert_items_match( + items, + 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())), + ], + ); + } + + #[test] + async fn test_suggest_true() { + let src = r#" + fn main() { + let x = t>|< + } + "#; + assert_completion_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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: Cont>|< + } + "#; + assert_completion_excluding_auto_import( + 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: Cont>|<) {} + "#; + assert_completion_excluding_auto_import( + 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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("bar", "i32")]).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![ + function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)"), + function_completion_item("foobar2(…)", "foobar2(${1:x})", "fn(&mut self, i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![field_completion_item("foo", "i32")]).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![field_completion_item("bar", "Bar")]).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![function_completion_item("foo()", "foo()", "fn(self) -> Foo")]) + .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![field_completion_item("bar", "i32")]).await; + } + + #[test] + async fn test_completes_tuple_fields() { + let src = r#" + fn main() { + let tuple = (1, true); + tuple.>|< + } + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.kind == Some(CompletionItemKind::FIELD)); + let items = items.collect(); + + assert_items_match( + items, + vec![field_completion_item("0", "Field"), field_completion_item("1", "bool")], + ); + } + + #[test] + async fn test_completes_constructor_fields() { + let src = r#" + mod foobar { + struct Foo { + bb: i32, + bbb: Field, + bbbb: bool, + bbbbb: str<6>, + } + } + + fn main() { + foobar::Foo { bbb: 1, b>|<, bbbbb } + } + "#; + assert_completion( + src, + vec![field_completion_item("bb", "i32"), field_completion_item("bbbb", "bool")], + ) + .await; + } + + #[test] + async fn test_completes_trait_methods() { + let src = r#" + trait One { + fn one() -> Self; + } + + fn main() { + One::>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) + .await; + } + + #[test] + async fn test_auto_imports() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + + struct Foo { + } + + impl Foo { + // This is here to make sure it's not offered for completion + fn hello_world() {} + } + } + } + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 0, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + + assert_eq!(item.sort_text, Some(auto_import_sort_text())); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + fn foo() { + hel>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 2, character: 4 }, + end: Position { line: 2, character: 4 }, + }, + new_text: "use bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_not_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + mod baz { + fn foo() { + hel>|< + } + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use super::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 7, character: 8 }, + end: Position { line: 7, character: 8 }, + }, + new_text: "use super::bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_import_inserts_after_last_use() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 8, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + } + + #[test] + async fn test_does_not_auto_import_test_functions() { + let src = r#" + mod foo { + mod bar { + #[test] + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_does_not_auto_import_private_functions() { + let src = r#" + mod foo { + mod bar { + fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_auto_import_suggests_modules_too() { + let src = r#" + mod foo { + mod barbaz { + fn hello_world() {} + } + } + + fn main() { + barb>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "barbaz"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::barbaz)".to_string()), + description: None + }) + ); + } + + #[test] + async fn test_completes_matching_any_part_of_an_identifier_by_underscore() { + let src = r#" + struct Foo { + some_property: i32, + } + + fn foo(f: Foo) { + f.prop>|< + } + "#; + assert_completion(src, vec![field_completion_item("some_property", "i32")]).await; + } + + #[test] + async fn test_completes_in_impl_type() { + let src = r#" + struct FooBar { + } + + impl FooB>|< + "#; + + assert_completion( + src, + vec![simple_completion_item( + "FooBar", + CompletionItemKind::STRUCT, + Some("FooBar".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_completes_in_impl_for_type() { + let src = r#" + struct FooBar { + } + + impl Default for FooB>|< + "#; + + assert_completion( + src, + vec![simple_completion_item( + "FooBar", + CompletionItemKind::STRUCT, + Some("FooBar".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_auto_import_with_super() { + let src = r#" + pub fn bar_baz() {} + + mod tests { + fn foo() { + bar_b>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "bar_baz()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use super::bar_baz)".to_string()), + description: Some("fn()".to_string()) + }) + ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs new file mode 100644 index 00000000000..b487c8baf36 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs @@ -0,0 +1,132 @@ +/// This file includes the completion logic that's just about +/// traversing the AST without any additional logic. +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, CallExpression, CastExpression, ConstrainStatement, + Expression, ForRange, FunctionReturnType, IndexExpression, InfixExpression, Literal, + MethodCallExpression, NoirTrait, NoirTypeAlias, TraitImplItem, UnresolvedType, + }, + ParsedModule, +}; + +use super::NodeFinder; + +impl<'a> NodeFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + } + + pub(super) fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { + for unresolved_type in unresolved_type { + self.find_in_unresolved_type(unresolved_type); + } + } + + pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { + match return_type { + noirc_frontend::ast::FunctionReturnType::Default(_) => (), + noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { + self.find_in_unresolved_type(unresolved_type); + } + } + } + + pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { + self.find_in_unresolved_type(&noir_type_alias.typ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index b6fdc6f7842..95d8b82f84f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -319,6 +319,21 @@ fn format_parent_module_from_module_id( args: &ProcessRequestCallbackArgs, string: &mut String, ) -> bool { + let mut segments: Vec<&str> = Vec::new(); + + if let Some(module_attributes) = args.interner.try_module_attributes(module) { + segments.push(&module_attributes.name); + + let mut current_attributes = module_attributes; + while let Some(parent_attributes) = args.interner.try_module_attributes(&ModuleId { + krate: module.krate, + local_id: current_attributes.parent, + }) { + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + } + let crate_id = module.krate; let crate_name = match crate_id { CrateId::Root(_) => Some(args.crate_name.clone()), @@ -328,44 +343,21 @@ fn format_parent_module_from_module_id( .find(|dep| dep.crate_id == crate_id) .map(|dep| format!("{}", dep.name)), CrateId::Stdlib(_) => Some("std".to_string()), - CrateId::Dummy => None, + CrateId::Dummy => unreachable!("ICE: A dummy CrateId should not be accessible"), }; - let wrote_crate = if let Some(crate_name) = crate_name { - string.push_str(" "); - string.push_str(&crate_name); - true - } else { - false - }; - - let Some(module_attributes) = args.interner.try_module_attributes(module) else { - return wrote_crate; + if let Some(crate_name) = &crate_name { + segments.push(crate_name); }; - if wrote_crate { - string.push_str("::"); - } else { - string.push_str(" "); - } - - let mut segments = Vec::new(); - let mut current_attributes = module_attributes; - while let Some(parent_attributes) = args.interner.try_module_attributes(&ModuleId { - krate: module.krate, - local_id: current_attributes.parent, - }) { - segments.push(&parent_attributes.name); - current_attributes = parent_attributes; - } - - for segment in segments.iter().rev() { - string.push_str(segment); - string.push_str("::"); + if segments.is_empty() { + return false; } - string.push_str(&module_attributes.name); + segments.reverse(); + string.push_str(" "); + string.push_str(&segments.join("::")); true } @@ -431,7 +423,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..a1e083187d3 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, } } @@ -714,9 +722,9 @@ mod inlay_hints_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: start_line, character: 0 }, - end: lsp_types::Position { line: end_line, character: 0 }, + range: Range { + start: Position { line: start_line, character: 0 }, + end: Position { line: end_line, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index e138f839600..e88c7f11465 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -46,6 +46,7 @@ mod inlay_hint; mod profile_run; mod references; mod rename; +mod signature_help; mod test_run; mod tests; @@ -56,7 +57,8 @@ pub(crate) use { goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + rename::on_rename_request, signature_help::on_signature_help_request, + test_run::on_test_run_request, tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -234,13 +236,22 @@ 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, }, completion_item: None, })), + signature_help_provider: Some(lsp_types::OneOf::Right( + lsp_types::SignatureHelpOptions { + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs new file mode 100644 index 00000000000..c2c69185547 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -0,0 +1,291 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, PathString}; +use lsp_types::{ + ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, +}; +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::NodeInterner, + node_interner::ReferenceId, + ParsedModule, Type, +}; + +use crate::{utils, LspState}; + +use super::process_request; + +mod tests; +mod traversal; + +pub(crate) fn on_signature_help_request( + state: &mut LspState, + params: SignatureHelpParams, +) -> impl Future, ResponseError>> { + let uri = params.text_document_position_params.clone().text_document.uri; + + let result = process_request(state, params.text_document_position_params.clone(), |args| { + let path = PathString::from_path(uri.to_file_path().unwrap()); + args.files.get_file_id(&path).and_then(|file_id| { + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) + }) + }) + }); + future::ready(result) +} + +struct SignatureFinder<'a> { + file: FileId, + byte_index: usize, + interner: &'a NodeInterner, + signature_help: Option, +} + +impl<'a> SignatureFinder<'a> { + fn new(file: FileId, byte_index: usize, interner: &'a NodeInterner) -> Self { + Self { file, byte_index, interner, signature_help: None } + } + + fn find(&mut self, parsed_module: &ParsedModule) -> Option { + self.find_in_parsed_module(parsed_module); + + self.signature_help.clone() + } + + fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn try_compute_signature_help( + &mut self, + arguments: &[Expression], + arguments_span: Span, + name_span: Span, + has_self: bool, + ) { + if self.signature_help.is_some() { + return; + } + + if !self.includes_span(arguments_span) { + return; + } + + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + let location = Location::new(name_span, self.file); + + // Check if the call references a named function + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(location) { + let name = self.interner.function_name(&func_id); + let func_meta = self.interner.function_meta(&func_id); + + let signature_information = + self.func_meta_signature_information(func_meta, name, active_parameter, has_self); + self.set_signature_help(signature_information); + return; + } + + // Otherwise, the call must be a reference to an fn type + if let Some(mut typ) = self.interner.type_at_location(location) { + if let Type::Forall(_, forall_typ) = typ { + typ = *forall_typ; + } + if let Type::Function(args, return_type, _, unconstrained) = typ { + let signature_information = self.function_type_signature_information( + &args, + &return_type, + unconstrained, + active_parameter, + ); + self.set_signature_help(signature_information); + } + } + } + + fn func_meta_signature_information( + &self, + func_meta: &FuncMeta, + name: &str, + active_parameter: Option, + has_self: bool, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str("fn "); + label.push_str(name); + label.push('('); + for (index, (pattern, typ, _)) in func_meta.parameters.0.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + if has_self && index == 0 { + if let Type::MutableReference(..) = typ { + label.push_str("&mut self"); + } else { + label.push_str("self"); + } + } else { + let parameter_start = label.chars().count(); + + self.hir_pattern_to_argument(pattern, &mut label); + label.push_str(": "); + label.push_str(&typ.to_string()); + + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([ + parameter_start as u32, + parameter_end as u32, + ]), + documentation: None, + }); + } + } + label.push(')'); + + match &func_meta.return_type { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(typ) => { + label.push_str(" -> "); + label.push_str(&typ.to_string()); + } + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + fn function_type_signature_information( + &self, + args: &[Type], + return_type: &Type, + unconstrained: bool, + active_parameter: Option, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + if unconstrained { + label.push_str("unconstrained "); + } + label.push_str("fn("); + for (index, typ) in args.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(&typ.to_string()); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + if let Type::Unit = return_type { + // Nothing + } else { + label.push_str(" -> "); + label.push_str(&return_type.to_string()); + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + 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 set_signature_help(&mut self, signature_information: SignatureInformation) { + let signature_help = SignatureHelp { + active_parameter: signature_information.active_parameter, + signatures: vec![signature_information], + active_signature: Some(0), + }; + self.signature_help = Some(signature_help); + } + + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs new file mode 100644 index 00000000000..c48ee159084 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -0,0 +1,196 @@ +#[cfg(test)] +mod signature_help_tests { + use crate::{ + notifications::on_did_open_text_document, requests::on_signature_help_request, test_utils, + }; + + use lsp_types::{ + DidOpenTextDocumentParams, ParameterLabel, Position, SignatureHelp, SignatureHelpParams, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_signature_help(src: &str) -> SignatureHelp { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .find_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .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(), + }, + }, + ); + + on_signature_help_request( + &mut state, + SignatureHelpParams { + context: None, + text_document_position_params: 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 }, + }, + ) + .await + .expect("Could not execute on_signature_help_request") + .unwrap() + } + + fn check_label(signature_label: &str, parameter_label: &ParameterLabel, expected_string: &str) { + let ParameterLabel::LabelOffsets(offsets) = parameter_label else { + panic!("Expected label to be LabelOffsets, got {:?}", parameter_label); + }; + + assert_eq!(&signature_label[offsets[0] as usize..offsets[1] as usize], expected_string); + } + + #[test] + async fn test_signature_help_for_call_at_first_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + fn wrapper(x: u32) {} + + fn bar() { + wrapper(foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_call_between_arguments() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1,>|< 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_at_second_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, >|<2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_past_last_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, 2, >|<); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(2)); + } + + #[test] + async fn test_signature_help_for_method_call() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self, x: i32, y: Field) -> u32 { 0 } + } + + fn wrapper(x: u32) {} + + fn bar(f: Foo) { + wrapper(f.foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(self, x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_fn_call() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + let f = foo; + f(>|<1, 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn(i32, Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "i32"); + check_label(&signature.label, ¶ms[1].label, "Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs new file mode 100644 index 00000000000..22f92a86124 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs @@ -0,0 +1,304 @@ +/// This file includes the signature help logic that's just about +/// traversing the AST without any additional logic. +use super::SignatureFinder; + +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, + ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, + IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, + TraitImplItem, TraitItem, TypeImpl, + }, + parser::{Item, ItemKind}, + ParsedModule, +}; + +impl<'a> SignatureFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_item(&mut self, item: &Item) { + if !self.includes_span(item.span) { + return; + } + + match &item.kind { + ItemKind::Submodules(parsed_sub_module) => { + self.find_in_parsed_module(&parsed_sub_module.contents); + } + ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), + ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), + ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), + ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), + ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), + ItemKind::Import(..) + | ItemKind::TypeAlias(_) + | ItemKind::Struct(_) + | ItemKind::ModuleDecl(_) => (), + } + } + + pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { + self.find_in_block_expression(&noir_function.def.body); + } + + pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + for item in &noir_trait_impl.items { + self.find_in_trait_impl_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + for (method, span) in &type_impl.methods { + if self.includes_span(*span) { + self.find_in_noir_function(method); + } + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { + match trait_item { + TraitItem::Function { body, .. } => { + if let Some(body) = body { + self.find_in_block_expression(body); + }; + } + TraitItem::Constant { default_value, .. } => { + if let Some(default_value) = default_value { + self.find_in_expression(default_value); + } + } + TraitItem::Type { .. } => (), + } + } + + pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { + for statement in &block_expression.statements { + if self.includes_span(statement.span) { + self.find_in_statement(statement); + } + } + } + + pub(super) fn find_in_statement(&mut self, statement: &Statement) { + if !self.includes_span(statement.span) { + return; + } + + match &statement.kind { + StatementKind::Let(let_statement) => { + self.find_in_let_statement(let_statement); + } + StatementKind::Constrain(constrain_statement) => { + self.find_in_constrain_statement(constrain_statement); + } + StatementKind::Expression(expression) => { + self.find_in_expression(expression); + } + StatementKind::Assign(assign_statement) => { + self.find_in_assign_statement(assign_statement); + } + StatementKind::For(for_loop_statement) => { + self.find_in_for_loop_statement(for_loop_statement); + } + StatementKind::Comptime(statement) => { + self.find_in_statement(statement); + } + StatementKind::Semi(expression) => { + self.find_in_expression(expression); + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } + } + + pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { + self.find_in_expression(&let_statement.expression); + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { + self.find_in_for_range(&for_loop_statement.range); + self.find_in_expression(&for_loop_statement.block); + } + + pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { + match lvalue { + LValue::Ident(_) => (), + LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), + LValue::Index { array, index, span: _ } => { + self.find_in_lvalue(array); + self.find_in_expression(index); + } + LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), + } + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_expression(&mut self, expression: &Expression) { + match &expression.kind { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Prefix(prefix_expression) => { + self.find_in_expression(&prefix_expression.rhs); + } + ExpressionKind::Index(index_expression) => { + self.find_in_index_expression(index_expression); + } + ExpressionKind::Call(call_expression) => { + self.find_in_call_expression(call_expression, expression.span); + } + ExpressionKind::MethodCall(method_call_expression) => { + self.find_in_method_call_expression(method_call_expression, expression.span); + } + ExpressionKind::Constructor(constructor_expression) => { + self.find_in_constructor_expression(constructor_expression); + } + ExpressionKind::MemberAccess(member_access_expression) => { + self.find_in_member_access_expression(member_access_expression); + } + ExpressionKind::Cast(cast_expression) => { + self.find_in_cast_expression(cast_expression); + } + ExpressionKind::Infix(infix_expression) => { + self.find_in_infix_expression(infix_expression); + } + ExpressionKind::If(if_expression) => { + self.find_in_if_expression(if_expression); + } + ExpressionKind::Tuple(expressions) => { + self.find_in_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Unquote(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Comptime(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Variable(_) + | ExpressionKind::AsTraitPath(_) + | ExpressionKind::Quote(_) + | ExpressionKind::Resolved(_) + | ExpressionKind::Error => (), + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_constructor_expression( + &mut self, + constructor_expression: &ConstructorExpression, + ) { + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_member_access_expression( + &mut self, + member_access_expression: &MemberAccessExpression, + ) { + self.find_in_expression(&member_access_expression.lhs); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { + self.find_in_expression(&if_expression.condition); + self.find_in_expression(&if_expression.consequence); + + if let Some(alternative) = &if_expression.alternative { + self.find_in_expression(alternative); + } + } + + pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { + self.find_in_expression(&lambda.body); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 5afda0d292a..3ac1f35e18e 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ CompletionOptions, DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, - HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, + HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, SignatureHelpOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; @@ -161,6 +161,10 @@ pub(crate) struct ServerCapabilities { /// The server provides completion support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) completion_provider: Option>, + + /// The server provides signature help support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) signature_help_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 56e88dacf2d..046eca88099 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/nargo/src/ops/execute.rs b/noir/noir-repo/tooling/nargo/src/ops/execute.rs index c9cc60d03d9..2e214c4e425 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/execute.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/execute.rs @@ -139,9 +139,9 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> }); // Set current function to the circuit we are about to execute - self.current_function_index = call_info.id as usize; + self.current_function_index = call_info.id.as_usize(); // Execute the ACIR call - let acir_to_call = &self.functions[call_info.id as usize]; + let acir_to_call = &self.functions[call_info.id.as_usize()]; let initial_witness = call_info.initial_witness; let call_solved_witness = self.execute_circuit(initial_witness)?; @@ -163,7 +163,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> } } acvm.resolve_pending_acir_call(call_resolved_outputs); - self.witness_stack.push(call_info.id, call_solved_witness); + self.witness_stack.push(call_info.id.0, call_solved_witness); } } } diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index dabb779ae8d..f3d9f92caaa 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -8,6 +8,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `nargo_cli` to `nargo` diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index 3f8cd055569..4dcfccdf085 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -207,7 +207,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ panic!("JSON was not well-formatted {:?}\n\n{:?}", e, std::str::from_utf8(&output.stdout)) }}); - let num_opcodes = &json["programs"][0]["functions"][0]["acir_opcodes"]; + let num_opcodes = &json["programs"][0]["functions"][0]["opcodes"]; assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); "#; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index a6395d1c8c9..7e4a8b4117d 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -100,8 +100,7 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = - table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]); for program_info in info_report.programs { let program_rows: Vec = program_info.into(); @@ -176,18 +175,32 @@ struct ProgramInfo { #[serde(skip)] expression_width: ExpressionWidth, functions: Vec, + #[serde(skip)] + unconstrained_functions_opcodes: usize, + unconstrained_functions: Vec, } impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { + let mut main = vecmap(program_info.functions, |function| { row![ Fm->format!("{}", program_info.package_name), Fc->format!("{}", function.name), format!("{:?}", program_info.expression_width), - Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", function.opcodes), + Fc->format!("{}", program_info.unconstrained_functions_opcodes), ] - }) + }); + main.extend(vecmap(program_info.unconstrained_functions, |function| { + row![ + Fm->format!("{}", program_info.package_name), + Fc->format!("{}", function.name), + format!("N/A", ), + Fc->format!("N/A"), + Fc->format!("{}", function.opcodes), + ] + })); + main } } @@ -203,7 +216,7 @@ struct ContractInfo { #[derive(Debug, Serialize)] struct FunctionInfo { name: String, - acir_opcodes: usize, + opcodes: usize, } impl From for Vec { @@ -213,7 +226,7 @@ impl From for Vec { Fm->format!("{}", contract_info.name), Fc->format!("{}", function.name), format!("{:?}", contract_info.expression_width), - Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", function.opcodes), ] }) } @@ -231,9 +244,35 @@ fn count_opcodes_and_gates_in_program( .enumerate() .map(|(i, function)| FunctionInfo { name: compiled_program.names[i].clone(), - acir_opcodes: function.opcodes.len(), + opcodes: function.opcodes.len(), }) .collect(); - ProgramInfo { package_name: package.name.to_string(), expression_width, functions } + let opcodes_len: Vec = compiled_program + .bytecode + .unconstrained_functions + .iter() + .map(|func| func.bytecode.len()) + .collect(); + let unconstrained_functions_opcodes = compiled_program + .bytecode + .unconstrained_functions + .into_par_iter() + .map(|function| function.bytecode.len()) + .sum(); + let unconstrained_info: Vec = compiled_program + .brillig_names + .clone() + .iter() + .zip(opcodes_len) + .map(|(name, len)| FunctionInfo { name: name.clone(), opcodes: len }) + .collect(); + + ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions, + unconstrained_functions_opcodes, + unconstrained_functions: unconstrained_info, + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 05b2fdb7d52..9868f259097 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] bytecount = "0.6.3" noirc_frontend.workspace = true 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/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index 7c9faa4562a..e4766e44859 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index 4c0c1f75e42..a7baf334bff 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs index aecb620b79d..b79f232d9e8 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs @@ -107,10 +107,8 @@ pub(super) fn arb_abi_type() -> BoxedStrategy { (1..10u32, inner.clone()) .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) .boxed(), - prop::collection::vec(inner.clone(), 1..10) - .prop_map(|fields| { AbiType::Tuple { fields } }) - .boxed(), - (".*", prop::collection::vec((".+", inner), 1..10)) + vec(inner.clone(), 1..10).prop_map(|fields| { AbiType::Tuple { fields } }).boxed(), + (".*", vec((".+", inner), 1..10)) .prop_map(|(path, mut fields)| { // Require that all field names are unique. ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); @@ -141,7 +139,7 @@ fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { prop_compose! { pub(super) fn arb_abi_and_input_map() - (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + (mut parameters_with_values in vec(arb_abi_param_and_value(), 0..100), return_type: Option) -> (Abi, InputMap) { // Require that all parameter names are unique. ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); 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/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index ef4a468b661..b0c11979d17 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -121,8 +121,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result::from_serde(&return_struct) - .map_err(|err| err.to_string().into()) + ::from_serde(&return_struct).map_err(|err| err.to_string().into()) } #[wasm_bindgen(js_name = serializeWitness)] @@ -155,7 +154,7 @@ pub fn abi_decode_error( AbiErrorType::Custom(typ) => { let input_value = decode_value(&mut raw_error.data.into_iter(), &typ)?; let json_types = JsonTypes::try_from_input_value(&input_value, &typ)?; - ::from_serde(&json_types) + ::from_serde(&json_types) .map_err(|err| err.to_string().into()) } } diff --git a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml index 4249604f949..13ff68e423a 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index 91f02157414..ffffc6345d5 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -37,6 +37,8 @@ pub struct ProgramArtifact { pub file_map: BTreeMap, pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } impl From for ProgramArtifact { @@ -49,6 +51,7 @@ impl From for ProgramArtifact { debug_symbols: ProgramDebugInfo { debug_infos: compiled_program.debug }, file_map: compiled_program.file_map, names: compiled_program.names, + brillig_names: compiled_program.brillig_names, } } } @@ -64,6 +67,7 @@ impl From for CompiledProgram { file_map: program.file_map, warnings: vec![], names: program.names, + brillig_names: program.brillig_names, } } } diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 0ccd56b791f..136775d5831 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e072c63f274..0fa12239d05 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -128,7 +128,7 @@ mod tests { } impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { + fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let response = self .mock_responses .get(artifact_path) @@ -173,6 +173,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index f91e50d8716..d7f3cbb9b85 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -188,6 +188,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file @@ -228,6 +229,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: vec!["main".to_string()], }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index 772c87ad1cb..edb3e1b0f08 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -100,7 +100,7 @@ fn format_binary_field_op(op: &BinaryFieldOp) -> String { } } -fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { +fn format_binary_int(op: &BinaryIntOp) -> String { match op { BinaryIntOp::Add => "add".to_string(), BinaryIntOp::Sub => "sub".to_string(), 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 From 551576dbf2438fe36f1afdba0925a560c2768d80 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 20 Aug 2024 14:59:45 +0000 Subject: [PATCH 16/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 7e37bfd1b7bbe4f5ddadabd7802a734eaf1012ea Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 20 Aug 2024 15:20:53 +0000 Subject: [PATCH 17/21] fmt verify_honk_proof --- .../execution_success/verify_honk_proof/src/main.nr | 7 +------ noir/verify_honk_proof/src/main.nr | 10 ++-------- 2 files changed, 3 insertions(+), 14 deletions(-) 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 index 667ed25f70b..d785dabffb0 100644 --- 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 @@ -13,10 +13,5 @@ fn main( // I believe we want to eventually make it public too though. key_hash: Field ) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); + std::verify_proof(verification_key, proof, public_inputs, key_hash); } diff --git a/noir/verify_honk_proof/src/main.nr b/noir/verify_honk_proof/src/main.nr index d7c08cb5c2c..672e3f4f9dc 100644 --- a/noir/verify_honk_proof/src/main.nr +++ b/noir/verify_honk_proof/src/main.nr @@ -10,13 +10,7 @@ fn main( 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 - // WORKTODO: Can we delete this program? noir needed it for some reason but its not used in bberg + key_hash: Field// WORKTODO: Can we delete this program? noir needed it for some reason but its not used in bberg ) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); + std::verify_proof(verification_key, proof, public_inputs, key_hash); } From 6946f7f733b2d0d3bc748fde8350b8ba2cfea876 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 20 Aug 2024 18:42:53 +0000 Subject: [PATCH 18/21] [1 changes] feat(optimization): Follow past `array_set`s when optimizing `array_get`s (https://github.com/noir-lang/noir/pull/5772) feat: user `super::` in LSP autocompletion if possible (https://github.com/noir-lang/noir/pull/5751) fix: unconstrained fn mismatch is now a warning (https://github.com/noir-lang/noir/pull/5764) feat(perf): mem2reg function state for value loads to optimize across blocks (https://github.com/noir-lang/noir/pull/5757) feat: add `Expr::as_array`, `Expr::as_repeated_element_array` and same for slice (https://github.com/noir-lang/noir/pull/5750) chore: Add Brillig loop bytecode size regression and update noir-gates-diff report (https://github.com/noir-lang/noir/pull/5747) feat: fault-tolerant parsing of `fn` and `impl` (https://github.com/noir-lang/noir/pull/5753) fix: add missing trait impls for integer types to stdlib (https://github.com/noir-lang/noir/pull/5738) chore: refactor ACIR function IDs from raw integers to struct (https://github.com/noir-lang/noir/pull/5748) feat: (LSP) suggest names that match any part of the current prefix (https://github.com/noir-lang/noir/pull/5752) feat: LSP auto-import completion (https://github.com/noir-lang/noir/pull/5741) fix: Allow comptime code to use break without also being `unconstrained` (https://github.com/noir-lang/noir/pull/5744) feat: add `Expr::as_any_integer` and `Expr::as_member_access` (https://github.com/noir-lang/noir/pull/5742) chore: clarify Field use (https://github.com/noir-lang/noir/pull/5740) feat: add `Expr::as_binary_op` (https://github.com/noir-lang/noir/pull/5734) chore(docs): expanding solidity verifier chain list (https://github.com/noir-lang/noir/pull/5587) chore: apply some new lints across workspace (https://github.com/noir-lang/noir/pull/5736) feat: suggest trait methods in LSP completion (https://github.com/noir-lang/noir/pull/5735) feat: LSP autocomplete constructor fields (https://github.com/noir-lang/noir/pull/5732) feat: add `Expr::as_unary` (https://github.com/noir-lang/noir/pull/5731) chore: count brillig opcodes in nargo info (https://github.com/noir-lang/noir/pull/5189) feat: suggest tuple fields in LSP completion (https://github.com/noir-lang/noir/pull/5730) feat: add `Expr::as_bool` (https://github.com/noir-lang/noir/pull/5729) feat: add `Expr` methods: `as_tuple`, `as_parenthesized`, `as_index`, `as_if` (https://github.com/noir-lang/noir/pull/5726) feat: LSP signature help (https://github.com/noir-lang/noir/pull/5725) chore: split LSP completion.rs into several files (https://github.com/noir-lang/noir/pull/5723) feat: add `TraitImpl::trait_generic_args` and `TraitImpl::methods` (https://github.com/noir-lang/noir/pull/5722) fix: let LSP autocompletion work in more contexts (https://github.com/noir-lang/noir/pull/5719) fix(frontend): Continue type check if we are missing an unsafe block (https://github.com/noir-lang/noir/pull/5720) feat: add `unsafe` blocks for calling unconstrained code from constrained functions (https://github.com/noir-lang/noir/pull/4429) --- .noir-sync-commit | 2 +- .../.github/workflows/gates_report.yml | 2 +- .../workflows/gates_report_brillig.yml | 92 + noir/noir-repo/.gitignore | 1 + noir/noir-repo/Cargo.lock | 1 + noir/noir-repo/Cargo.toml | 6 + noir/noir-repo/acvm-repo/acir/Cargo.toml | 3 + .../acvm-repo/acir/src/circuit/mod.rs | 8 +- .../acvm-repo/acir/src/circuit/opcodes.rs | 6 +- .../acir/src/circuit/opcodes/function_id.rs | 17 + .../acir/tests/test_program_serialization.rs | 8 +- .../noir-repo/acvm-repo/acir_field/Cargo.toml | 3 + .../acvm-repo/acir_field/src/field_element.rs | 4 +- .../acvm-repo/acir_field/src/generic_ark.rs | 4 +- noir/noir-repo/acvm-repo/acvm/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs | 6 +- noir/noir-repo/acvm-repo/acvm_js/Cargo.toml | 3 + noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../acvm-repo/acvm_js/src/execute.rs | 4 +- .../acvm_js/src/js_execution_error.rs | 8 +- .../acvm-repo/blackbox_solver/Cargo.toml | 3 + .../bn254_blackbox_solver/Cargo.toml | 3 + .../src/generator/generators.rs | 2 +- noir/noir-repo/acvm-repo/brillig/Cargo.toml | 3 + .../noir-repo/acvm-repo/brillig_vm/Cargo.toml | 3 + noir/noir-repo/aztec_macros/Cargo.toml | 3 + .../src/transforms/contract_interface.rs | 176 +- .../aztec_macros/src/utils/parse_utils.rs | 5 +- noir/noir-repo/compiler/fm/Cargo.toml | 3 + .../noir-repo/compiler/noirc_arena/Cargo.toml | 3 + .../compiler/noirc_driver/Cargo.toml | 3 + .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 11 +- .../compiler/noirc_driver/src/program.rs | 2 + .../compiler/noirc_errors/Cargo.toml | 3 + .../compiler/noirc_errors/src/position.rs | 2 +- .../compiler/noirc_evaluator/Cargo.toml | 3 + .../src/brillig/brillig_gen.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 2 + .../src/brillig/brillig_ir/artifact.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 9 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 6 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 87 +- .../noirc_evaluator/src/ssa/ir/dfg.rs | 9 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 72 +- .../noirc_evaluator/src/ssa/ir/map.rs | 2 +- .../noirc_evaluator/src/ssa/opt/die.rs | 356 +++- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 47 +- .../src/ssa/ssa_gen/context.rs | 8 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 12 +- .../noirc_frontend/src/ast/expression.rs | 18 +- .../compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../noirc_frontend/src/ast/statement.rs | 10 +- .../compiler/noirc_frontend/src/debug/mod.rs | 24 +- .../noirc_frontend/src/elaborator/comptime.rs | 8 + .../src/elaborator/expressions.rs | 23 +- .../noirc_frontend/src/elaborator/mod.rs | 31 +- .../src/elaborator/statements.rs | 4 +- .../noirc_frontend/src/elaborator/traits.rs | 13 +- .../noirc_frontend/src/elaborator/types.rs | 80 +- .../src/hir/comptime/hir_to_display_ast.rs | 7 +- .../src/hir/comptime/interpreter.rs | 3 + .../src/hir/comptime/interpreter/builtin.rs | 349 +++- .../interpreter/builtin/builtin_helpers.rs | 86 +- .../noirc_frontend/src/hir/comptime/value.rs | 70 +- .../src/hir/def_collector/dc_mod.rs | 48 +- .../src/hir/def_map/module_def.rs | 2 +- .../src/hir/type_check/errors.rs | 10 + .../noirc_frontend/src/hir_def/expr.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- .../noirc_frontend/src/hir_def/traits.rs | 8 +- .../noirc_frontend/src/hir_def/types.rs | 138 +- .../noirc_frontend/src/lexer/token.rs | 6 + .../compiler/noirc_frontend/src/locations.rs | 76 +- .../src/monomorphization/ast.rs | 13 +- .../src/monomorphization/mod.rs | 45 +- .../noirc_frontend/src/node_interner.rs | 107 +- .../noirc_frontend/src/parser/errors.rs | 12 + .../noirc_frontend/src/parser/parser.rs | 136 +- .../src/parser/parser/function.rs | 35 +- .../src/parser/parser/traits.rs | 108 +- .../noirc_frontend/src/parser/parser/types.rs | 18 +- .../noirc_frontend/src/resolve_locations.rs | 6 + .../compiler/noirc_frontend/src/tests.rs | 195 ++ .../compiler/noirc_printable_type/Cargo.toml | 3 + .../compiler/noirc_printable_type/src/lib.rs | 1 + noir/noir-repo/compiler/wasm/Cargo.toml | 4 +- noir/noir-repo/cspell.json | 3 +- .../docs/explainers/explainer-writing-noir.md | 8 +- .../docs/how_to/how-to-solidity-verifier.md | 8 +- .../docs/docs/noir/concepts/unconstrained.md | 9 +- .../how_to/how-to-solidity-verifier.md | 8 +- .../reference/debugger/_category_.json | 2 +- noir/noir-repo/noir_stdlib/src/array.nr | 20 +- noir/noir-repo/noir_stdlib/src/cmp.nr | 26 + .../src/collections/bounded_vec.nr | 4 +- .../noir_stdlib/src/collections/map.nr | 4 +- .../noir_stdlib/src/collections/umap.nr | 6 +- noir/noir-repo/noir_stdlib/src/default.nr | 3 + noir/noir-repo/noir_stdlib/src/field/bn254.nr | 50 +- noir/noir-repo/noir_stdlib/src/hash/mod.nr | 22 +- noir/noir-repo/noir_stdlib/src/lib.nr | 18 +- .../noir_stdlib/src/{unsafe.nr => mem.nr} | 1 + noir/noir-repo/noir_stdlib/src/meta/expr.nr | 222 ++ noir/noir-repo/noir_stdlib/src/meta/mod.nr | 8 +- noir/noir-repo/noir_stdlib/src/meta/op.nr | 92 + .../noir_stdlib/src/meta/trait_impl.nr | 7 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 3 + noir/noir-repo/noir_stdlib/src/ops/arith.nr | 5 + noir/noir-repo/noir_stdlib/src/ops/bit.nr | 3 + noir/noir-repo/noir_stdlib/src/option.nr | 2 +- noir/noir-repo/noir_stdlib/src/slice.nr | 2 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 62 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../array_length_defaulting/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 4 +- .../regression_5008/src/main.nr | 4 +- .../turbofish_generic_count/src/main.nr | 2 +- .../unconstrained_ref/src/main.nr | 4 +- .../arithmetic_generics/src/main.nr | 4 +- .../brillig_cast/src/main.nr | 12 +- .../src/main.nr | 14 +- .../src/main.nr | 34 +- .../brillig_modulo/src/main.nr | 28 +- .../brillig_slice_input/src/main.nr | 12 +- .../comptime_exp/src/main.nr | 8 - .../comptime_fmt_strings/src/main.nr | 2 +- .../Nargo.toml | 2 +- .../comptime_trait_impl/src/main.nr | 32 + .../comptime_type/src/main.nr | 4 + .../macros_in_comptime/src/main.nr | 6 +- .../zeroed_slice/src/main.nr | 2 +- .../check_uncostrained_regression/src/main.nr | 4 +- .../brillig_assert_fail/src/main.nr | 6 +- .../brillig_assert_msg_runtime/src/main.nr | 6 +- .../src/main.nr | 4 +- .../regression_5202/src/main.nr | 8 +- .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 4 + .../Nargo.toml | 7 + .../Prover.toml | 1 + .../src/main.nr | 4 + .../acir_inside_brillig_recursion/src/main.nr | 4 +- .../aes128_encrypt/src/main.nr | 21 +- .../src/main.nr | 6 +- .../execution_success/bigint/src/main.nr | 4 +- .../brillig_acir_as_brillig/src/main.nr | 8 +- .../brillig_array_to_slice/src/main.nr | 10 +- .../brillig_arrays/src/main.nr | 6 +- .../brillig_assert/src/main.nr | 4 +- .../brillig_blake2s/src/main.nr | 4 +- .../src/main.nr | 4 +- .../brillig_calls/src/main.nr | 10 +- .../brillig_calls_array/src/main.nr | 6 +- .../brillig_calls_conditionals/src/main.nr | 10 +- .../brillig_conditional/src/main.nr | 4 +- .../brillig_ecdsa_secp256k1/src/main.nr | 4 +- .../brillig_ecdsa_secp256r1/src/main.nr | 4 +- .../brillig_fns_as_values/src/main.nr | 20 +- .../brillig_hash_to_field/src/main.nr | 4 +- .../brillig_identity_function/src/main.nr | 22 +- .../brillig_keccak/src/main.nr | 26 +- .../brillig_loop/src/main.nr | 6 +- .../brillig_loop_size_regression/Nargo.toml | 7 + .../brillig_loop_size_regression/Prover.toml | 0 .../brillig_loop_size_regression/src/main.nr | 16 + .../brillig_nested_arrays/src/main.nr | 16 +- .../execution_success/brillig_not/src/main.nr | 6 +- .../brillig_oracle/src/main.nr | 30 +- .../brillig_recursion/src/main.nr | 4 +- .../brillig_sha256/src/main.nr | 4 +- .../brillig_unitialised_arrays/src/main.nr | 6 +- .../execution_success/databus/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 2 +- .../global_consts/src/main.nr | 2 +- .../is_unconstrained/src/main.nr | 4 +- .../nested_arrays_from_brillig/src/main.nr | 4 +- .../regression_4124/src/main.nr | 2 +- .../regression_5435/src/main.nr | 4 +- .../execution_success/slice_regex/src/main.nr | 8 +- .../execution_success/u16_support/src/main.nr | 4 +- .../execution_success/uhashmap/src/main.nr | 8 +- .../execution_success/unit_value/src/main.nr | 2 +- .../verify_honk_proof/Nargo.toml | 6 - .../verify_honk_proof/Prover.toml | 537 ----- .../verify_honk_proof/src/main.nr | 16 - .../test_programs/gates_report_brillig.sh | 33 + .../noir_test_success/mock_oracle/src/main.nr | 112 +- .../out_of_bounds_alignment/src/main.nr | 4 +- .../regression_4561/src/main.nr | 8 +- noir/noir-repo/tooling/acvm_cli/Cargo.toml | 3 + noir/noir-repo/tooling/debugger/Cargo.toml | 3 + .../noir-repo/tooling/debugger/src/context.rs | 15 +- noir/noir-repo/tooling/fuzzer/Cargo.toml | 3 + noir/noir-repo/tooling/lsp/Cargo.toml | 4 + noir/noir-repo/tooling/lsp/src/lib.rs | 11 +- .../tooling/lsp/src/notifications/mod.rs | 8 +- .../tooling/lsp/src/requests/completion.rs | 1792 ++++------------- .../src/requests/completion/auto_import.rs | 210 ++ .../lsp/src/requests/completion/builtins.rs | 64 + .../requests/completion/completion_items.rs | 391 ++++ .../lsp/src/requests/completion/kinds.rs | 42 + .../lsp/src/requests/completion/sort_text.rs | 39 + .../lsp/src/requests/completion/tests.rs | 1641 +++++++++++++++ .../lsp/src/requests/completion/traversal.rs | 132 ++ .../tooling/lsp/src/requests/hover.rs | 56 +- .../tooling/lsp/src/requests/inlay_hint.rs | 16 +- .../noir-repo/tooling/lsp/src/requests/mod.rs | 15 +- .../lsp/src/requests/signature_help.rs | 291 +++ .../lsp/src/requests/signature_help/tests.rs | 196 ++ .../src/requests/signature_help/traversal.rs | 304 +++ noir/noir-repo/tooling/lsp/src/types.rs | 6 +- noir/noir-repo/tooling/nargo/Cargo.toml | 3 + .../tooling/nargo/src/ops/execute.rs | 6 +- noir/noir-repo/tooling/nargo_cli/Cargo.toml | 3 + noir/noir-repo/tooling/nargo_cli/build.rs | 2 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 57 +- noir/noir-repo/tooling/nargo_fmt/Cargo.toml | 3 + .../tooling/nargo_fmt/src/rewrite/expr.rs | 3 + .../tooling/nargo_fmt/src/rewrite/typ.rs | 6 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 2 + .../nargo_fmt/tests/expected/unsafe.nr | 8 + .../tooling/nargo_fmt/tests/input/fn.nr | 2 + .../tooling/nargo_fmt/tests/input/unsafe.nr | 8 + noir/noir-repo/tooling/nargo_toml/Cargo.toml | 3 + .../noir_js_backend_barretenberg/package.json | 2 +- noir/noir-repo/tooling/noirc_abi/Cargo.toml | 3 + .../tooling/noirc_abi/src/arbitrary.rs | 8 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- .../tooling/noirc_abi_wasm/src/lib.rs | 5 +- .../tooling/noirc_artifacts/Cargo.toml | 3 + .../tooling/noirc_artifacts/src/program.rs | 4 + noir/noir-repo/tooling/profiler/Cargo.toml | 3 + .../profiler/src/cli/gates_flamegraph_cmd.rs | 3 +- .../src/cli/opcodes_flamegraph_cmd.rs | 2 + .../tooling/profiler/src/opcode_formatter.rs | 2 +- noir/noir-repo/yarn.lock | 13 +- 250 files changed, 7234 insertions(+), 2813 deletions(-) create mode 100644 noir/noir-repo/.github/workflows/gates_report_brillig.yml create mode 100644 noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs rename noir/noir-repo/noir_stdlib/src/{unsafe.nr => mem.nr} (99%) create mode 100644 noir/noir-repo/noir_stdlib/src/meta/op.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/trait_impl.nr delete mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr rename noir/noir-repo/test_programs/compile_success_empty/{comptime_exp => comptime_trait_impl}/Nargo.toml (74%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_array_set_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_known_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_failure/unused_slice_get_unknown_index_out_of_bounds/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/test_programs/gates_report_brillig.sh create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs create mode 100644 noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/expected/unsafe.nr create mode 100644 noir/noir-repo/tooling/nargo_fmt/tests/input/unsafe.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index d575ffe3e8c..46b3d36c570 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0ebf1fee471641db0bffcc8307d20327613c78c1 +090501dfaf7c569b1aa944856bf68ad663572ae4 diff --git a/noir/noir-repo/.github/workflows/gates_report.yml b/noir/noir-repo/.github/workflows/gates_report.yml index 0cc94a1a04d..0b0a527b69e 100644 --- a/noir/noir-repo/.github/workflows/gates_report.yml +++ b/noir/noir-repo/.github/workflows/gates_report.yml @@ -80,7 +80,7 @@ jobs: - name: Compare gates reports id: gates_diff - uses: vezenovm/noir-gates-diff@acf12797860f237117e15c0d6e08d64253af52b6 + uses: noir-lang/noir-gates-diff@1931aaaa848a1a009363d6115293f7b7fc72bb87 with: report: gates_report.json summaryQuantile: 0.9 # only display the 10% most significant circuit size diffs in the summary (defaults to 20%) diff --git a/noir/noir-repo/.github/workflows/gates_report_brillig.yml b/noir/noir-repo/.github/workflows/gates_report_brillig.yml new file mode 100644 index 00000000000..4e12a6fcbca --- /dev/null +++ b/noir/noir-repo/.github/workflows/gates_report_brillig.yml @@ -0,0 +1,92 @@ +name: Report Brillig bytecode size diff + +on: + push: + branches: + - master + pull_request: + +jobs: + build-nargo: + runs-on: ubuntu-latest + strategy: + matrix: + target: [x86_64-unknown-linux-gnu] + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.74.1 + + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build Nargo + run: cargo build --package nargo_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/nargo ./dist/nargo + 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: nargo + path: ./dist/* + retention-days: 3 + + compare_brillig_bytecode_size_reports: + needs: [build-nargo] + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Download nargo binary + uses: actions/download-artifact@v4 + with: + name: nargo + path: ./nargo + + - name: Set nargo on PATH + run: | + nargo_binary="${{ github.workspace }}/nargo/nargo" + chmod +x $nargo_binary + echo "$(dirname $nargo_binary)" >> $GITHUB_PATH + export PATH="$PATH:$(dirname $nargo_binary)" + nargo -V + + - name: Generate Brillig bytecode size report + working-directory: ./test_programs + run: | + chmod +x gates_report_brillig.sh + ./gates_report_brillig.sh + mv gates_report_brillig.json ../gates_report_brillig.json + + - name: Compare Brillig bytecode size reports + id: brillig_bytecode_diff + uses: noir-lang/noir-gates-diff@3fb844067b25d1b59727ea600b614503b33503f4 + with: + report: gates_report_brillig.json + header: | + # Changes to Brillig bytecode sizes + brillig_report: true + summaryQuantile: 0.9 # only display the 10% most significant bytecode size diffs in the summary (defaults to 20%) + + - name: Add bytecode size diff to sticky comment + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: brillig + # delete the comment in case changes no longer impact brillig bytecode sizes + delete: ${{ !steps.brillig_bytecode_diff.outputs.markdown }} + message: ${{ steps.brillig_bytecode_diff.outputs.markdown }} \ No newline at end of file diff --git a/noir/noir-repo/.gitignore b/noir/noir-repo/.gitignore index 2c877a4d02c..aeb7d8757c4 100644 --- a/noir/noir-repo/.gitignore +++ b/noir/noir-repo/.gitignore @@ -33,6 +33,7 @@ tooling/noir_js/lib !compiler/wasm/noir-script/target gates_report.json +gates_report_brillig.json # Github Actions scratch space # This gives a location to download artifacts into the repository in CI without making git dirty. diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index bacbf939786..e1159c71201 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -2722,6 +2722,7 @@ dependencies = [ "acvm", "async-lsp", "codespan-lsp", + "convert_case 0.6.0", "fm", "fxhash", "lsp-types 0.94.1", diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 63509170057..7d9b3254c53 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -50,6 +50,12 @@ rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" +[workspace.lints.rust] +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" +unused_qualifications = "warn" + [workspace.dependencies] # ACVM workspace dependencies diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 88616ccffb5..860b565544b 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 00d0933a3aa..43984e4a922 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -103,7 +103,7 @@ impl ErrorSelector { impl Serialize for ErrorSelector { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.to_string().serialize(serializer) } @@ -112,7 +112,7 @@ impl Serialize for ErrorSelector { impl<'de> Deserialize<'de> for ErrorSelector { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; let as_u64 = s.parse().map_err(serde::de::Error::custom)?; @@ -224,7 +224,7 @@ impl Circuit { } impl Program { - fn write(&self, writer: W) -> std::io::Result<()> { + fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); encoder.write_all(&buf)?; @@ -250,7 +250,7 @@ impl Program { } impl Deserialize<'a>> Program { - fn read(reader: R) -> std::io::Result { + fn read(reader: R) -> std::io::Result { let mut gz_decoder = flate2::read::GzDecoder::new(reader); let mut buf_d = Vec::new(); gz_decoder.read_to_end(&mut buf_d)?; diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs index b1fdc5e0080..d7f0f5f6f1f 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs @@ -2,6 +2,10 @@ use super::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, directives::Directive, }; + +pub mod function_id; +pub use function_id::AcirFunctionId; + use crate::native_types::{Expression, Witness}; use acir_field::AcirField; use serde::{Deserialize, Serialize}; @@ -125,7 +129,7 @@ pub enum Opcode { Call { /// Id for the function being called. It is the responsibility of the executor /// to fetch the appropriate circuit from this id. - id: u32, + id: AcirFunctionId, /// Inputs to the function call inputs: Vec, /// Outputs of the function call diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs new file mode 100644 index 00000000000..b5abb1b3942 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/function_id.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)] +#[serde(transparent)] +pub struct AcirFunctionId(pub u32); + +impl AcirFunctionId { + pub fn as_usize(&self) -> usize { + self.0 as usize + } +} + +impl std::fmt::Display for AcirFunctionId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 1a634eeea9c..ce28d47021c 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -14,7 +14,7 @@ use std::collections::BTreeSet; use acir::{ circuit::{ brillig::{BrilligBytecode, BrilligFunctionId, BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, + opcodes::{AcirFunctionId, BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, @@ -381,13 +381,13 @@ fn nested_acir_call_circuit() { // x // } let nested_call = Opcode::Call { - id: 1, + id: AcirFunctionId(1), inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(2)], predicate: None, }; let nested_call_two = Opcode::Call { - id: 1, + id: AcirFunctionId(1), inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(3)], predicate: None, @@ -419,7 +419,7 @@ fn nested_acir_call_circuit() { q_c: FieldElement::one() + FieldElement::one(), }); let call = Opcode::Call { - id: 2, + id: AcirFunctionId(2), inputs: vec![Witness(2), Witness(1)], outputs: vec![Witness(3)], predicate: None, diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index a037a453348..c1cffc1334e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 92ccbc4e5f6..2323f008dbe 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -115,7 +115,7 @@ impl From for FieldElement { } } -impl Serialize for FieldElement { +impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -124,7 +124,7 @@ impl Serialize for FieldElement { } } -impl<'de, T: ark_ff::PrimeField> Deserialize<'de> for FieldElement { +impl<'de, T: PrimeField> Deserialize<'de> for FieldElement { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs index f7228e66314..74927c07a36 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs @@ -2,7 +2,7 @@ use num_bigint::BigUint; /// This trait is extremely unstable and WILL have breaking changes. pub trait AcirField: - std::marker::Sized + Sized + std::fmt::Display + std::fmt::Debug + Default @@ -24,7 +24,7 @@ pub trait AcirField: // + From + From + std::hash::Hash - + std::cmp::Eq + + Eq { fn one() -> Self; fn zero() -> Self; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 2ee4d529e5a..bf1170ce073 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs index 83c5aeb6296..647c11bd3c3 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/mod.rs @@ -6,7 +6,7 @@ use acir::{ brillig::ForeignCallResult, circuit::{ brillig::{BrilligBytecode, BrilligFunctionId}, - opcodes::{BlockId, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{AcirFunctionId, BlockId, ConstantOrWitnessEnum, FunctionInput}, AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, STRING_ERROR_SELECTOR, }, @@ -575,7 +575,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { else { unreachable!("Not executing a Call opcode"); }; - if *id == 0 { + if *id == AcirFunctionId(0) { return Err(OpcodeResolutionError::AcirMainCallAttempted { opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir( self.instruction_pointer(), @@ -716,7 +716,7 @@ pub(crate) fn is_predicate_false( #[derive(Debug, Clone, PartialEq)] pub struct AcirCallWaitInfo { /// Index in the list of ACIR function's that should be called - pub id: u32, + pub id: AcirFunctionId, /// Initial witness for the given circuit to be called pub initial_witness: WitnessMap, } diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 0d90b9ba54f..6b457a8f5f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] 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/acvm-repo/acvm_js/src/execute.rs b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs index c596dcf9614..98a0c4c3abe 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/execute.rs @@ -250,7 +250,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { acvm.resolve_pending_foreign_call(result); } ACVMStatus::RequiresAcirCall(call_info) => { - let acir_to_call = &self.functions[call_info.id as usize]; + let acir_to_call = &self.functions[call_info.id.as_usize()]; let initial_witness = call_info.initial_witness; let call_solved_witness = self .execute_circuit(acir_to_call, initial_witness, witness_stack) @@ -267,7 +267,7 @@ impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { } } acvm.resolve_pending_acir_call(call_resolved_outputs); - witness_stack.push(call_info.id, call_solved_witness.clone()); + witness_stack.push(call_info.id.0, call_solved_witness.clone()); } } } diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index 23da88247fc..f6a00af7942 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -54,16 +54,14 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; let assertion_payload = match assertion_payload { - Some(raw) => ::from_serde(&raw) + Some(raw) => ::from_serde(&raw) .expect("Cannot serialize assertion payload"), None => JsValue::UNDEFINED, }; let brillig_function_id = match brillig_function_id { - Some(function_id) => { - ::from_serde(&function_id) - .expect("Cannot serialize Brillig function id") - } + Some(function_id) => ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id"), None => JsValue::UNDEFINED, }; diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 10b491c7f67..d0473857e1c 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ab677396c22..9a23f503a06 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index bb51426b33b..a4125014d56 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -91,7 +91,7 @@ mod test { fn test_derive_generators() { let res = derive_generators("test domain".as_bytes(), 128, 0); - let is_unique = |y: Affine, j: usize| -> bool { + let is_unique = |y: Affine, j: usize| -> bool { for (i, res) in res.iter().enumerate() { if i != j && *res == y { return false; diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index 631acbd55d8..9f5a5ce0ee2 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ebd35f1579f..cd9f754ebc0 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed..c9d88e36e28 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index b48f445be36..c367333a6f4 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml index b94f997b7b9..68fc4d1c920 100644 --- a/noir/noir-repo/compiler/noirc_arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -5,3 +5,6 @@ authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a9949f5093a..b244018cc71 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] 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_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 72c95823553..467bda2ca88 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -274,11 +274,8 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = if options.disable_macros { - &[] - } else { - &[&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs( @@ -557,6 +554,7 @@ pub fn compile_no_check( let force_compile = force_compile || options.print_acir || options.show_brillig + || options.force_brillig || options.show_ssa || options.emit_ssa; @@ -578,7 +576,7 @@ pub fn compile_no_check( emit_ssa: if options.emit_ssa { Some(context.package_build_path.clone()) } else { None }, }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + let SsaProgramArtifact { program, debug, warnings, names, brillig_names, error_types, .. } = create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); @@ -593,5 +591,6 @@ pub fn compile_no_check( noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, names, + brillig_names, }) } diff --git a/noir/noir-repo/compiler/noirc_driver/src/program.rs b/noir/noir-repo/compiler/noirc_driver/src/program.rs index 8e02de0b8b3..88460482928 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/program.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/program.rs @@ -29,4 +29,6 @@ pub struct CompiledProgram { pub warnings: Vec, /// Names of the functions in the program. These are used for more informative debugging and benchmarking. pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index 41b1cd0ff58..61b274c605f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 02b242e8b4d..1792197eab7 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -16,7 +16,7 @@ pub struct Spanned { /// This is important for tests. Two Spanned objects are equal if their content is equal /// They may not have the same span. Use into_span to test for Span being equal specifically -impl PartialEq> for Spanned { +impl PartialEq> for Spanned { fn eq(&self, other: &Spanned) -> bool { self.contents == other.contents } diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 66c770b5064..81feb0b7154 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..d6364cefc9a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -27,5 +27,7 @@ pub(crate) fn convert_ssa_function( BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); } - brillig_context.artifact() + let mut artifact = brillig_context.artifact(); + artifact.name = func.name().to_string(); + artifact } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index dff4da56c1e..fca1f60544d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -55,6 +55,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_invert".to_string(), } } @@ -109,5 +110,6 @@ pub(crate) fn directive_quotient() -> GeneratedBrillig { ], assert_messages: Default::default(), locations: Default::default(), + name: "directive_integer_quotient".to_string(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..53964f64170 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -22,6 +22,7 @@ pub(crate) struct GeneratedBrillig { pub(crate) byte_code: Vec>, pub(crate) locations: BTreeMap, pub(crate) assert_messages: BTreeMap, + pub(crate) name: String, } #[derive(Default, Debug, Clone)] @@ -49,6 +50,8 @@ pub(crate) struct BrilligArtifact { locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. call_stack: CallStack, + /// Name of the function, only used for debugging purposes. + pub(crate) name: String, } /// A pointer to a location in the opcode. @@ -81,6 +84,7 @@ impl BrilligArtifact { byte_code: self.byte_code, locations: self.locations, assert_messages: self.assert_messages, + name: self.name, } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index b632958f37f..9daf98e606b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -151,6 +151,7 @@ pub struct SsaProgramArtifact { pub main_input_witnesses: Vec, pub main_return_witnesses: Vec, pub names: Vec, + pub brillig_names: Vec, pub error_types: BTreeMap, } @@ -167,6 +168,7 @@ impl SsaProgramArtifact { main_input_witnesses: Vec::default(), main_return_witnesses: Vec::default(), names: Vec::default(), + brillig_names: Vec::default(), error_types, } } @@ -202,8 +204,10 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = - optimize_into_acir(program, options)?; + let ArtifactsAndWarnings( + (generated_acirs, generated_brillig, brillig_function_names, error_types), + ssa_level_warnings, + ) = optimize_into_acir(program, options)?; if options.force_brillig_output { assert_eq!( generated_acirs.len(), @@ -236,6 +240,7 @@ pub fn create_program( program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } + program_artifact.brillig_names = brillig_function_names; Ok(program_artifact) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index b2a73106468..a8324c11592 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -8,7 +8,7 @@ use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; use acvm::acir::circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}; -use acvm::acir::circuit::opcodes::{BlockId, BlockType, MemOp}; +use acvm::acir::circuit::opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}; use acvm::acir::circuit::{AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode}; use acvm::blackbox_solver; use acvm::brillig_vm::{MemoryValue, VMStatus, VM}; @@ -1959,7 +1959,7 @@ impl AcirContext { pub(crate) fn call_acir_function( &mut self, - id: u32, + id: AcirFunctionId, inputs: Vec, output_count: usize, predicate: AcirVar, @@ -2005,7 +2005,7 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 22b13aeae79..3e36459a0d6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -29,7 +29,7 @@ use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; pub(crate) use acir_ir::generated_acir::GeneratedAcir; -use acvm::acir::circuit::opcodes::BlockType; +use acvm::acir::circuit::opcodes::{AcirFunctionId, BlockType}; use noirc_frontend::monomorphization::ast::InlineType; use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId}; @@ -235,7 +235,7 @@ impl Debug for AcirDynamicArray { #[derive(Debug, Clone)] pub(crate) enum AcirValue { Var(AcirVar, AcirType), - Array(im::Vector), + Array(Vector), DynamicArray(AcirDynamicArray), } @@ -282,6 +282,7 @@ impl AcirValue { pub(crate) type Artifacts = ( Vec>, Vec>, + Vec, BTreeMap, ); @@ -334,11 +335,13 @@ impl Ssa { } } - let brillig = vecmap(shared_context.generated_brillig, |brillig| BrilligBytecode { - bytecode: brillig.byte_code, - }); + let (brillig_bytecode, brillig_names) = shared_context + .generated_brillig + .into_iter() + .map(|brillig| (BrilligBytecode { bytecode: brillig.byte_code }, brillig.name)) + .unzip(); - Ok((acirs, brillig, self.error_selector_to_type)) + Ok((acirs, brillig_bytecode, brillig_names, self.error_selector_to_type)) } } @@ -772,7 +775,7 @@ impl<'a> Context<'a> { .get(id) .expect("ICE: should have an associated final index"); let output_vars = self.acir_context.call_acir_function( - *acir_function_id, + AcirFunctionId(*acir_function_id), inputs, output_count, self.current_side_effects_enabled_var, @@ -955,6 +958,8 @@ impl<'a> Context<'a> { BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); + entry_point.name = func.name().to_string(); + // Link the entry point with all dependencies while let Some(unresolved_fn_label) = entry_point.first_unresolved_function_call() { let artifact = &brillig.find_by_function_label(unresolved_fn_label.clone()); @@ -1645,7 +1650,7 @@ impl<'a> Context<'a> { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - let array: im::Vector = init_values.into(); + let array: Vector = init_values.into(); self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } @@ -2862,9 +2867,13 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { #[cfg(test)] mod test { + use acvm::{ acir::{ - circuit::{brillig::BrilligFunctionId, ExpressionWidth, Opcode, OpcodeLocation}, + circuit::{ + brillig::BrilligFunctionId, opcodes::AcirFunctionId, ExpressionWidth, Opcode, + OpcodeLocation, + }, native_types::Witness, }, FieldElement, @@ -2966,7 +2975,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: @@ -3015,8 +3024,18 @@ mod test { let main_opcodes = main_acir.opcodes(); assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo`"); - check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); - check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + check_call_opcode( + &main_opcodes[0], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(2)], + ); + check_call_opcode( + &main_opcodes[1], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(3)], + ); if let Opcode::AssertZero(expr) = &main_opcodes[2] { assert_eq!(expr.linear_combinations[0].0, FieldElement::from(1u128)); @@ -3061,7 +3080,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` @@ -3071,9 +3090,19 @@ mod test { let main_opcodes = main_acir.opcodes(); assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); - check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + check_call_opcode( + &main_opcodes[0], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(2)], + ); // The output of the first call should be the input of the second call - check_call_opcode(&main_opcodes[1], 1, vec![Witness(2), Witness(1)], vec![Witness(3)]); + check_call_opcode( + &main_opcodes[1], + AcirFunctionId(1), + vec![Witness(2), Witness(1)], + vec![Witness(3)], + ); } fn basic_nested_call(inline_type: InlineType) { @@ -3151,7 +3180,7 @@ mod test { let ssa = builder.finish(); - let (acir_functions, _, _) = ssa + let (acir_functions, _, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3162,9 +3191,19 @@ mod test { assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); // Both of these should call func_with_nested_foo_call f1 - check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + check_call_opcode( + &main_opcodes[0], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(2)], + ); // The output of the first call should be the input of the second call - check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + check_call_opcode( + &main_opcodes[1], + AcirFunctionId(1), + vec![Witness(0), Witness(1)], + vec![Witness(3)], + ); let func_with_nested_call_acir = &acir_functions[1]; let func_with_nested_call_opcodes = func_with_nested_call_acir.opcodes(); @@ -3177,7 +3216,7 @@ mod test { // Should call foo f2 check_call_opcode( &func_with_nested_call_opcodes[1], - 2, + AcirFunctionId(2), vec![Witness(3), Witness(1)], vec![Witness(4)], ); @@ -3185,7 +3224,7 @@ mod test { fn check_call_opcode( opcode: &Opcode, - expected_id: u32, + expected_id: AcirFunctionId, expected_inputs: Vec, expected_outputs: Vec, ) { @@ -3265,7 +3304,7 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3329,7 +3368,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3403,7 +3442,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); @@ -3491,7 +3530,7 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = ssa + let (acir_functions, brillig_functions, _, _) = ssa .into_acir(&brillig, ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 34d7d595eb9..f06f46d7af8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -171,13 +171,14 @@ impl DataFlowGraph { ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - use InsertInstructionResult::*; match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { - SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification), + SimplifyResult::SimplifiedTo(simplification) => { + InsertInstructionResult::SimplifiedTo(simplification) + } SimplifyResult::SimplifiedToMultiple(simplification) => { - SimplifiedToMultiple(simplification) + InsertInstructionResult::SimplifiedToMultiple(simplification) } - SimplifyResult::Remove => InstructionRemoved, + SimplifyResult::Remove => InsertInstructionResult::InstructionRemoved, result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 7dcb50762f5..1b3466c76fa 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -608,16 +608,11 @@ impl Instruction { } } Instruction::ArrayGet { array, index } => { - let array = dfg.get_array_constant(*array); - let index = dfg.get_numeric_constant(*index); - if let (Some((array, _)), Some(index)) = (array, index) { - let index = - index.try_to_u32().expect("Expected array index to fit in u32") as usize; - if index < array.len() { - return SimplifiedTo(array[index]); - } + if let Some(index) = dfg.get_numeric_constant(*index) { + try_optimize_array_get_from_previous_set(dfg, *array, index) + } else { + None } - None } Instruction::ArraySet { array, index, value, .. } => { let array = dfg.get_array_constant(*array); @@ -744,6 +739,65 @@ impl Instruction { } } +/// Given a chain of operations like: +/// v1 = array_set [10, 11, 12], index 1, value: 5 +/// v2 = array_set v1, index 2, value: 6 +/// v3 = array_set v2, index 2, value: 7 +/// v4 = array_get v3, index 1 +/// +/// We want to optimize `v4` to `10`. To do this we need to follow the array value +/// through several array sets. For each array set: +/// - If the index is non-constant we fail the optimization since any index may be changed +/// - If the index is constant and is our target index, we conservatively fail the optimization +/// in case the array_set is disabled from a previous `enable_side_effects_if` and the array get +/// was not. +/// - Otherwise, we check the array value of the array set. +/// - If the array value is constant, we use that array. +/// - If the array value is from a previous array-set, we recur. +fn try_optimize_array_get_from_previous_set( + dfg: &DataFlowGraph, + mut array_id: Id, + target_index: FieldElement, +) -> SimplifyResult { + let mut elements = None; + + // Arbitrary number of maximum tries just to prevent this optimization from taking too long. + let max_tries = 5; + for _ in 0..max_tries { + match &dfg[array_id] { + Value::Instruction { instruction, .. } => { + match &dfg[*instruction] { + Instruction::ArraySet { array, index, value, .. } => { + if let Some(constant) = dfg.get_numeric_constant(*index) { + if constant == target_index { + return SimplifyResult::SimplifiedTo(*value); + } + + array_id = *array; // recur + } else { + return SimplifyResult::None; + } + } + _ => return SimplifyResult::None, + } + } + Value::Array { array, typ: _ } => { + elements = Some(array.clone()); + break; + } + _ => return SimplifyResult::None, + } + } + + if let (Some(array), Some(index)) = (elements, target_index.try_to_u64()) { + let index = index as usize; + if index < array.len() { + return SimplifyResult::SimplifiedTo(array[index]); + } + } + SimplifyResult::None +} + pub(crate) type ErrorType = HirType; pub(crate) fn error_selector_from_type(typ: &ErrorType) -> ErrorSelector { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1c9a31f0c99..f1265553b83 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -50,7 +50,7 @@ impl Id { // Need to manually implement most impls on Id. // Otherwise rust assumes that Id: Hash only if T: Hash, // which isn't true since the T is not used internally. -impl std::hash::Hash for Id { +impl Hash for Id { fn hash(&self, state: &mut H) { self.index.hash(state); } 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/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 5b1139e5b9c..e5a25dcfef1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -66,6 +66,8 @@ mod block; use std::collections::{BTreeMap, BTreeSet}; +use fxhash::FxHashMap as HashMap; + use crate::ssa::{ ir::{ basic_block::BasicBlockId, @@ -111,6 +113,10 @@ struct PerFunctionContext<'f> { /// We avoid removing individual instructions as we go since removing elements /// from the middle of Vecs many times will be slower than a single call to `retain`. instructions_to_remove: BTreeSet, + + /// Track a value's last load across all blocks. + /// If a value is not used in anymore loads we can remove the last store to that value. + last_loads: HashMap, } impl<'f> PerFunctionContext<'f> { @@ -124,6 +130,7 @@ impl<'f> PerFunctionContext<'f> { inserter: FunctionInserter::new(function), blocks: BTreeMap::new(), instructions_to_remove: BTreeSet::new(), + last_loads: HashMap::default(), } } @@ -140,6 +147,18 @@ impl<'f> PerFunctionContext<'f> { let references = self.find_starting_references(block); self.analyze_block(block, references); } + + // If we never load from an address within a function we can remove all stores to that address. + // This rule does not apply to reference parameters, which we must also check for before removing these stores. + for (block_id, block) in self.blocks.iter() { + let block_params = self.inserter.function.dfg.block_parameters(*block_id); + for (value, store_instruction) in block.last_stores.iter() { + let is_reference_param = block_params.contains(value); + if self.last_loads.get(value).is_none() && !is_reference_param { + self.instructions_to_remove.insert(*store_instruction); + } + } + } } /// The value of each reference at the start of the given block is the unification @@ -239,6 +258,8 @@ impl<'f> PerFunctionContext<'f> { self.instructions_to_remove.insert(instruction); } else { references.mark_value_used(address, self.inserter.function); + + self.last_loads.insert(address, instruction); } } Instruction::Store { address, value } => { @@ -594,10 +615,8 @@ mod tests { // acir fn main f0 { // b0(): // v7 = allocate - // store Field 5 at v7 // jmp b1(Field 5) // b1(v3: Field): - // store Field 6 at v7 // return v3, Field 5, Field 6 // } let ssa = ssa.mem2reg(); @@ -609,9 +628,9 @@ mod tests { assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); assert_eq!(count_loads(b1, &main.dfg), 0); - // Neither store is removed since they are each the last in the block and there are multiple blocks - assert_eq!(count_stores(main.entry_block(), &main.dfg), 1); - assert_eq!(count_stores(b1, &main.dfg), 1); + // All stores are removed as there are no loads to the values being stored anywhere in the function. + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); // The jmp to b1 should also be a constant 5 now match main.dfg[main.entry_block()].terminator() { @@ -641,8 +660,8 @@ mod tests { // b1(): // store Field 1 at v3 // store Field 2 at v4 - // v8 = load v3 - // v9 = eq v8, Field 2 + // v7 = load v3 + // v8 = eq v7, Field 2 // return // } let main_id = Id::test_new(0); @@ -681,12 +700,9 @@ mod tests { // acir fn main f0 { // b0(): // v9 = allocate - // store Field 0 at v9 // v10 = allocate - // store v9 at v10 // jmp b1() // b1(): - // store Field 2 at v9 // return // } let ssa = ssa.mem2reg(); @@ -698,14 +714,17 @@ mod tests { assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); assert_eq!(count_loads(b1, &main.dfg), 0); - // Only the first store in b1 is removed since there is another store to the same reference + // All stores should be removed. + // The first store in b1 is removed since there is another store to the same reference // in the same block, and the store is not needed before the later store. - assert_eq!(count_stores(main.entry_block(), &main.dfg), 2); - assert_eq!(count_stores(b1, &main.dfg), 1); + // The rest of the stores are also removed as no loads are done within any blocks + // to the stored values. + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); let b1_instructions = main.dfg[b1].instructions(); // We expect the last eq to be optimized out - assert_eq!(b1_instructions.len(), 1); + assert_eq!(b1_instructions.len(), 0); } } 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..13e5c2445ad 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 @@ -85,7 +85,7 @@ pub(super) struct Loop { } /// The queue of functions remaining to compile -type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; +type FunctionQueue = Vec<(FuncId, IrFunctionId)>; impl<'a> FunctionContext<'a> { /// Create a new FunctionContext to compile the first function in the shared_context's @@ -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 @@ -1005,14 +1005,14 @@ impl SharedContext { } /// Pops the next function from the shared function queue, returning None if the queue is empty. - pub(super) fn pop_next_function_in_queue(&self) -> Option<(ast::FuncId, IrFunctionId)> { + pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() } /// Return the matching id for the given function if known. If it is not known this /// will add the function to the queue of functions to compile, assign it a new id, /// and return this new id. - pub(super) fn get_or_queue_function(&self, id: ast::FuncId) -> IrFunctionId { + pub(super) fn get_or_queue_function(&self, id: FuncId) -> IrFunctionId { // Start a new block to guarantee the destructor for the map lock is released // before map needs to be acquired again in self.functions.write() below { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1373d1bc46e..6b19aff2674 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -396,11 +396,11 @@ impl<'a> FunctionContext<'a> { /// return a reference to each element, for use with the store instruction. fn codegen_array_index( &mut self, - array: super::ir::value::ValueId, - index: super::ir::value::ValueId, + array: ValueId, + index: ValueId, element_type: &ast::Type, location: Location, - length: Option, + length: Option, ) -> Result { // base_index = index * type_size let index = self.make_array_index(index); @@ -438,11 +438,7 @@ impl<'a> FunctionContext<'a> { /// Prepare a slice access. /// Check that the index being used to access a slice element /// is less than the dynamic slice length. - fn codegen_slice_access_check( - &mut self, - index: super::ir::value::ValueId, - length: Option, - ) { + fn codegen_slice_access_check(&mut self, index: ValueId, length: Option) { let index = self.make_array_index(index); // We convert the length as an array index type for comparison let array_len = self 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..1fd1e313c44 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, QuotedTypeId}; -use crate::token::{Attributes, Token, Tokens}; +use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -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 @@ -477,6 +478,20 @@ pub struct FunctionDefinition { pub return_visibility: Visibility, } +impl FunctionDefinition { + pub fn is_private(&self) -> bool { + self.visibility == ItemVisibility::Private + } + + pub fn is_test(&self) -> bool { + if let Some(attribute) = &self.attributes.function { + matches!(attribute, FunctionAttribute::Test(..)) + } else { + false + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Param { pub visibility: Visibility, @@ -587,6 +602,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/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index afa2e7fa7a8..2b78c02e53c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -267,6 +267,14 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_empty_fn(); let module = self.module_id(); self.interner.push_function(id, &function.def, module, location); + + if self.interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + self.interner.register_function(id, &function.def); + } + let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { file_id: self.file, 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..65e94c4fcf4 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; @@ -489,7 +506,7 @@ impl<'context> Elaborator<'context> { unseen_fields.remove(&field_name); seen_fields.insert(field_name.clone()); - self.unify_with_coercions(&field_type, expected_type, resolved, || { + self.unify_with_coercions(&field_type, expected_type, resolved, field_span, || { TypeCheckError::TypeMismatch { expected_typ: expected_type.to_string(), expr_typ: field_type.to_string(), @@ -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..2e4a69c067f 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)); @@ -1290,6 +1297,12 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; + let name = if self.interner.is_in_lsp_mode() { + Some(let_stmt.pattern.name_ident().to_string()) + } else { + None + }; + if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { @@ -1312,8 +1325,9 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner - .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); + if let Some(name) = name { + self.interner.register_global(global_id, name, self.module_id()); + } self.local_module = old_module; self.file = old_file; @@ -1451,9 +1465,12 @@ impl<'context> Elaborator<'context> { /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { - self.current_item.map_or(true, |id| match id { - DependencyId::Function(id) => !self.interner.function_modifiers(&id).is_unconstrained, - _ => true, - }) + !self.in_comptime_context() + && self.current_item.map_or(true, |id| match id { + DependencyId::Function(id) => { + !self.interner.function_modifiers(&id).is_unconstrained + } + _ => true, + }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 48380383eb0..da4492eb211 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -78,7 +78,7 @@ impl<'context> Elaborator<'context> { let r#type = if annotated_type != Type::Error { // Now check if LHS is the same type as the RHS // Importantly, we do not coerce any types implicitly - self.unify_with_coercions(&expr_type, &annotated_type, expression, || { + self.unify_with_coercions(&expr_type, &annotated_type, expression, expr_span, || { TypeCheckError::TypeMismatch { expected_typ: annotated_type.to_string(), expr_typ: expr_type.to_string(), @@ -136,7 +136,7 @@ impl<'context> Elaborator<'context> { self.push_err(TypeCheckError::VariableMustBeMutable { name, span }); } - self.unify_with_coercions(&expr_type, &lvalue_type, expression, || { + self.unify_with_coercions(&expr_type, &lvalue_type, expression, span, || { TypeCheckError::TypeMismatchWithSource { actual: expr_type.clone(), expected: lvalue_type.clone(), 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..80c5aad7fd2 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 { @@ -641,10 +641,18 @@ impl<'context> Elaborator<'context> { actual: &Type, expected: &Type, expression: ExprId, + span: Span, make_error: impl FnOnce() -> TypeCheckError, ) { let mut errors = Vec::new(); - actual.unify_with_coercions(expected, expression, self.interner, &mut errors, make_error); + actual.unify_with_coercions( + expected, + expression, + span, + self.interner, + &mut errors, + make_error, + ); self.errors.extend(errors.into_iter().map(|error| (error.into(), self.file))); } @@ -735,11 +743,13 @@ 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 { - expected_typ: param.to_string(), - expr_typ: arg.to_string(), - expr_span: *arg_span, + for (param, (arg, arg_expr_id, arg_span)) in fn_params.iter().zip(callsite_args) { + self.unify_with_coercions(arg, param, *arg_expr_id, *arg_span, || { + TypeCheckError::TypeMismatch { + expected_typ: param.to_string(), + expr_typ: arg.to_string(), + expr_span: *arg_span, + } }); } @@ -763,7 +773,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 +783,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 +1139,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 +1325,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 +1395,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}"), @@ -1437,7 +1459,7 @@ impl<'context> Elaborator<'context> { }); } } else { - self.unify_with_coercions(&body_type, declared_return_type, body_id, || { + self.unify_with_coercions(&body_type, declared_return_type, body_id, func_span, || { let mut error = TypeCheckError::TypeMismatchWithSource { expected: declared_return_type.clone(), actual: body_type.clone(), @@ -1577,7 +1599,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..33f8c9d8332 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. @@ -896,6 +897,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs == rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "=="), }, BinaryOpKind::NotEqual => match (lhs, rhs) { @@ -908,6 +910,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { (Value::U16(lhs), Value::U16(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs != rhs)), (lhs, rhs) => make_error(self, lhs, rhs, "!="), }, BinaryOpKind::Less => match (lhs, rhs) { 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..f65c5ae8cdf 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}; @@ -17,13 +17,13 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, UnresolvedType, - UnresolvedTypeData, Visibility, + ArrayLiteral, ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, Literal, + UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }, 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, @@ -47,7 +47,23 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_array" => expr_as_array(arguments, return_type, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), + "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), + "expr_as_if" => expr_as_if(arguments, return_type, location), + "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_integer" => expr_as_integer(arguments, return_type, location), + "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), + "expr_as_repeated_element_array" => { + expr_as_repeated_element_array(arguments, return_type, location) + } + "expr_as_repeated_element_slice" => { + expr_as_repeated_element_slice(arguments, return_type, location) + } + "expr_as_slice" => expr_as_slice(arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), @@ -86,6 +102,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 +113,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 +530,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 +650,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 { @@ -688,6 +766,71 @@ fn zeroed(return_type: Type) -> IResult { } } +// fn as_array(self) -> Option<[Expr]> +fn expr_as_array( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Standard(exprs))) = expr { + let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(exprs, typ)) + } else { + None + } + }) +} + +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + +// fn as_bool(self) -> Option +fn expr_as_bool( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Bool(bool)) = expr { + Some(Value::Bool(bool)) + } else { + None + } + }) +} + // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( arguments: Vec<(Value, Location)>, @@ -708,6 +851,194 @@ fn expr_as_function_call( }) } +// fn as_if(self) -> Option<(Expr, Expr, Option)> +fn expr_as_if( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::If(if_expr) = expr { + // Get the type of `Option` + let option_type = extract_option_generic_type(return_type.clone()); + let Type::Tuple(option_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(option_types.len(), 3); + let alternative_option_type = option_types[2].clone(); + + let alternative = + option(alternative_option_type, if_expr.alternative.map(|e| Value::Expr(e.kind))); + + Some(Value::Tuple(vec![ + Value::Expr(if_expr.condition.kind), + Value::Expr(if_expr.consequence.kind), + alternative.ok()?, + ])) + } else { + None + } + }) +} + +// fn as_index(self) -> Option +fn expr_as_index( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Index(index_expr) = expr { + Some(Value::Tuple(vec![ + Value::Expr(index_expr.collection.kind), + Value::Expr(index_expr.index.kind), + ])) + } else { + None + } + }) +} + +// fn as_integer(self) -> Option<(Field, bool)> +fn expr_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + }) +} + +// fn as_member_access(self) -> Option<(Expr, Quoted)> +fn expr_as_member_access( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::MemberAccess(member_access) = expr { + let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); + Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + } else { + None + } + }) +} + +// fn as_repeated_element_array(self) -> Option<(Expr, Expr)> +fn expr_as_repeated_element_array( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Array(ArrayLiteral::Repeated { + repeated_element, + length, + })) = expr + { + Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)])) + } else { + None + } + }) +} + +// fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> +fn expr_as_repeated_element_slice( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated { + repeated_element, + length, + })) = expr + { + Some(Value::Tuple(vec![Value::Expr(repeated_element.kind), Value::Expr(length.kind)])) + } else { + None + } + }) +} + +// fn as_slice(self) -> Option<[Expr]> +fn expr_as_slice( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(exprs))) = expr { + let exprs = exprs.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(exprs, typ)) + } else { + None + } + }) +} + +// fn as_tuple(self) -> Option<[Expr]> +fn expr_as_tuple( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::Tuple(expressions) = expr { + let expressions = expressions.into_iter().map(|expr| Value::Expr(expr.kind)).collect(); + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Expr))); + Some(Value::Slice(expressions, typ)) + } else { + None + } + }) +} + +// fn as_unary_op(self) -> Option<(UnaryOp, Expr)> +fn expr_as_unary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Prefix(prefix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + tuple_types.pop().unwrap(); + let unary_op_type = tuple_types.pop().unwrap(); + + // These values should match the values used in noir_stdlib/src/meta/op.nr + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, + }; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(unary_op_value.into())); + + let unary_op = Value::Struct(fields, unary_op_type); + let rhs = Value::Expr(prefix_expr.rhs.kind); + Some(Value::Tuple(vec![unary_op, rhs])) + } else { + None + } + }) +} + // Helper function for implementing the `expr_as_...` functions. fn expr_as( arguments: Vec<(Value, Location)>, @@ -719,8 +1050,12 @@ where F: FnOnce(ExpressionKind) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let expr = get_expr(self_argument)?; - let option_value = f(expr); + let mut expression_kind = get_expr(self_argument)?; + while let ExpressionKind::Parenthesized(expression) = expression_kind { + expression_kind = expression.kind; + } + + let option_value = f(expression_kind); option(return_type, option_value) } 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/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index be2afd13507..6436c2562bc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::ModuleAttributes; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -232,6 +232,13 @@ impl<'a> ModCollector<'a> { let location = Location::new(function.span(), self.file_id); context.def_interner.push_function(func_id, &function.def, module, location); + if context.def_interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + context.def_interner.register_function(func_id, &function.def); + } + // Now link this func_id to a crate level map with the noir function and the module id // Encountering a NoirFunction, we retrieve it's module_data to get the namespace // Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner @@ -307,8 +314,8 @@ impl<'a> ModCollector<'a> { }; // Add the struct to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_struct(name, id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_struct(name.clone(), id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -322,10 +329,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Struct(id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_struct(id, name.to_string(), parent_module_id); + } } definition_errors } @@ -383,7 +390,7 @@ impl<'a> ModCollector<'a> { // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_type_alias(name, type_alias_id); + .declare_type_alias(name.clone(), type_alias_id); if let Err((first_def, second_def)) = result { let err = DefCollectorErrorKind::Duplicate { @@ -396,10 +403,11 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Alias(type_alias_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + let name = name.to_string(); + context.def_interner.register_type_alias(type_alias_id, name, parent_module_id); + } } errors } @@ -432,8 +440,8 @@ impl<'a> ModCollector<'a> { }; // Add the trait to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, trait_id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_trait(name.clone(), trait_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -567,10 +575,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location( - ReferenceId::Trait(trait_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_trait(trait_id, name.to_string(), parent_module_id); + } self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -766,6 +774,10 @@ impl<'a> ModCollector<'a> { parent: self.module_id, }, ); + + if context.def_interner.is_in_lsp_mode() { + context.def_interner.register_module(mod_id, mod_name.0.contents.clone()); + } } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs index 54d092f9515..a487bda81b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs @@ -3,7 +3,7 @@ use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; use super::ModuleId; /// A generic ID that references either a module, function, type, interface or global -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), 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..d51760f9d20 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,10 @@ 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("Converting an unconstrained fn to a non-unconstrained fn is unsafe")] + UnsafeFn { 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 +360,12 @@ 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) + } + TypeCheckError::UnsafeFn { 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..d6d114c7075 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, } @@ -199,6 +205,12 @@ impl ResolvedGeneric { } } +enum FunctionCoercionResult { + NoCoercion, + Coerced(Type), + UnconstrainedMismatch(Type), +} + impl std::hash::Hash for StructType { fn hash(&self, state: &mut H) { self.id.hash(state); @@ -662,15 +674,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 +743,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 +870,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 +950,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 +1007,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) | Type::Quoted(_) @@ -1039,7 +1056,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 +1094,7 @@ impl Type { | Type::Slice(_) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::FmtString(_, _) | Type::InfixExpr(..) | Type::Error => true, @@ -1208,7 +1225,7 @@ impl Type { | Type::TypeVariable(_, _) | Type::TraitAsType(..) | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) + | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) @@ -1576,8 +1593,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)?; } @@ -1762,18 +1782,57 @@ impl Type { &self, expected: &Type, expression: ExprId, + span: Span, interner: &mut NodeInterner, errors: &mut Vec, make_error: impl FnOnce() -> TypeCheckError, ) { 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()); + 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` + match self.try_fn_to_unconstrained_fn_coercion(expected) { + FunctionCoercionResult::NoCoercion => errors.push(make_error()), + FunctionCoercionResult::Coerced(coerced_self) => { + coerced_self + .unify_with_coercions(expected, expression, span, interner, errors, make_error); + } + FunctionCoercionResult::UnconstrainedMismatch(coerced_self) => { + errors.push(TypeCheckError::UnsafeFn { span }); + + coerced_self + .unify_with_coercions(expected, expression, span, interner, errors, 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) -> FunctionCoercionResult { + // 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()) + { + let coerced_type = Type::Function(params, ret, env, unconstrained_expected); + + match (unconstrained_self, unconstrained_expected) { + (true, true) | (false, false) => FunctionCoercionResult::NoCoercion, + (false, true) => FunctionCoercionResult::Coerced(coerced_type), + (true, false) => FunctionCoercionResult::UnconstrainedMismatch(coerced_type), } } else { - Type::apply_type_bindings(bindings); + FunctionCoercionResult::NoCoercion } } @@ -1789,13 +1848,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 +2133,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 +2195,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 +2249,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 +2345,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 +2374,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 +2397,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 +2490,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 +2579,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/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index c437676b605..0ac13a58ecf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,9 +1,10 @@ use fm::FileId; use noirc_errors::Location; use rangemap::RangeMap; -use rustc_hash::FxHashMap; +use rustc_hash::FxHashMap as HashMap; use crate::{ + ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, macros_api::{NodeInterner, StructId}, node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, @@ -12,7 +13,7 @@ use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] pub(crate) struct LocationIndices { - map_file_to_range: FxHashMap>, + map_file_to_range: HashMap>, } impl LocationIndices { @@ -275,4 +276,75 @@ impl NodeInterner { .neighbors_directed(reference_index, petgraph::Direction::Outgoing) .next() } + + pub(crate) fn register_module(&mut self, id: ModuleId, name: String) { + self.register_name_for_auto_import(name, ModuleDefId::ModuleId(id), ItemVisibility::Public); + } + + pub(crate) fn register_global( + &mut self, + id: GlobalId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Global(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::GlobalId(id), visibility); + } + + pub(crate) fn register_struct( + &mut self, + id: StructId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Struct(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeId(id), visibility); + } + + pub(crate) fn register_trait(&mut self, id: TraitId, name: String, parent_module_id: ModuleId) { + self.add_definition_location(ReferenceId::Trait(id), Some(parent_module_id)); + + self.register_name_for_auto_import(name, ModuleDefId::TraitId(id), ItemVisibility::Public); + } + + pub(crate) fn register_type_alias( + &mut self, + id: TypeAliasId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Alias(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeAliasId(id), visibility); + } + + pub(crate) fn register_function(&mut self, id: FuncId, func_def: &FunctionDefinition) { + self.register_name_for_auto_import( + func_def.name.0.contents.clone(), + ModuleDefId::FunctionId(id), + func_def.visibility, + ); + } + + fn register_name_for_auto_import( + &mut self, + name: String, + module_def_id: ModuleDefId, + visibility: ItemVisibility, + ) { + if !self.lsp_mode { + return; + } + + self.auto_import_names.entry(name).or_default().push((module_def_id, visibility)); + } + + pub fn get_auto_import_names(&self) -> &HashMap> { + &self.auto_import_names + } } 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..46ec2676930 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -19,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::ModuleDefId; use crate::macros_api::UnaryOp; use crate::QuotedType; @@ -179,10 +180,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, @@ -231,6 +232,11 @@ pub struct NodeInterner { // (ReferenceId::Reference and ReferenceId::Local aren't included here) pub(crate) reference_modules: HashMap, + // All names (and their definitions) that can be offered for auto_import. + // These include top-level functions, global variables and types, but excludes + // impl and trait-impl methods. + pub(crate) auto_import_names: HashMap>, + /// Each value currently in scope in the comptime interpreter. /// Each element of the Vec represents a scope with every scope together making /// up all currently visible definitions. The first scope is always the global scope. @@ -602,6 +608,7 @@ impl Default for NodeInterner { reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::default(), reference_modules: HashMap::default(), + auto_import_names: HashMap::default(), comptime_scopes: vec![HashMap::default()], } } @@ -910,6 +917,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored self.add_definition_location(ReferenceId::Function(id), Some(module)); + definition_id } @@ -951,12 +959,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 +1139,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 +1212,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 +1318,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 +1336,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 +1672,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 +1697,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 +1706,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 +1803,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 +1827,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 +2006,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 +2015,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 +2046,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 +2056,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 +2107,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..ebb58ddc224 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,20 @@ 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 {{ or -> after function parameters")] + ExpectedLeftBraceOrArrowAfterFunctionParameters, + #[error("expected {{ after if condition")] + ExpectedLeftBraceAfterIfCondition, + #[error("expected <, where or {{ after trait name")] + ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitName, + #[error("expected <, where or {{ after impl type")] + ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType, + #[error("expected <, where or {{ after trait impl for type")] + ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, #[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..eddf85becfe 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -219,13 +219,27 @@ fn top_level_statement<'a>( /// /// implementation: 'impl' generics type '{' function_definition ... '}' fn implementation() -> impl NoirParser { + let methods_or_error = just(Token::LeftBrace) + .ignore_then(spanned(function::function_definition(true)).repeated()) + .then_ignore(just(Token::RightBrace)) + .or_not() + .validate(|methods, span, emit| { + if let Some(methods) = methods { + methods + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterImplType, + span, + )); + vec![] + } + }); + keyword(Keyword::Impl) .ignore_then(function::generics()) .then(parse_type().map_with_span(|typ, span| (typ, span))) .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(spanned(function::function_definition(true)).repeated()) - .then_ignore(just(Token::RightBrace)) + .then(methods_or_error) .map(|args| { let ((other_args, where_clause), methods) = args; let (generics, (object_type, type_span)) = other_args; @@ -377,6 +391,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 +534,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 +871,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 +917,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 +940,7 @@ where UnaryRhs::MemberAccess(field) => { Expression::member_access_or_method_call(lhs, field, span) } + UnaryRhs::JustADot => lhs, }, ) } @@ -931,10 +968,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 +1139,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 +1467,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 +1751,44 @@ 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"); + } + + #[test] + fn parse_recover_impl_without_body() { + let src = "impl Foo"; + + let (top_level_statement, errors) = parse_recover(implementation(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected <, where or { after impl type"); + + let top_level_statement = top_level_statement.unwrap(); + let TopLevelStatement::Impl(impl_) = top_level_statement else { + panic!("Expected to parse an impl"); + }; + + assert_eq!(impl_.object_type.to_string(), "Foo"); + assert!(impl_.methods.is_empty()); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs index 3de48d2e02a..56760898374 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -6,7 +6,10 @@ use super::{ self_parameter, where_clause, NoirParser, }; use crate::token::{Keyword, Token, TokenKind}; -use crate::{ast::IntegerBitSize, parser::spanned}; +use crate::{ + ast::{BlockExpression, IntegerBitSize}, + parser::spanned, +}; use crate::{ ast::{ FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, @@ -20,10 +23,24 @@ use crate::{ }; use chumsky::prelude::*; +use noirc_errors::Span; /// function_definition: attribute function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block /// function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block pub(super) fn function_definition(allow_self: bool) -> impl NoirParser { + let body_or_error = + spanned(block(fresh_statement()).or_not()).validate(|(body, body_span), span, emit| { + if let Some(body) = body { + (body, body_span) + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBraceOrArrowAfterFunctionParameters, + span, + )); + (BlockExpression { statements: vec![] }, Span::from(span.end()..span.end())) + } + }); + attributes() .then(function_modifiers()) .then_ignore(keyword(Keyword::Fn)) @@ -32,7 +49,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser after function parameters"); + + let noir_function = noir_function.unwrap(); + assert_eq!(noir_function.name(), "foo"); + assert_eq!(noir_function.parameters().len(), 1); + assert!(noir_function.def.body.statements.is_empty()); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs index 0874cadd34e..0cf5e63f5f3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -23,14 +23,28 @@ use crate::{ use super::{generic_type_args, parse_type, primitives::ident}; pub(super) fn trait_definition() -> impl NoirParser { + let trait_body_or_error = just(Token::LeftBrace) + .ignore_then(trait_body()) + .then_ignore(just(Token::RightBrace)) + .or_not() + .validate(|items, span, emit| { + if let Some(items) = items { + items + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitName, + span, + )); + vec![] + } + }); + attributes() .then_ignore(keyword(Keyword::Trait)) .then(ident()) .then(function::generics()) .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_body()) - .then_ignore(just(Token::RightBrace)) + .then(trait_body_or_error) .validate(|((((attributes, name), generics), where_clause), items), span, emit| { let attributes = validate_secondary_attributes(attributes, span, emit); TopLevelStatement::Trait(NoirTrait { @@ -70,13 +84,26 @@ fn trait_function_declaration() -> impl NoirParser { let trait_function_body_or_semicolon = block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); + let trait_function_body_or_semicolon_or_error = + trait_function_body_or_semicolon.or_not().validate(|body, span, emit| { + if let Some(body) = body { + body + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBraceOrArrowAfterFunctionParameters, + span, + )); + None + } + }); + keyword(Keyword::Fn) .ignore_then(ident()) .then(function::generics()) .then(parenthesized(function_declaration_parameters())) .then(function_return_type().map(|(_, typ)| typ)) .then(where_clause()) - .then(trait_function_body_or_semicolon) + .then(trait_function_body_or_semicolon_or_error) .map(|(((((name, generics), parameters), return_type), where_clause), body)| { TraitItem::Function { name, generics, parameters, return_type, where_clause, body } }) @@ -101,6 +128,24 @@ fn trait_type_declaration() -> impl NoirParser { /// /// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' pub(super) fn trait_implementation() -> impl NoirParser { + let body_or_error = + just(Token::LeftBrace) + .ignore_then(trait_implementation_body()) + .then_ignore(just(Token::RightBrace)) + .or_not() + .validate(|items, span, emit| { + if let Some(items) = items { + items + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedLeftBracketOrWhereOrLeftBraceOrArrowAfterTraitImplForType, + span, + )); + + vec![] + } + }); + keyword(Keyword::Impl) .ignore_then(function::generics()) .then(path_no_turbofish()) @@ -108,13 +153,10 @@ pub(super) fn trait_implementation() -> impl NoirParser { .then_ignore(keyword(Keyword::For)) .then(parse_type()) .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_implementation_body()) - .then_ignore(just(Token::RightBrace)) + .then(body_or_error) .map(|args| { let (((other_args, object_type), where_clause), items) = args; let ((impl_generics, trait_name), trait_generics) = other_args; - TopLevelStatement::TraitImpl(NoirTraitImpl { impl_generics, trait_name, @@ -227,4 +269,54 @@ mod test { vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], ); } + + #[test] + fn parse_recover_function_without_left_brace_or_semicolon() { + let src = "fn foo(x: i32)"; + + let (trait_item, errors) = parse_recover(trait_function_declaration(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected { or -> after function parameters"); + + let Some(TraitItem::Function { name, parameters, body, .. }) = trait_item else { + panic!("Expected to parser trait item as function"); + }; + + assert_eq!(name.to_string(), "foo"); + assert_eq!(parameters.len(), 1); + assert!(body.is_none()); + } + + #[test] + fn parse_recover_trait_without_body() { + let src = "trait Foo"; + + let (top_level_statement, errors) = parse_recover(trait_definition(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected <, where or { after trait name"); + + let top_level_statement = top_level_statement.unwrap(); + let TopLevelStatement::Trait(trait_) = top_level_statement else { + panic!("Expected to parse a trait"); + }; + + assert_eq!(trait_.name.to_string(), "Foo"); + assert!(trait_.items.is_empty()); + } + + #[test] + fn parse_recover_trait_impl_without_body() { + let src = "impl Foo for Bar"; + + let (top_level_statement, errors) = parse_recover(trait_implementation(), src); + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].message, "expected <, where or { after trait impl for type"); + + let top_level_statement = top_level_statement.unwrap(); + let TopLevelStatement::TraitImpl(trait_impl) = top_level_statement else { + panic!("Expected to parse a trait impl"); + }; + + assert!(trait_impl.items.is_empty()); + } } 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..bba596ed19f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -2460,6 +2460,201 @@ 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); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn 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::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn 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/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index 5140f5a5a8c..8bb56703e8a 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index 20272dfeecb..c8b8c3bb06e 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -6,8 +6,10 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index e939cd583e8..18417b376e5 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -216,7 +216,8 @@ "wasi", "wasmer", "Weierstraß", - "zshell" + "zshell", + "Linea" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md index c8a42c379e6..3ef6e014a2f 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -86,7 +86,7 @@ A Noir program makes a statement that can be verified. It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure + - Conceptually a tree structure - Leaves (inputs) are the `Field` type - Nodes contain arithmetic operations to combine them (gates) - The root is the final result (return value) @@ -99,12 +99,16 @@ You can dig deeper and use the `--print-acir` param to take a closer look at ind ### Use the `Field` type Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. +Some things to be mindful of when using a Field type for a regular integer value: +- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) + - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 +- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size +- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to :::tip Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates ::: -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. ### Use Arithmetic over non-arithmetic operations diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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.32.0/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. 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/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index 10182ca83b0..ec979d60753 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -18,10 +18,12 @@ impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } } impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other } } impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } } +impl Eq for u16 { fn eq(self, other: u16) -> bool { self == other } } impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } } impl Eq for u1 { fn eq(self, other: u1) -> bool { self == other } } impl Eq for i8 { fn eq(self, other: i8) -> bool { self == other } } +impl Eq for i16 { fn eq(self, other: i16) -> bool { self == other } } impl Eq for i32 { fn eq(self, other: i32) -> bool { self == other } } impl Eq for i64 { fn eq(self, other: i64) -> bool { self == other } } @@ -157,6 +159,18 @@ impl Ord for u32 { } } +impl Ord for u16 { + fn cmp(self, other: u16) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} + impl Ord for u8 { fn cmp(self, other: u8) -> Ordering { if self < other { @@ -181,6 +195,18 @@ impl Ord for i8 { } } +impl Ord for i16 { + fn cmp(self, other: i16) -> Ordering { + if self < other { + Ordering::less() + } else if self > other { + Ordering::greater() + } else { + Ordering::equal() + } + } +} + impl Ord for i32 { fn cmp(self, other: i32) -> Ordering { if self < other { 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/default.nr b/noir/noir-repo/noir_stdlib/src/default.nr index f9399bfb865..3ac5fbb394e 100644 --- a/noir/noir-repo/noir_stdlib/src/default.nr +++ b/noir/noir-repo/noir_stdlib/src/default.nr @@ -17,11 +17,14 @@ comptime fn derive_default(s: StructDefinition) -> Quoted { impl Default for Field { fn default() -> Field { 0 } } +impl Default for u1 { fn default() -> u1 { 0 } } impl Default for u8 { fn default() -> u8 { 0 } } +impl Default for u16 { fn default() -> u16 { 0 } } impl Default for u32 { fn default() -> u32 { 0 } } impl Default for u64 { fn default() -> u64 { 0 } } impl Default for i8 { fn default() -> i8 { 0 } } +impl Default for i16 { fn default() -> i16 { 0 } } impl Default for i32 { fn default() -> i32 { 0 } } impl Default for i64 { fn default() -> i64 { 0 } } 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..3abc630ab10 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 } @@ -193,12 +195,24 @@ impl Hash for Field { } } +impl Hash for u1 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, self as Field); + } +} + impl Hash for u8 { fn hash(self, state: &mut H) where H: Hasher{ H::write(state, self as Field); } } +impl Hash for u16 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, self as Field); + } +} + impl Hash for u32 { fn hash(self, state: &mut H) where H: Hasher{ H::write(state, self as Field); @@ -217,6 +231,12 @@ impl Hash for i8 { } } +impl Hash for i16 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, self as Field); + } +} + impl Hash for i32 { fn hash(self, state: &mut H) where H: Hasher{ H::write(state, self as Field); 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/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 54681632543..94889b7c3dd 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,6 +1,228 @@ use crate::option::Option; +use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_array)] + fn as_array(self) -> Option<[Expr]> {} + + #[builtin(expr_as_integer)] + fn as_integer(self) -> Option<(Field, bool)> {} + + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + + #[builtin(expr_as_bool)] + fn as_bool(self) -> Option {} + #[builtin(expr_as_function_call)] fn as_function_call(self) -> Option<(Expr, [Expr])> {} + + #[builtin(expr_as_if)] + fn as_if(self) -> Option<(Expr, Expr, Option)> {} + + #[builtin(expr_as_index)] + fn as_index(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_member_access)] + fn as_member_access(self) -> Option<(Expr, Quoted)> {} + + #[builtin(expr_as_repeated_element_array)] + fn as_repeated_element_array(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_repeated_element_slice)] + fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> {} + + #[builtin(expr_as_slice)] + fn as_slice(self) -> Option<[Expr]> {} + + #[builtin(expr_as_tuple)] + fn as_tuple(self) -> Option<[Expr]> {} + + #[builtin(expr_as_unary_op)] + fn as_unary_op(self) -> Option<(UnaryOp, Expr)> {} +} + +mod tests { + use crate::meta::op::UnaryOp; + use crate::meta::op::BinaryOp; + + #[test] + fn test_expr_as_array() { + comptime + { + let expr = quote { [1, 2, 4] }.as_expr().unwrap(); + let elems = expr.as_array().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (2, false)); + assert_eq(elems[2].as_integer().unwrap(), (4, false)); + } + } + + #[test] + fn test_expr_as_integer() { + comptime + { + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + } + } + + #[test] + fn test_expr_as_binary_op() { + comptime + { + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + } + } + + #[test] + fn test_expr_as_bool() { + comptime + { + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + } + } + + #[test] + fn test_expr_as_function_call() { + comptime + { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } + + #[test] + fn test_expr_as_if() { + comptime + { + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + } + } + + #[test] + fn test_expr_as_index() { + comptime + { + let expr = quote { foo[bar] }.as_expr().unwrap(); + assert(expr.as_index().is_some()); + } + } + + #[test] + fn test_expr_as_member_access() { + comptime + { + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); + } + } + + #[test] + fn test_expr_as_repeated_element_array() { + comptime + { + let expr = quote { [1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_array().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_repeated_element_slice() { + comptime + { + let expr = quote { &[1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_slice().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_slice() { + comptime + { + let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); + let elems = expr.as_slice().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (3, false)); + assert_eq(elems[2].as_integer().unwrap(), (5, false)); + } + } + + #[test] + fn test_expr_as_tuple() { + comptime + { + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + } + } + + #[test] + fn test_expr_as_unary_op() { + comptime + { + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + } + } + + #[test] + fn test_automatically_unwraps_parenthesized_expression() { + comptime + { + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + } + } + + comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op + } + + comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op + } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 2763685fd0d..d16a8648bc2 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -5,9 +5,11 @@ use crate::hash::poseidon2::Poseidon2Hasher; mod expr; mod function_def; mod module; +mod op; mod struct_def; mod trait_constraint; mod trait_def; +mod trait_impl; mod typ; mod quoted; @@ -31,7 +33,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); @@ -41,7 +45,7 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted result } -unconstrained pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { HANDLERS.insert(t, f); } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr new file mode 100644 index 00000000000..ebd89677c50 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -0,0 +1,92 @@ +struct UnaryOp { + op: Field +} + +impl UnaryOp { + fn is_minus(self) -> bool { + self.op == 0 + } + + fn is_not(self) -> bool { + self.op == 1 + } + + fn is_mutable_reference(self) -> bool { + self.op == 2 + } + + fn is_dereference(self) -> bool { + self.op == 3 + } +} + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + 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/ops/arith.nr b/noir/noir-repo/noir_stdlib/src/ops/arith.nr index df0ff978a7c..918c8e9bc28 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/arith.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/arith.nr @@ -10,6 +10,7 @@ impl Add for u64 { fn add(self, other: u64) -> u64 { self + other } } impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } } impl Add for u16 { fn add(self, other: u16) -> u16 { self + other } } impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } } +impl Add for u1 { fn add(self, other: u1) -> u1 { self + other } } impl Add for i8 { fn add(self, other: i8) -> i8 { self + other } } impl Add for i16 { fn add(self, other: i16) -> i16 { self + other } } @@ -28,6 +29,7 @@ impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other } } impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } } impl Sub for u16 { fn sub(self, other: u16) -> u16 { self - other } } impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } } +impl Sub for u1 { fn sub(self, other: u1) -> u1 { self - other } } impl Sub for i8 { fn sub(self, other: i8) -> i8 { self - other } } impl Sub for i16 { fn sub(self, other: i16) -> i16 { self - other } } @@ -46,6 +48,7 @@ impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other } } impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } } impl Mul for u16 { fn mul(self, other: u16) -> u16 { self * other } } impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } } +impl Mul for u1 { fn mul(self, other: u1) -> u1 { self * other } } impl Mul for i8 { fn mul(self, other: i8) -> i8 { self * other } } impl Mul for i16 { fn mul(self, other: i16) -> i16 { self * other } } @@ -64,6 +67,7 @@ impl Div for u64 { fn div(self, other: u64) -> u64 { self / other } } impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } } impl Div for u16 { fn div(self, other: u16) -> u16 { self / other } } impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } } +impl Div for u1 { fn div(self, other: u1) -> u1 { self / other } } impl Div for i8 { fn div(self, other: i8) -> i8 { self / other } } impl Div for i16 { fn div(self, other: i16) -> i16 { self / other } } @@ -80,6 +84,7 @@ impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u1 { fn rem(self, other: u1) -> u1 { self % other } } impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } diff --git a/noir/noir-repo/noir_stdlib/src/ops/bit.nr b/noir/noir-repo/noir_stdlib/src/ops/bit.nr index a31cfee878c..015d0008e7a 100644 --- a/noir/noir-repo/noir_stdlib/src/ops/bit.nr +++ b/noir/noir-repo/noir_stdlib/src/ops/bit.nr @@ -31,6 +31,7 @@ impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u1 { fn bitor(self, other: u1) -> u1 { self | other } } impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } @@ -49,6 +50,7 @@ impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other } } impl BitAnd for u32 { fn bitand(self, other: u32) -> u32 { self & other } } impl BitAnd for u16 { fn bitand(self, other: u16) -> u16 { self & other } } impl BitAnd for u8 { fn bitand(self, other: u8) -> u8 { self & other } } +impl BitAnd for u1 { fn bitand(self, other: u1) -> u1 { self & other } } impl BitAnd for i8 { fn bitand(self, other: i8) -> i8 { self & other } } impl BitAnd for i16 { fn bitand(self, other: i16) -> i16 { self & other } } @@ -67,6 +69,7 @@ impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other } } impl BitXor for u32 { fn bitxor(self, other: u32) -> u32 { self ^ other } } impl BitXor for u16 { fn bitxor(self, other: u16) -> u16 { self ^ other } } impl BitXor for u8 { fn bitxor(self, other: u8) -> u8 { self ^ other } } +impl BitXor for u1 { fn bitxor(self, other: u1) -> u1 { self ^ other } } impl BitXor for i8 { fn bitxor(self, other: i8) -> i8 { self ^ other } } impl BitXor for i16 { fn bitxor(self, other: i16) -> i16 { self ^ other } } 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_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr deleted file mode 100644 index 8b6f7b480c7..00000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - comptime - { - let expr = quote { foo(bar) }.as_expr().unwrap(); - let (_function, args) = expr.as_function_call().unwrap(); - assert_eq(args.len(), 1); - } -} 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_exp/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml similarity index 74% rename from noir/noir-repo/test_programs/compile_success_empty/comptime_exp/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml index df36e0e05b0..c9a6f1e150e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "comptime_exp" +name = "comptime_trait_impl" type = "bin" authors = [""] compiler_version = ">=0.31.0" 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..c5cc7880112 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,14 +4,16 @@ use std::meta::unquote; fn main() { comptime { - foo::<3>(5); + unsafe { + foo::<3>(5) + }; submodule::bar(); } } // Call a different function from the interpreter, then have the // elaborator switch to the middle of foo from its previous scope in main -unconstrained comptime fn foo(x: Field) { +comptime fn foo(x: Field) { assert(modulus_num_bits() != 0); let cond = quote { modulus_num_bits() != 0 }; 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_loop_size_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Nargo.toml new file mode 100644 index 00000000000..d2a98e19742 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_loop_size_regression" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr new file mode 100644 index 00000000000..488304114b9 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/brillig_loop_size_regression/src/main.nr @@ -0,0 +1,16 @@ +struct EnumEmulation { + a: Option, + b: Option, + c: Option, +} + +unconstrained fn main() -> pub Field { + let mut emulated_enum = EnumEmulation { a: Option::some(1), b: Option::none(), c: Option::none() }; + + for _ in 0..1 { + assert_eq(emulated_enum.a.unwrap(), 1); + } + + emulated_enum.a = Option::some(2); + emulated_enum.a.unwrap() +} 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/gates_report_brillig.sh b/noir/noir-repo/test_programs/gates_report_brillig.sh new file mode 100644 index 00000000000..d3f6344dbf4 --- /dev/null +++ b/noir/noir-repo/test_programs/gates_report_brillig.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e + +# These tests are incompatible with gas reporting +excluded_dirs=("workspace" "workspace_default_member" "double_verify_nested_proof" "overlapping_dep_and_mod" "comptime_println") + +current_dir=$(pwd) +base_path="$current_dir/execution_success" +test_dirs=$(ls $base_path) + +# We generate a Noir workspace which contains all of the test cases +# This allows us to generate a gates report using `nargo info` for all of them at once. + +echo "[workspace]" > Nargo.toml +echo "members = [" >> Nargo.toml + +for dir in $test_dirs; do + if [[ " ${excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + if [[ ${CI-false} = "true" ]] && [[ " ${ci_excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + echo " \"execution_success/$dir\"," >> Nargo.toml +done + +echo "]" >> Nargo.toml + +nargo info --force-brillig --json > gates_report_brillig.json + +rm Nargo.toml 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/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3..06dd5e676bd 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `acvm_cli` to `acvm` diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 540d6d11bc0..b9b83d86836 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [build-dependencies] build-data.workspace = true diff --git a/noir/noir-repo/tooling/debugger/src/context.rs b/noir/noir-repo/tooling/debugger/src/context.rs index 6ec1aff8325..890732b579c 100644 --- a/noir/noir-repo/tooling/debugger/src/context.rs +++ b/noir/noir-repo/tooling/debugger/src/context.rs @@ -566,7 +566,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &mut self, call_info: AcirCallWaitInfo, ) -> DebugCommandResult { - let callee_circuit = &self.circuits[call_info.id as usize]; + let callee_circuit = &self.circuits[call_info.id.as_usize()]; let callee_witness_map = call_info.initial_witness; let callee_acvm = ACVM::new( self.backend, @@ -578,7 +578,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { let caller_acvm = std::mem::replace(&mut self.acvm, callee_acvm); self.acvm_stack .push(ExecutionFrame { circuit_id: self.current_circuit_id, acvm: caller_acvm }); - self.current_circuit_id = call_info.id; + self.current_circuit_id = call_info.id.0; // Explicitly handling the new ACVM status here handles two edge cases: // 1. there is a breakpoint set at the beginning of a circuit @@ -596,7 +596,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { let ACVMStatus::RequiresAcirCall(call_info) = self.acvm.get_status() else { unreachable!("Resolving an ACIR call, the caller is in an invalid state"); }; - let acir_to_call = &self.circuits[call_info.id as usize]; + let acir_to_call = &self.circuits[call_info.id.as_usize()]; let mut call_resolved_outputs = Vec::new(); for return_witness_index in acir_to_call.return_values.indices() { @@ -946,7 +946,7 @@ mod tests { brillig::IntegerBitSize, circuit::{ brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}, - opcodes::{BlockId, BlockType}, + opcodes::{AcirFunctionId, BlockId, BlockType}, }, native_types::Expression, AcirField, @@ -1210,7 +1210,12 @@ mod tests { outputs: vec![], predicate: None, }, - Opcode::Call { id: 1, inputs: vec![], outputs: vec![], predicate: None }, + Opcode::Call { + id: AcirFunctionId(1), + inputs: vec![], + outputs: vec![], + predicate: None, + }, Opcode::AssertZero(Expression::default()), ], ..Circuit::default() diff --git a/noir/noir-repo/tooling/fuzzer/Cargo.toml b/noir/noir-repo/tooling/fuzzer/Cargo.toml index 106d8abead1..31701991844 100644 --- a/noir/noir-repo/tooling/fuzzer/Cargo.toml +++ b/noir/noir-repo/tooling/fuzzer/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 03c6c9105ba..353a6ade904 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true# rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -30,6 +33,7 @@ thiserror.workspace = true fm.workspace = true rayon = "1.8.0" fxhash.workspace = true +convert_case = "0.6.0" [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] wasm-bindgen.workspace = true diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ca34d7686fd..366b158ad89 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -23,14 +23,14 @@ use fxhash::FxHashSet; use lsp_types::{ request::{ Completion, DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, - References, Rename, + References, Rename, SignatureHelpRequest, }, CodeLens, }; use nargo::{ package::{Package, PackageType}, parse_all, - workspace::{self, Workspace}, + workspace::Workspace, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -55,7 +55,7 @@ use requests::{ on_goto_declaration_request, on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, - on_test_run_request, on_tests_request, LspInitializationOptions, + on_signature_help_request, on_test_run_request, on_tests_request, LspInitializationOptions, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -143,6 +143,7 @@ impl NargoLspService { .request::(on_hover_request) .request::(on_inlay_hint_request) .request::(on_completion_request) + .request::(on_signature_help_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -383,7 +384,7 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { pub fn insert_all_files_for_workspace_into_file_manager( state: &LspState, - workspace: &workspace::Workspace, + workspace: &Workspace, file_manager: &mut FileManager, ) { // First add files we cached: these have the source code of files that are modified @@ -410,7 +411,7 @@ fn prepare_package_from_source_string() { let client = ClientSocket::new_closed(); let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); - let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); + let (mut context, crate_id) = prepare_source(source.to_string(), &mut state); let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3a60de15c4a..4d2186badc3 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -223,7 +223,7 @@ mod notification_tests { use super::*; use lsp_types::{ - InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + InlayHintLabel, InlayHintParams, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, WorkDoneProgressParams, }; @@ -270,9 +270,9 @@ mod notification_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: 0, character: 0 }, - end: lsp_types::Position { line: 1, character: 0 }, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 1, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index 48616c0f52d..302152f0829 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,74 +4,47 @@ use std::{ }; use async_lsp::ResponseError; -use builtins::{builtin_integer_types, keyword_builtin_function, keyword_builtin_type}; -use fm::{FileId, PathString}; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, - CompletionResponse, InsertTextFormat, +use completion_items::{ + crate_completion_item, field_completion_item, simple_completion_item, + struct_field_completion_item, }; +use convert_case::{Case, Casing}; +use fm::{FileId, FileMap, PathString}; +use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; +use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - ArrayLiteral, AsTraitPath, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, Expression, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, LValue, Lambda, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, PathKind, PathSegment, Pattern, - Statement, TraitImplItem, TraitItem, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedType, UseTree, UseTreeKind, + AsTraitPath, BlockExpression, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, Ident, IfExpression, LValue, Lambda, LetStatement, + MemberAccessExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, + PathSegment, Pattern, Statement, StatementKind, TraitItem, TypeImpl, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, }, graph::{CrateId, Dependency}, hir::{ 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}, + hir_def::traits::Trait, + 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 crate::{requests::to_lsp_location, utils, LspState}; use super::process_request; +mod auto_import; 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; +mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -94,7 +67,9 @@ pub(crate) fn on_completion_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut finder = NodeFinder::new( + args.files, file_id, + source, byte_index, byte, args.crate_id, @@ -110,7 +85,9 @@ pub(crate) fn on_completion_request( } struct NodeFinder<'a> { + files: &'a FileMap, file: FileId, + lines: Vec<&'a str>, byte_index: usize, byte: Option, /// The module ID of the current file. @@ -129,11 +106,20 @@ struct NodeFinder<'a> { /// Type parameters in the current scope. These are collected when entering /// a struct, a function, etc., and cleared afterwards. type_parameters: HashSet, + /// ModuleDefIds we already suggested, so we don't offer these for auto-import. + suggested_module_def_ids: HashSet, + /// How many nested `mod` we are in deep + nesting: usize, + /// The line where an auto_import must be inserted + auto_import_line: usize, } impl<'a> NodeFinder<'a> { + #[allow(clippy::too_many_arguments)] fn new( + files: &'a FileMap, file: FileId, + source: &'a str, byte_index: usize, byte: Option, krate: CrateId, @@ -153,7 +139,9 @@ impl<'a> NodeFinder<'a> { }; let module_id = ModuleId { krate, local_id }; Self { + files, file, + lines: source.lines().collect(), byte_index, byte, root_module_id, @@ -164,6 +152,9 @@ impl<'a> NodeFinder<'a> { completion_items: Vec::new(), local_variables: HashMap::new(), type_parameters: HashSet::new(), + suggested_module_def_ids: HashSet::new(), + nesting: 0, + auto_import_line: 0, } } @@ -173,17 +164,26 @@ 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()); + } + } - fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); + Some(CompletionResponse::Array(items)) } } fn find_in_item(&mut self, item: &Item) { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; + } + } + if !self.includes_span(item.span) { return; } @@ -206,10 +206,19 @@ impl<'a> NodeFinder<'a> { ModuleId { krate: self.module_id.krate, local_id: *child_module }; } + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + self.find_in_parsed_module(&parsed_sub_module.contents); // Restore the old module before continuing self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; } ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), @@ -243,6 +252,9 @@ impl<'a> NodeFinder<'a> { } fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + self.find_in_path(&noir_trait_impl.trait_name, RequestedItems::OnlyTypes); + self.find_in_unresolved_type(&noir_trait_impl.object_type); + self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_trait_impl.impl_generics); @@ -253,15 +265,9 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + self.find_in_unresolved_type(&type_impl.object_type); + self.type_parameters.clear(); self.collect_type_parameters_in_generics(&type_impl.generics); @@ -277,10 +283,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { self.type_parameters.clear(); self.collect_type_parameters_in_generics(&noir_struct.generics); @@ -292,12 +294,6 @@ impl<'a> NodeFinder<'a> { self.type_parameters.clear(); } - fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { match trait_item { TraitItem::Function { @@ -357,22 +353,22 @@ impl<'a> NodeFinder<'a> { fn find_in_statement(&mut self, statement: &Statement) { match &statement.kind { - noirc_frontend::ast::StatementKind::Let(let_statement) => { + StatementKind::Let(let_statement) => { self.find_in_let_statement(let_statement, true); } - noirc_frontend::ast::StatementKind::Constrain(constrain_statement) => { + StatementKind::Constrain(constrain_statement) => { self.find_in_constrain_statement(constrain_statement); } - noirc_frontend::ast::StatementKind::Expression(expression) => { + StatementKind::Expression(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Assign(assign_statement) => { + StatementKind::Assign(assign_statement) => { self.find_in_assign_statement(assign_statement); } - noirc_frontend::ast::StatementKind::For(for_loop_statement) => { + StatementKind::For(for_loop_statement) => { self.find_in_for_loop_statement(for_loop_statement); } - noirc_frontend::ast::StatementKind::Comptime(statement) => { + StatementKind::Comptime(statement) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -381,12 +377,10 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::StatementKind::Semi(expression) => { + StatementKind::Semi(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::StatementKind::Break - | noirc_frontend::ast::StatementKind::Continue - | noirc_frontend::ast::StatementKind::Error => (), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } @@ -403,22 +397,6 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - fn find_in_assign_statement( - &mut self, - assign_statement: &noirc_frontend::ast::AssignStatement, - ) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { let old_local_variables = self.local_variables.clone(); let ident = &for_loop_statement.identifier; @@ -432,7 +410,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); @@ -442,69 +431,53 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - fn find_in_expression(&mut self, expression: &Expression) { match &expression.kind { - noirc_frontend::ast::ExpressionKind::Literal(literal) => self.find_in_literal(literal), - noirc_frontend::ast::ExpressionKind::Block(block_expression) => { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { self.find_in_block_expression(block_expression); } - noirc_frontend::ast::ExpressionKind::Prefix(prefix_expression) => { + ExpressionKind::Prefix(prefix_expression) => { self.find_in_expression(&prefix_expression.rhs); } - noirc_frontend::ast::ExpressionKind::Index(index_expression) => { + ExpressionKind::Index(index_expression) => { self.find_in_index_expression(index_expression); } - noirc_frontend::ast::ExpressionKind::Call(call_expression) => { + ExpressionKind::Call(call_expression) => { self.find_in_call_expression(call_expression); } - noirc_frontend::ast::ExpressionKind::MethodCall(method_call_expression) => { + ExpressionKind::MethodCall(method_call_expression) => { self.find_in_method_call_expression(method_call_expression); } - noirc_frontend::ast::ExpressionKind::Constructor(constructor_expression) => { + ExpressionKind::Constructor(constructor_expression) => { self.find_in_constructor_expression(constructor_expression); } - noirc_frontend::ast::ExpressionKind::MemberAccess(member_access_expression) => { + ExpressionKind::MemberAccess(member_access_expression) => { self.find_in_member_access_expression(member_access_expression); } - noirc_frontend::ast::ExpressionKind::Cast(cast_expression) => { + ExpressionKind::Cast(cast_expression) => { self.find_in_cast_expression(cast_expression); } - noirc_frontend::ast::ExpressionKind::Infix(infix_expression) => { + ExpressionKind::Infix(infix_expression) => { self.find_in_infix_expression(infix_expression); } - noirc_frontend::ast::ExpressionKind::If(if_expression) => { + ExpressionKind::If(if_expression) => { self.find_in_if_expression(if_expression); } - noirc_frontend::ast::ExpressionKind::Variable(path) => { + ExpressionKind::Variable(path) => { self.find_in_path(path, RequestedItems::AnyItems); } - noirc_frontend::ast::ExpressionKind::Tuple(expressions) => { + ExpressionKind::Tuple(expressions) => { self.find_in_expressions(expressions); } - noirc_frontend::ast::ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - noirc_frontend::ast::ExpressionKind::Parenthesized(expression) => { + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Unquote(expression) => { + ExpressionKind::Unquote(expression) => { self.find_in_expression(expression); } - noirc_frontend::ast::ExpressionKind::Comptime(block_expression, _) => { + ExpressionKind::Comptime(block_expression, _) => { // When entering a comptime block, regular local variables shouldn't be offered anymore let old_local_variables = self.local_variables.clone(); self.local_variables.clear(); @@ -513,58 +486,75 @@ impl<'a> NodeFinder<'a> { self.local_variables = old_local_variables; } - noirc_frontend::ast::ExpressionKind::AsTraitPath(as_trait_path) => { + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::ExpressionKind::Quote(_) - | noirc_frontend::ast::ExpressionKind::Resolved(_) - | noirc_frontend::ast::ExpressionKind::Error => (), + ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), } - } - fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), + // "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_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } + fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return; } - } - fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } } - fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - } + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { + return; + }; - fn find_in_method_call_expression(&mut self, method_call_expression: &MethodCallExpression) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); + } + + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } } @@ -572,16 +562,20 @@ impl<'a> NodeFinder<'a> { &mut self, member_access_expression: &MemberAccessExpression, ) { - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } + 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().to_case(Case::Snake); + self.complete_type_fields_and_methods(&typ, &prefix); + return; + } + } - fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); + self.find_in_expression(&member_access_expression.lhs); } fn find_in_if_expression(&mut self, if_expression: &IfExpression) { @@ -617,21 +611,6 @@ impl<'a> NodeFinder<'a> { self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { if let Some(span) = unresolved_type.span { if !self.includes_span(span) { @@ -640,48 +619,48 @@ impl<'a> NodeFinder<'a> { } match &unresolved_type.typ { - noirc_frontend::ast::UnresolvedTypeData::Array(_, unresolved_type) => { + UnresolvedTypeData::Array(_, unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Slice(unresolved_type) => { + UnresolvedTypeData::Slice(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Parenthesized(unresolved_type) => { + UnresolvedTypeData::Parenthesized(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Named(path, unresolved_types, _) => { + UnresolvedTypeData::Named(path, unresolved_types, _) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { self.find_in_path(path, RequestedItems::OnlyTypes); self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::MutableReference(unresolved_type) => { + UnresolvedTypeData::MutableReference(unresolved_type) => { self.find_in_unresolved_type(unresolved_type); } - noirc_frontend::ast::UnresolvedTypeData::Tuple(unresolved_types) => { + UnresolvedTypeData::Tuple(unresolved_types) => { self.find_in_unresolved_types(unresolved_types); } - noirc_frontend::ast::UnresolvedTypeData::Function(args, ret, env) => { + UnresolvedTypeData::Function(args, ret, env, _) => { self.find_in_unresolved_types(args); self.find_in_unresolved_type(ret); self.find_in_unresolved_type(env); } - noirc_frontend::ast::UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(as_trait_path) => { self.find_in_as_trait_path(as_trait_path); } - noirc_frontend::ast::UnresolvedTypeData::Expression(_) - | noirc_frontend::ast::UnresolvedTypeData::FormatString(_, _) - | noirc_frontend::ast::UnresolvedTypeData::String(_) - | noirc_frontend::ast::UnresolvedTypeData::Unspecified - | noirc_frontend::ast::UnresolvedTypeData::Quoted(_) - | noirc_frontend::ast::UnresolvedTypeData::FieldElement - | noirc_frontend::ast::UnresolvedTypeData::Integer(_, _) - | noirc_frontend::ast::UnresolvedTypeData::Bool - | noirc_frontend::ast::UnresolvedTypeData::Unit - | noirc_frontend::ast::UnresolvedTypeData::Resolved(_) - | noirc_frontend::ast::UnresolvedTypeData::Error => (), + UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Error => (), } } @@ -706,20 +685,54 @@ impl<'a> NodeFinder<'a> { at_root = idents.is_empty(); } + let prefix = prefix.to_case(Case::Snake); + 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(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods(trait_, &prefix, FunctionKind::Any); + 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, @@ -743,6 +756,7 @@ impl<'a> NodeFinder<'a> { self.type_parameters_completion(&prefix); } } + self.complete_auto_imports(&prefix, requested_items); } } @@ -827,7 +841,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 { @@ -835,11 +849,11 @@ impl<'a> NodeFinder<'a> { segments.push(ident.clone()); if let Some(module_id) = self.resolve_module(segments) { - let prefix = String::new(); + let prefix = ""; let at_root = false; self.complete_in_module( module_id, - &prefix, + prefix, path_kind, at_root, module_completion_kind, @@ -849,7 +863,7 @@ impl<'a> NodeFinder<'a> { }; } else { // We are right after the last segment - let prefix = ident.to_string(); + let prefix = ident.to_string().to_case(Case::Snake); if segments.is_empty() { let at_root = true; self.complete_in_module( @@ -913,6 +927,103 @@ 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::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); + } + } + } + } + } + + fn complete_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + ) { + for (name, func_id) in &trait_.method_ids { + 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); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); + } + } + } + } + + 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(struct_field_completion_item(name, typ)); + } + } + } + + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } + } + #[allow(clippy::too_many_arguments)] fn complete_in_module( &mut self, @@ -921,7 +1032,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 +1062,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,9 +1079,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } @@ -977,9 +1092,11 @@ impl<'a> NodeFinder<'a> { module_def_id, name.clone(), function_completion_kind, + function_kind, requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } } @@ -1011,131 +1128,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 +1137,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, + if let Ok(path_resolution) = path_resolver.resolve(self.def_maps, path, &mut None) { + return Some(path_resolution.module_def_id); } - } - 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 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); } } - } - 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()), - )); - } - } - } - - 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 { @@ -1211,994 +1163,76 @@ impl<'a> NodeFinder<'a> { } } +/// Returns true if name matches a prefix written in code. +/// `prefix` must already be in snake case. +/// This method splits both name and prefix by underscore, +/// then checks that every part of name starts with a part of +/// prefix, in order. +/// +/// For example: +/// +/// // "merk" and "ro" match "merkle" and "root" and are in order +/// name_matches("compute_merkle_root", "merk_ro") == true +/// +/// // "ro" matches "root", but "merkle" comes before it, so no match +/// name_matches("compute_merkle_root", "ro_mer") == false +/// +/// // neither "compute" nor "merkle" nor "root" start with "oot" +/// name_matches("compute_merkle_root", "oot") == false fn name_matches(name: &str, prefix: &str) -> bool { - name.starts_with(prefix) -} + let name = name.to_case(Case::Snake); + let name_parts: Vec<&str> = name.split('_').collect(); -fn module_completion_item(name: impl Into) -> CompletionItem { - simple_completion_item(name, CompletionItemKind::MODULE, None) -} + let mut last_index: i32 = -1; + for prefix_part in prefix.split('_') { + // Look past parts we already matched + let offset = if last_index >= 0 { last_index as usize + 1 } else { 0 }; -fn crate_completion_item(name: impl Into) -> CompletionItem { - simple_completion_item(name, CompletionItemKind::MODULE, None) -} + if let Some(mut name_part_index) = + name_parts.iter().skip(offset).position(|name_part| name_part.starts_with(prefix_part)) + { + // Need to adjust the index if we skipped some segments + name_part_index += offset; -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, + if last_index >= name_part_index as i32 { + return false; + } + last_index = name_part_index as i32; + } else { + return false; + } } + + true } -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, +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, } } #[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; - } +mod completion_name_matches_tests { + use crate::requests::completion::name_matches; #[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 test_name_matches() { + assert!(name_matches("foo", "foo")); + assert!(name_matches("foo_bar", "bar")); + assert!(name_matches("FooBar", "foo")); + assert!(name_matches("FooBar", "bar")); + assert!(name_matches("FooBar", "foo_bar")); + assert!(name_matches("bar_baz", "bar_b")); + + assert!(!name_matches("foo_bar", "o_b")); } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs new file mode 100644 index 00000000000..8d7824502c1 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -0,0 +1,210 @@ +use std::collections::BTreeMap; + +use lsp_types::{Position, Range, TextEdit}; +use noirc_frontend::{ + ast::ItemVisibility, + graph::{CrateId, Dependency}, + hir::def_map::{CrateDefMap, ModuleId}, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, +}; + +use super::{ + kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, + name_matches, + sort_text::auto_import_sort_text, + NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn complete_auto_imports(&mut self, prefix: &str, requested_items: RequestedItems) { + let current_module_parent_id = get_parent_module_id(self.def_maps, self.module_id); + + for (name, entries) in self.interner.get_auto_import_names() { + if !name_matches(name, prefix) { + continue; + } + + for (module_def_id, visibility) in entries { + if self.suggested_module_def_ids.contains(module_def_id) { + continue; + } + + let Some(mut completion_item) = self.module_def_id_completion_item( + *module_def_id, + name.clone(), + FunctionCompletionKind::NameAndParameters, + FunctionKind::Any, + requested_items, + ) else { + continue; + }; + + let module_full_path; + if let ModuleDefId::ModuleId(module_id) = module_def_id { + module_full_path = module_id_path( + *module_id, + &self.module_id, + current_module_parent_id, + self.interner, + self.dependencies, + ); + } else { + let Some(parent_module) = get_parent_module(self.interner, *module_def_id) + else { + continue; + }; + + match *visibility { + ItemVisibility::Public => (), + ItemVisibility::Private => { + // Technically this can't be reached because we don't record private items for auto-import, + // but this is here for completeness. + continue; + } + ItemVisibility::PublicCrate => { + if self.module_id.krate != parent_module.krate { + continue; + } + } + } + + module_full_path = module_id_path( + parent_module, + &self.module_id, + current_module_parent_id, + self.interner, + self.dependencies, + ); + } + + let full_path = if let ModuleDefId::ModuleId(..) = module_def_id { + module_full_path + } else { + format!("{}::{}", module_full_path, name) + }; + + let mut label_details = completion_item.label_details.unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + + let line = self.auto_import_line as u32; + let character = (self.nesting * 4) as u32; + let indent = " ".repeat(self.nesting * 4); + let mut newlines = "\n"; + + // If the line we are inserting into is not an empty line, insert an extra line to make some room + if let Some(line_text) = self.lines.get(line as usize) { + if !line_text.trim().is_empty() { + newlines = "\n\n"; + } + } + + completion_item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + new_text: format!("use {};{}{}", full_path, newlines, indent), + }]); + + completion_item.sort_text = Some(auto_import_sort_text()); + + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(*module_def_id); + } + } + } +} + +fn get_parent_module(interner: &NodeInterner, module_def_id: ModuleDefId) -> Option { + let reference_id = module_def_id_to_reference_id(module_def_id); + interner.reference_module(reference_id).copied() +} + +fn get_parent_module_id( + def_maps: &BTreeMap, + module_id: ModuleId, +) -> Option { + let crate_def_map = &def_maps[&module_id.krate]; + let module_data = &crate_def_map.modules()[module_id.local_id.0]; + module_data.parent.map(|parent| ModuleId { krate: module_id.krate, local_id: parent }) +} + +fn module_def_id_to_reference_id(module_def_id: ModuleDefId) -> ReferenceId { + match module_def_id { + ModuleDefId::ModuleId(id) => ReferenceId::Module(id), + ModuleDefId::FunctionId(id) => ReferenceId::Function(id), + ModuleDefId::TypeId(id) => ReferenceId::Struct(id), + ModuleDefId::TypeAliasId(id) => ReferenceId::Alias(id), + ModuleDefId::TraitId(id) => ReferenceId::Trait(id), + ModuleDefId::GlobalId(id) => ReferenceId::Global(id), + } +} + +/// Returns the path to reach an item inside `target_module_id` from inside `current_module_id`. +/// Returns a relative path if possible. +fn module_id_path( + target_module_id: ModuleId, + current_module_id: &ModuleId, + current_module_parent_id: Option, + interner: &NodeInterner, + dependencies: &[Dependency], +) -> String { + if Some(target_module_id) == current_module_parent_id { + return "super".to_string(); + } + + let mut segments: Vec<&str> = Vec::new(); + let mut is_relative = false; + + if let Some(module_attributes) = interner.try_module_attributes(&target_module_id) { + segments.push(&module_attributes.name); + + let mut current_attributes = module_attributes; + loop { + let parent_module_id = + &ModuleId { krate: target_module_id.krate, local_id: current_attributes.parent }; + + if current_module_id == parent_module_id { + is_relative = true; + break; + } + + if current_module_parent_id == Some(*parent_module_id) { + segments.push("super"); + is_relative = true; + break; + } + + let Some(parent_attributes) = interner.try_module_attributes(parent_module_id) else { + break; + }; + + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + } + + let crate_id = target_module_id.krate; + let crate_name = if is_relative { + None + } else { + match crate_id { + CrateId::Root(_) => Some("crate".to_string()), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Crate(_) => dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| dep.name.to_string()), + CrateId::Dummy => unreachable!("ICE: A dummy CrateId should not be accessible"), + } + }; + + if let Some(crate_name) = &crate_name { + segments.push(crate_name); + }; + + segments.reverse(); + segments.join("::") +} 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..d4190f5241c --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -0,0 +1,391 @@ +use lsp_types::{ + Command, 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::{ + crate_or_module_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.first() { + 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 if let Type::Tuple(self_tuple_types) = self_type { + // Tuple types of different lengths seem to also have methods defined on all of them, + // so here we reject methods for tuples where the length doesn't match. + if let Type::Tuple(func_self_tuple_types) = func_self_type { + if self_tuple_types.len() != func_self_tuple_types.len() { + 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 + }; + + let completion_item = match function_completion_kind { + FunctionCompletionKind::Name => completion_item, + FunctionCompletionKind::NameAndParameters => { + completion_item_with_trigger_parameter_hints_command(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 { + completion_item_with_sort_text( + simple_completion_item(name, CompletionItemKind::MODULE, None), + crate_or_module_sort_text(), + ) +} + +pub(super) fn crate_completion_item(name: impl Into) -> CompletionItem { + completion_item_with_sort_text( + simple_completion_item(name, CompletionItemKind::MODULE, None), + crate_or_module_sort_text(), + ) +} + +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 struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { + field_completion_item(field, typ.to_string()) +} + +pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +} + +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 } +} + +pub(super) fn completion_item_with_trigger_parameter_hints_command( + completion_item: CompletionItem, +) -> CompletionItem { + CompletionItem { + command: Some(Command { + title: "Trigger parameter hints".to_string(), + command: "editor.action.triggerParameterHints".to_string(), + arguments: None, + }), + ..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..9bdc603660f --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -0,0 +1,39 @@ +/// 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 { + "a".to_string() +} + +/// This is the default sort text. +pub(super) fn default_sort_text() -> String { + "b".to_string() +} + +/// We want crates and modules to show up after other things (for example +/// local variables, functions or types) +pub(super) fn crate_or_module_sort_text() -> String { + "c".to_string() +} + +/// Sort text for auto-import items. We want these to show up after local definitions. +pub(super) fn auto_import_sort_text() -> String { + "d".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 { + "e".to_string() +} + +/// We want to show operator methods last. +pub(super) fn operator_sort_text() -> String { + "f".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 { + "g".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..8cf06adc027 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -0,0 +1,1641 @@ +#[cfg(test)] +mod completion_tests { + use crate::{ + notifications::on_did_open_text_document, + requests::{ + completion::{ + completion_items::{ + completion_item_with_sort_text, + completion_item_with_trigger_parameter_hints_command, crate_completion_item, + field_completion_item, module_completion_item, simple_completion_item, + snippet_completion_item, + }, + sort_text::{auto_import_sort_text, self_mismatch_sort_text}, + }, + on_completion_request, + }, + test_utils, + }; + + use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, + CompletionResponse, DidOpenTextDocumentParams, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_completions(src: &str) -> 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(), + }, + }, + ); + + 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"); + + if let Some(CompletionResponse::Array(items)) = response { + items + } else { + vec![] + } + } + + fn assert_items_match(mut items: Vec, mut expected: Vec) { + items.sort_by_key(|item| item.label.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); + } + + async fn assert_completion(src: &str, expected: Vec) { + let items = get_completions(src).await; + assert_items_match(items, expected); + } + + async fn assert_completion_excluding_auto_import(src: &str, expected: Vec) { + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.additional_text_edits.is_none()).collect(); + assert_items_match(items, expected); + } + + pub(super) fn function_completion_item( + label: impl Into, + insert_text: impl Into, + description: impl Into, + ) -> CompletionItem { + completion_item_with_trigger_parameter_hints_command(snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + Some(description.into()), + )) + } + + #[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 qux {} + } + + fn main() { + foo::q>|< + } + "#; + assert_completion(src, vec![module_completion_item("qux")]).await; + } + + #[test] + async fn test_complete_path_with_local_variable() { + let src = r#" + fn main() { + let local = 1; + l>|< + } + "#; + assert_completion_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + src, + vec![function_completion_item("hello()", "hello()", "fn()")], + ) + .await; + } + + #[test] + async fn test_complete_function() { + let src = r#" + fn hello(x: i32, y: Field) { } + + fn main() { + h>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "hello(…)", + "hello(${1:x}, ${2:y})", + "fn(i32, Field)".to_string(), + )], + ) + .await; + } + + #[test] + async fn test_complete_builtin_functions() { + let src = r#" + fn main() { + a>|< + } + "#; + assert_completion_excluding_auto_import( + src, + vec![ + snippet_completion_item( + "assert(…)", + CompletionItemKind::FUNCTION, + "assert(${1:predicate})", + Some("fn(T)".to_string()), + ), + function_completion_item("assert_constant(…)", "assert_constant(${1:x})", "fn(T)"), + 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() { + So>|< + } + } + "#; + assert_completion_excluding_auto_import( + 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() { + So>|< + } + } + "#; + assert_completion_excluding_auto_import( + 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 { + ind>|< + } + } + "#; + assert_completion_excluding_auto_import( + 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(|lambda_var| lambda_v>|<) + } + "#; + assert_completion_excluding_auto_import( + src, + vec![simple_completion_item( + "lambda_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>|<) {} + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.label.starts_with('i')).collect(); + + assert_items_match( + items, + 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())), + ], + ); + } + + #[test] + async fn test_suggest_true() { + let src = r#" + fn main() { + let x = t>|< + } + "#; + assert_completion_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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_excluding_auto_import( + 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: Cont>|< + } + "#; + assert_completion_excluding_auto_import( + 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: Cont>|<) {} + "#; + assert_completion_excluding_auto_import( + 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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("property", "i32")]).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![field_completion_item("bar", "i32")]).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![ + function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)"), + function_completion_item("foobar2(…)", "foobar2(${1:x})", "fn(&mut self, i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![function_completion_item("foobar(…)", "foobar(${1:x})", "fn(self, i32)")], + ) + .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( + function_completion_item( + "foobar(…)", + "foobar(${1:self}, ${2:x})", + "fn(self, i32)", + ), + self_mismatch_sort_text(), + ), + completion_item_with_sort_text( + function_completion_item( + "foobar2(…)", + "foobar2(${1:self}, ${2:x})", + "fn(&mut self, i32)", + ), + self_mismatch_sort_text(), + ), + function_completion_item("foobar3(…)", "foobar3(${1:y})", "fn(i32)"), + ], + ) + .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![field_completion_item("foo", "i32")]).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![field_completion_item("bar", "Bar")]).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![function_completion_item("foo()", "foo()", "fn(self) -> Foo")]) + .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![field_completion_item("bar", "i32")]).await; + } + + #[test] + async fn test_completes_tuple_fields() { + let src = r#" + fn main() { + let tuple = (1, true); + tuple.>|< + } + "#; + + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.kind == Some(CompletionItemKind::FIELD)); + let items = items.collect(); + + assert_items_match( + items, + vec![field_completion_item("0", "Field"), field_completion_item("1", "bool")], + ); + } + + #[test] + async fn test_completes_constructor_fields() { + let src = r#" + mod foobar { + struct Foo { + bb: i32, + bbb: Field, + bbbb: bool, + bbbbb: str<6>, + } + } + + fn main() { + foobar::Foo { bbb: 1, b>|<, bbbbb } + } + "#; + assert_completion( + src, + vec![field_completion_item("bb", "i32"), field_completion_item("bbbb", "bool")], + ) + .await; + } + + #[test] + async fn test_completes_trait_methods() { + let src = r#" + trait One { + fn one() -> Self; + } + + fn main() { + One::>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) + .await; + } + + #[test] + async fn test_auto_imports() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + + struct Foo { + } + + impl Foo { + // This is here to make sure it's not offered for completion + fn hello_world() {} + } + } + } + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 0, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + + assert_eq!(item.sort_text, Some(auto_import_sort_text())); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + fn foo() { + hel>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 2, character: 4 }, + end: Position { line: 2, character: 4 }, + }, + new_text: "use bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_not_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + mod baz { + fn foo() { + hel>|< + } + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use super::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 7, character: 8 }, + end: Position { line: 7, character: 8 }, + }, + new_text: "use super::bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_import_inserts_after_last_use() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 8, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + } + + #[test] + async fn test_does_not_auto_import_test_functions() { + let src = r#" + mod foo { + mod bar { + #[test] + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_does_not_auto_import_private_functions() { + let src = r#" + mod foo { + mod bar { + fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_auto_import_suggests_modules_too() { + let src = r#" + mod foo { + mod barbaz { + fn hello_world() {} + } + } + + fn main() { + barb>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "barbaz"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::barbaz)".to_string()), + description: None + }) + ); + } + + #[test] + async fn test_completes_matching_any_part_of_an_identifier_by_underscore() { + let src = r#" + struct Foo { + some_property: i32, + } + + fn foo(f: Foo) { + f.prop>|< + } + "#; + assert_completion(src, vec![field_completion_item("some_property", "i32")]).await; + } + + #[test] + async fn test_completes_in_impl_type() { + let src = r#" + struct FooBar { + } + + impl FooB>|< + "#; + + assert_completion( + src, + vec![simple_completion_item( + "FooBar", + CompletionItemKind::STRUCT, + Some("FooBar".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_completes_in_impl_for_type() { + let src = r#" + struct FooBar { + } + + impl Default for FooB>|< + "#; + + assert_completion( + src, + vec![simple_completion_item( + "FooBar", + CompletionItemKind::STRUCT, + Some("FooBar".to_string()), + )], + ) + .await; + } + + #[test] + async fn test_auto_import_with_super() { + let src = r#" + pub fn bar_baz() {} + + mod tests { + fn foo() { + bar_b>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "bar_baz()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use super::bar_baz)".to_string()), + description: Some("fn()".to_string()) + }) + ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs new file mode 100644 index 00000000000..b487c8baf36 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/traversal.rs @@ -0,0 +1,132 @@ +/// This file includes the completion logic that's just about +/// traversing the AST without any additional logic. +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, CallExpression, CastExpression, ConstrainStatement, + Expression, ForRange, FunctionReturnType, IndexExpression, InfixExpression, Literal, + MethodCallExpression, NoirTrait, NoirTypeAlias, TraitImplItem, UnresolvedType, + }, + ParsedModule, +}; + +use super::NodeFinder; + +impl<'a> NodeFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + } + + pub(super) fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { + for unresolved_type in unresolved_type { + self.find_in_unresolved_type(unresolved_type); + } + } + + pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { + match return_type { + noirc_frontend::ast::FunctionReturnType::Default(_) => (), + noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { + self.find_in_unresolved_type(unresolved_type); + } + } + } + + pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { + self.find_in_unresolved_type(&noir_type_alias.typ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index b6fdc6f7842..95d8b82f84f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -319,6 +319,21 @@ fn format_parent_module_from_module_id( args: &ProcessRequestCallbackArgs, string: &mut String, ) -> bool { + let mut segments: Vec<&str> = Vec::new(); + + if let Some(module_attributes) = args.interner.try_module_attributes(module) { + segments.push(&module_attributes.name); + + let mut current_attributes = module_attributes; + while let Some(parent_attributes) = args.interner.try_module_attributes(&ModuleId { + krate: module.krate, + local_id: current_attributes.parent, + }) { + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + } + let crate_id = module.krate; let crate_name = match crate_id { CrateId::Root(_) => Some(args.crate_name.clone()), @@ -328,44 +343,21 @@ fn format_parent_module_from_module_id( .find(|dep| dep.crate_id == crate_id) .map(|dep| format!("{}", dep.name)), CrateId::Stdlib(_) => Some("std".to_string()), - CrateId::Dummy => None, + CrateId::Dummy => unreachable!("ICE: A dummy CrateId should not be accessible"), }; - let wrote_crate = if let Some(crate_name) = crate_name { - string.push_str(" "); - string.push_str(&crate_name); - true - } else { - false - }; - - let Some(module_attributes) = args.interner.try_module_attributes(module) else { - return wrote_crate; + if let Some(crate_name) = &crate_name { + segments.push(crate_name); }; - if wrote_crate { - string.push_str("::"); - } else { - string.push_str(" "); - } - - let mut segments = Vec::new(); - let mut current_attributes = module_attributes; - while let Some(parent_attributes) = args.interner.try_module_attributes(&ModuleId { - krate: module.krate, - local_id: current_attributes.parent, - }) { - segments.push(&parent_attributes.name); - current_attributes = parent_attributes; - } - - for segment in segments.iter().rev() { - string.push_str(segment); - string.push_str("::"); + if segments.is_empty() { + return false; } - string.push_str(&module_attributes.name); + segments.reverse(); + string.push_str(" "); + string.push_str(&segments.join("::")); true } @@ -431,7 +423,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..a1e083187d3 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, } } @@ -714,9 +722,9 @@ mod inlay_hints_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: start_line, character: 0 }, - end: lsp_types::Position { line: end_line, character: 0 }, + range: Range { + start: Position { line: start_line, character: 0 }, + end: Position { line: end_line, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index e138f839600..e88c7f11465 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -46,6 +46,7 @@ mod inlay_hint; mod profile_run; mod references; mod rename; +mod signature_help; mod test_run; mod tests; @@ -56,7 +57,8 @@ pub(crate) use { goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + rename::on_rename_request, signature_help::on_signature_help_request, + test_run::on_test_run_request, tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -234,13 +236,22 @@ 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, }, completion_item: None, })), + signature_help_provider: Some(lsp_types::OneOf::Right( + lsp_types::SignatureHelpOptions { + trigger_characters: Some(vec!["(".to_string(), ",".to_string()]), + retrigger_characters: None, + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs new file mode 100644 index 00000000000..c2c69185547 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -0,0 +1,291 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, PathString}; +use lsp_types::{ + ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, +}; +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + hir_def::{function::FuncMeta, stmt::HirPattern}, + macros_api::NodeInterner, + node_interner::ReferenceId, + ParsedModule, Type, +}; + +use crate::{utils, LspState}; + +use super::process_request; + +mod tests; +mod traversal; + +pub(crate) fn on_signature_help_request( + state: &mut LspState, + params: SignatureHelpParams, +) -> impl Future, ResponseError>> { + let uri = params.text_document_position_params.clone().text_document.uri; + + let result = process_request(state, params.text_document_position_params.clone(), |args| { + let path = PathString::from_path(uri.to_file_path().unwrap()); + args.files.get_file_id(&path).and_then(|file_id| { + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) + }) + }) + }); + future::ready(result) +} + +struct SignatureFinder<'a> { + file: FileId, + byte_index: usize, + interner: &'a NodeInterner, + signature_help: Option, +} + +impl<'a> SignatureFinder<'a> { + fn new(file: FileId, byte_index: usize, interner: &'a NodeInterner) -> Self { + Self { file, byte_index, interner, signature_help: None } + } + + fn find(&mut self, parsed_module: &ParsedModule) -> Option { + self.find_in_parsed_module(parsed_module); + + self.signature_help.clone() + } + + fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { + self.find_in_expression(&call_expression.func); + self.find_in_expressions(&call_expression.arguments); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn find_in_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) { + self.find_in_expression(&method_call_expression.object); + self.find_in_expressions(&method_call_expression.arguments); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + } + + fn try_compute_signature_help( + &mut self, + arguments: &[Expression], + arguments_span: Span, + name_span: Span, + has_self: bool, + ) { + if self.signature_help.is_some() { + return; + } + + if !self.includes_span(arguments_span) { + return; + } + + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + let location = Location::new(name_span, self.file); + + // Check if the call references a named function + if let Some(ReferenceId::Function(func_id)) = self.interner.find_referenced(location) { + let name = self.interner.function_name(&func_id); + let func_meta = self.interner.function_meta(&func_id); + + let signature_information = + self.func_meta_signature_information(func_meta, name, active_parameter, has_self); + self.set_signature_help(signature_information); + return; + } + + // Otherwise, the call must be a reference to an fn type + if let Some(mut typ) = self.interner.type_at_location(location) { + if let Type::Forall(_, forall_typ) = typ { + typ = *forall_typ; + } + if let Type::Function(args, return_type, _, unconstrained) = typ { + let signature_information = self.function_type_signature_information( + &args, + &return_type, + unconstrained, + active_parameter, + ); + self.set_signature_help(signature_information); + } + } + } + + fn func_meta_signature_information( + &self, + func_meta: &FuncMeta, + name: &str, + active_parameter: Option, + has_self: bool, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str("fn "); + label.push_str(name); + label.push('('); + for (index, (pattern, typ, _)) in func_meta.parameters.0.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + if has_self && index == 0 { + if let Type::MutableReference(..) = typ { + label.push_str("&mut self"); + } else { + label.push_str("self"); + } + } else { + let parameter_start = label.chars().count(); + + self.hir_pattern_to_argument(pattern, &mut label); + label.push_str(": "); + label.push_str(&typ.to_string()); + + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([ + parameter_start as u32, + parameter_end as u32, + ]), + documentation: None, + }); + } + } + label.push(')'); + + match &func_meta.return_type { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(typ) => { + label.push_str(" -> "); + label.push_str(&typ.to_string()); + } + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + fn function_type_signature_information( + &self, + args: &[Type], + return_type: &Type, + unconstrained: bool, + active_parameter: Option, + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + if unconstrained { + label.push_str("unconstrained "); + } + label.push_str("fn("); + for (index, typ) in args.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(&typ.to_string()); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + if let Type::Unit = return_type { + // Nothing + } else { + label.push_str(" -> "); + label.push_str(&return_type.to_string()); + } + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + + 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 set_signature_help(&mut self, signature_information: SignatureInformation) { + let signature_help = SignatureHelp { + active_parameter: signature_information.active_parameter, + signatures: vec![signature_information], + active_signature: Some(0), + }; + self.signature_help = Some(signature_help); + } + + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs new file mode 100644 index 00000000000..c48ee159084 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -0,0 +1,196 @@ +#[cfg(test)] +mod signature_help_tests { + use crate::{ + notifications::on_did_open_text_document, requests::on_signature_help_request, test_utils, + }; + + use lsp_types::{ + DidOpenTextDocumentParams, ParameterLabel, Position, SignatureHelp, SignatureHelpParams, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, + }; + use tokio::test; + + async fn get_signature_help(src: &str) -> SignatureHelp { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let (line, column) = src + .lines() + .enumerate() + .find_map(|(line_index, line)| { + line.find(">|<").map(|char_index| (line_index, char_index)) + }) + .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(), + }, + }, + ); + + on_signature_help_request( + &mut state, + SignatureHelpParams { + context: None, + text_document_position_params: 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 }, + }, + ) + .await + .expect("Could not execute on_signature_help_request") + .unwrap() + } + + fn check_label(signature_label: &str, parameter_label: &ParameterLabel, expected_string: &str) { + let ParameterLabel::LabelOffsets(offsets) = parameter_label else { + panic!("Expected label to be LabelOffsets, got {:?}", parameter_label); + }; + + assert_eq!(&signature_label[offsets[0] as usize..offsets[1] as usize], expected_string); + } + + #[test] + async fn test_signature_help_for_call_at_first_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + fn wrapper(x: u32) {} + + fn bar() { + wrapper(foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_call_between_arguments() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1,>|< 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_at_second_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, >|<2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(1)); + } + + #[test] + async fn test_signature_help_for_call_past_last_argument() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + foo(1, 2, >|<); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.active_parameter, Some(2)); + } + + #[test] + async fn test_signature_help_for_method_call() { + let src = r#" + struct Foo {} + + impl Foo { + fn foo(self, x: i32, y: Field) -> u32 { 0 } + } + + fn wrapper(x: u32) {} + + fn bar(f: Foo) { + wrapper(f.foo(>|<1, 2)); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn foo(self, x: i32, y: Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "x: i32"); + check_label(&signature.label, ¶ms[1].label, "y: Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_fn_call() { + let src = r#" + fn foo(x: i32, y: Field) -> u32 { 0 } + + fn bar() { + let f = foo; + f(>|<1, 2); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "fn(i32, Field) -> u32"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "i32"); + check_label(&signature.label, ¶ms[1].label, "Field"); + + assert_eq!(signature.active_parameter, Some(0)); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs new file mode 100644 index 00000000000..22f92a86124 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs @@ -0,0 +1,304 @@ +/// This file includes the signature help logic that's just about +/// traversing the AST without any additional logic. +use super::SignatureFinder; + +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, + ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, + IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, + TraitImplItem, TraitItem, TypeImpl, + }, + parser::{Item, ItemKind}, + ParsedModule, +}; + +impl<'a> SignatureFinder<'a> { + pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { + for item in &parsed_module.items { + self.find_in_item(item); + } + } + + pub(super) fn find_in_item(&mut self, item: &Item) { + if !self.includes_span(item.span) { + return; + } + + match &item.kind { + ItemKind::Submodules(parsed_sub_module) => { + self.find_in_parsed_module(&parsed_sub_module.contents); + } + ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), + ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), + ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), + ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), + ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), + ItemKind::Import(..) + | ItemKind::TypeAlias(_) + | ItemKind::Struct(_) + | ItemKind::ModuleDecl(_) => (), + } + } + + pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { + self.find_in_block_expression(&noir_function.def.body); + } + + pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { + for item in &noir_trait_impl.items { + self.find_in_trait_impl_item(item); + } + } + + pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { + match item { + TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), + TraitImplItem::Constant(_, _, _) => (), + TraitImplItem::Type { .. } => (), + } + } + + pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { + for (method, span) in &type_impl.methods { + if self.includes_span(*span) { + self.find_in_noir_function(method); + } + } + } + + pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { + for item in &noir_trait.items { + self.find_in_trait_item(item); + } + } + + pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { + match trait_item { + TraitItem::Function { body, .. } => { + if let Some(body) = body { + self.find_in_block_expression(body); + }; + } + TraitItem::Constant { default_value, .. } => { + if let Some(default_value) = default_value { + self.find_in_expression(default_value); + } + } + TraitItem::Type { .. } => (), + } + } + + pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { + for statement in &block_expression.statements { + if self.includes_span(statement.span) { + self.find_in_statement(statement); + } + } + } + + pub(super) fn find_in_statement(&mut self, statement: &Statement) { + if !self.includes_span(statement.span) { + return; + } + + match &statement.kind { + StatementKind::Let(let_statement) => { + self.find_in_let_statement(let_statement); + } + StatementKind::Constrain(constrain_statement) => { + self.find_in_constrain_statement(constrain_statement); + } + StatementKind::Expression(expression) => { + self.find_in_expression(expression); + } + StatementKind::Assign(assign_statement) => { + self.find_in_assign_statement(assign_statement); + } + StatementKind::For(for_loop_statement) => { + self.find_in_for_loop_statement(for_loop_statement); + } + StatementKind::Comptime(statement) => { + self.find_in_statement(statement); + } + StatementKind::Semi(expression) => { + self.find_in_expression(expression); + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } + } + + pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { + self.find_in_expression(&let_statement.expression); + } + + pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { + self.find_in_expression(&constrain_statement.0); + + if let Some(exp) = &constrain_statement.1 { + self.find_in_expression(exp); + } + } + + pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { + self.find_in_lvalue(&assign_statement.lvalue); + self.find_in_expression(&assign_statement.expression); + } + + pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { + self.find_in_for_range(&for_loop_statement.range); + self.find_in_expression(&for_loop_statement.block); + } + + pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { + match lvalue { + LValue::Ident(_) => (), + LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), + LValue::Index { array, index, span: _ } => { + self.find_in_lvalue(array); + self.find_in_expression(index); + } + LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), + } + } + + pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { + match for_range { + ForRange::Range(start, end) => { + self.find_in_expression(start); + self.find_in_expression(end); + } + ForRange::Array(expression) => self.find_in_expression(expression), + } + } + + pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { + for expression in expressions { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_expression(&mut self, expression: &Expression) { + match &expression.kind { + ExpressionKind::Literal(literal) => self.find_in_literal(literal), + ExpressionKind::Block(block_expression) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Prefix(prefix_expression) => { + self.find_in_expression(&prefix_expression.rhs); + } + ExpressionKind::Index(index_expression) => { + self.find_in_index_expression(index_expression); + } + ExpressionKind::Call(call_expression) => { + self.find_in_call_expression(call_expression, expression.span); + } + ExpressionKind::MethodCall(method_call_expression) => { + self.find_in_method_call_expression(method_call_expression, expression.span); + } + ExpressionKind::Constructor(constructor_expression) => { + self.find_in_constructor_expression(constructor_expression); + } + ExpressionKind::MemberAccess(member_access_expression) => { + self.find_in_member_access_expression(member_access_expression); + } + ExpressionKind::Cast(cast_expression) => { + self.find_in_cast_expression(cast_expression); + } + ExpressionKind::Infix(infix_expression) => { + self.find_in_infix_expression(infix_expression); + } + ExpressionKind::If(if_expression) => { + self.find_in_if_expression(if_expression); + } + ExpressionKind::Tuple(expressions) => { + self.find_in_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), + ExpressionKind::Parenthesized(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Unquote(expression) => { + self.find_in_expression(expression); + } + ExpressionKind::Comptime(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Unsafe(block_expression, _) => { + self.find_in_block_expression(block_expression); + } + ExpressionKind::Variable(_) + | ExpressionKind::AsTraitPath(_) + | ExpressionKind::Quote(_) + | ExpressionKind::Resolved(_) + | ExpressionKind::Error => (), + } + } + + pub(super) fn find_in_literal(&mut self, literal: &Literal) { + match literal { + Literal::Array(array_literal) => self.find_in_array_literal(array_literal), + Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } + } + + pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), + ArrayLiteral::Repeated { repeated_element, length } => { + self.find_in_expression(repeated_element); + self.find_in_expression(length); + } + } + } + + pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { + self.find_in_expression(&index_expression.collection); + self.find_in_expression(&index_expression.index); + } + + pub(super) fn find_in_constructor_expression( + &mut self, + constructor_expression: &ConstructorExpression, + ) { + for (_field_name, expression) in &constructor_expression.fields { + self.find_in_expression(expression); + } + } + + pub(super) fn find_in_member_access_expression( + &mut self, + member_access_expression: &MemberAccessExpression, + ) { + self.find_in_expression(&member_access_expression.lhs); + } + + pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { + self.find_in_expression(&cast_expression.lhs); + } + + pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { + self.find_in_expression(&infix_expression.lhs); + self.find_in_expression(&infix_expression.rhs); + } + + pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { + self.find_in_expression(&if_expression.condition); + self.find_in_expression(&if_expression.consequence); + + if let Some(alternative) = &if_expression.alternative { + self.find_in_expression(alternative); + } + } + + pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { + self.find_in_expression(&lambda.body); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 5afda0d292a..3ac1f35e18e 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ CompletionOptions, DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, - HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, + HoverOptions, InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, SignatureHelpOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; @@ -161,6 +161,10 @@ pub(crate) struct ServerCapabilities { /// The server provides completion support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) completion_provider: Option>, + + /// The server provides signature help support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) signature_help_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 56e88dacf2d..046eca88099 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/nargo/src/ops/execute.rs b/noir/noir-repo/tooling/nargo/src/ops/execute.rs index c9cc60d03d9..2e214c4e425 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/execute.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/execute.rs @@ -139,9 +139,9 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> }); // Set current function to the circuit we are about to execute - self.current_function_index = call_info.id as usize; + self.current_function_index = call_info.id.as_usize(); // Execute the ACIR call - let acir_to_call = &self.functions[call_info.id as usize]; + let acir_to_call = &self.functions[call_info.id.as_usize()]; let initial_witness = call_info.initial_witness; let call_solved_witness = self.execute_circuit(initial_witness)?; @@ -163,7 +163,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> } } acvm.resolve_pending_acir_call(call_resolved_outputs); - self.witness_stack.push(call_info.id, call_solved_witness); + self.witness_stack.push(call_info.id.0, call_solved_witness); } } } diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index dabb779ae8d..f3d9f92caaa 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -8,6 +8,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `nargo_cli` to `nargo` diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index 3f8cd055569..4dcfccdf085 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -207,7 +207,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ panic!("JSON was not well-formatted {:?}\n\n{:?}", e, std::str::from_utf8(&output.stdout)) }}); - let num_opcodes = &json["programs"][0]["functions"][0]["acir_opcodes"]; + let num_opcodes = &json["programs"][0]["functions"][0]["opcodes"]; assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); "#; diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index a6395d1c8c9..7e4a8b4117d 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -100,8 +100,7 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = - table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Brillig Opcodes"]); for program_info in info_report.programs { let program_rows: Vec = program_info.into(); @@ -176,18 +175,32 @@ struct ProgramInfo { #[serde(skip)] expression_width: ExpressionWidth, functions: Vec, + #[serde(skip)] + unconstrained_functions_opcodes: usize, + unconstrained_functions: Vec, } impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { + let mut main = vecmap(program_info.functions, |function| { row![ Fm->format!("{}", program_info.package_name), Fc->format!("{}", function.name), format!("{:?}", program_info.expression_width), - Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", function.opcodes), + Fc->format!("{}", program_info.unconstrained_functions_opcodes), ] - }) + }); + main.extend(vecmap(program_info.unconstrained_functions, |function| { + row![ + Fm->format!("{}", program_info.package_name), + Fc->format!("{}", function.name), + format!("N/A", ), + Fc->format!("N/A"), + Fc->format!("{}", function.opcodes), + ] + })); + main } } @@ -203,7 +216,7 @@ struct ContractInfo { #[derive(Debug, Serialize)] struct FunctionInfo { name: String, - acir_opcodes: usize, + opcodes: usize, } impl From for Vec { @@ -213,7 +226,7 @@ impl From for Vec { Fm->format!("{}", contract_info.name), Fc->format!("{}", function.name), format!("{:?}", contract_info.expression_width), - Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", function.opcodes), ] }) } @@ -231,9 +244,35 @@ fn count_opcodes_and_gates_in_program( .enumerate() .map(|(i, function)| FunctionInfo { name: compiled_program.names[i].clone(), - acir_opcodes: function.opcodes.len(), + opcodes: function.opcodes.len(), }) .collect(); - ProgramInfo { package_name: package.name.to_string(), expression_width, functions } + let opcodes_len: Vec = compiled_program + .bytecode + .unconstrained_functions + .iter() + .map(|func| func.bytecode.len()) + .collect(); + let unconstrained_functions_opcodes = compiled_program + .bytecode + .unconstrained_functions + .into_par_iter() + .map(|function| function.bytecode.len()) + .sum(); + let unconstrained_info: Vec = compiled_program + .brillig_names + .clone() + .iter() + .zip(opcodes_len) + .map(|(name, len)| FunctionInfo { name: name.clone(), opcodes: len }) + .collect(); + + ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions, + unconstrained_functions_opcodes, + unconstrained_functions: unconstrained_info, + } } diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 05b2fdb7d52..9868f259097 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] bytecount = "0.6.3" noirc_frontend.workspace = true 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/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index 7c9faa4562a..e4766e44859 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] 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/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index 4c0c1f75e42..a7baf334bff 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs index aecb620b79d..b79f232d9e8 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs @@ -107,10 +107,8 @@ pub(super) fn arb_abi_type() -> BoxedStrategy { (1..10u32, inner.clone()) .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) .boxed(), - prop::collection::vec(inner.clone(), 1..10) - .prop_map(|fields| { AbiType::Tuple { fields } }) - .boxed(), - (".*", prop::collection::vec((".+", inner), 1..10)) + vec(inner.clone(), 1..10).prop_map(|fields| { AbiType::Tuple { fields } }).boxed(), + (".*", vec((".+", inner), 1..10)) .prop_map(|(path, mut fields)| { // Require that all field names are unique. ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); @@ -141,7 +139,7 @@ fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { prop_compose! { pub(super) fn arb_abi_and_input_map() - (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + (mut parameters_with_values in vec(arb_abi_param_and_value(), 0..100), return_type: Option) -> (Abi, InputMap) { // Require that all parameter names are unique. ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); 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/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index ef4a468b661..b0c11979d17 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -121,8 +121,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result::from_serde(&return_struct) - .map_err(|err| err.to_string().into()) + ::from_serde(&return_struct).map_err(|err| err.to_string().into()) } #[wasm_bindgen(js_name = serializeWitness)] @@ -155,7 +154,7 @@ pub fn abi_decode_error( AbiErrorType::Custom(typ) => { let input_value = decode_value(&mut raw_error.data.into_iter(), &typ)?; let json_types = JsonTypes::try_from_input_value(&input_value, &typ)?; - ::from_serde(&json_types) + ::from_serde(&json_types) .map_err(|err| err.to_string().into()) } } diff --git a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml index 4249604f949..13ff68e423a 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs index 91f02157414..ffffc6345d5 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/src/program.rs +++ b/noir/noir-repo/tooling/noirc_artifacts/src/program.rs @@ -37,6 +37,8 @@ pub struct ProgramArtifact { pub file_map: BTreeMap, pub names: Vec, + /// Names of the unconstrained functions in the program. + pub brillig_names: Vec, } impl From for ProgramArtifact { @@ -49,6 +51,7 @@ impl From for ProgramArtifact { debug_symbols: ProgramDebugInfo { debug_infos: compiled_program.debug }, file_map: compiled_program.file_map, names: compiled_program.names, + brillig_names: compiled_program.brillig_names, } } } @@ -64,6 +67,7 @@ impl From for CompiledProgram { file_map: program.file_map, warnings: vec![], names: program.names, + brillig_names: program.brillig_names, } } } diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 0ccd56b791f..136775d5831 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index e072c63f274..0fa12239d05 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -128,7 +128,7 @@ mod tests { } impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { + fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let response = self .mock_responses .get(artifact_path) @@ -173,6 +173,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index f91e50d8716..d7f3cbb9b85 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -188,6 +188,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: Vec::new(), }; // Write the artifact to a file @@ -228,6 +229,7 @@ mod tests { debug_symbols: ProgramDebugInfo { debug_infos: vec![DebugInfo::default()] }, file_map: BTreeMap::default(), names: vec!["main".to_string()], + brillig_names: vec!["main".to_string()], }; // Write the artifact to a file diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index 772c87ad1cb..edb3e1b0f08 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -100,7 +100,7 @@ fn format_binary_field_op(op: &BinaryFieldOp) -> String { } } -fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { +fn format_binary_int(op: &BinaryIntOp) -> String { match op { BinaryIntOp::Add => "add".to_string(), BinaryIntOp::Sub => "sub".to_string(), 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 From dfbd8b1cd045a36dd6ce01a57fbb410f8294db8c Mon Sep 17 00:00:00 2001 From: AztecBot Date: Tue, 20 Aug 2024 18:43:46 +0000 Subject: [PATCH 19/21] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 650298f4f7d..a758a09aae5 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.47.1 +0.48.0 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 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/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[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 new file mode 100644 index 00000000000..6e5c36559bd --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000000..10ea4577750 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// 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/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 661d71fb9c3..a5593cc284c 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": "0.48.0", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@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 16fb26e55db..c07d2d8a4c1 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 f10b5b2cb67..f77e9f7e72e 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.48.0": - version: 0.48.0 - resolution: "@aztec/bb.js@npm:0.48.0" +"@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" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: e06b864a5acea4299dfa350f732dd05a807968678fd3bc3b9c699f9bc50aef1525e2492dfacf9965270082c23b04653f55c40a34f75ead11a52e3fc5512ddce7 + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@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 @@ -4161,7 +4160,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.48.0 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 7fd99bb5d8b794ce27eb14004d1c6b69ff96e97b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 20 Aug 2024 18:46:50 +0000 Subject: [PATCH 20/21] upda BITSIZE_TOO_BIG_ERROR --- yarn-project/end-to-end/src/fixtures/fixtures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/end-to-end/src/fixtures/fixtures.ts b/yarn-project/end-to-end/src/fixtures/fixtures.ts index 25e9922b5cb..1aa0c265a03 100644 --- a/yarn-project/end-to-end/src/fixtures/fixtures.ts +++ b/yarn-project/end-to-end/src/fixtures/fixtures.ts @@ -5,7 +5,7 @@ export const privateKey2 = Buffer.from('59c6995e998f97a5a0044966f0945389dc9e86da /// Common errors export const U128_UNDERFLOW_ERROR = "Assertion failed: attempt to subtract with underflow 'hi == high'"; export const U128_OVERFLOW_ERROR = "Assertion failed: attempt to add with overflow 'hi == high'"; -export const BITSIZE_TOO_BIG_ERROR = "'self.__assert_max_bit_size(bit_size)'"; +export const BITSIZE_TOO_BIG_ERROR = "Assertion failed. 'self.__assert_max_bit_size'"; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/5818): Make these a fixed error after transition. export const DUPLICATE_NULLIFIER_ERROR = /dropped|duplicate nullifier|reverted/; export const NO_L1_TO_L2_MSG_ERROR = From 9303436491e3f85d9b4f5ee6a932b7e6c7342f9e Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Tue, 20 Aug 2024 18:56:22 +0000 Subject: [PATCH 21/21] . --- .../execution_success/verify_honk_proof/src/main.nr | 7 +------ noir/verify_honk_proof/src/main.nr | 5 +++-- 2 files changed, 4 insertions(+), 8 deletions(-) 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 index 667ed25f70b..d785dabffb0 100644 --- 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 @@ -13,10 +13,5 @@ fn main( // I believe we want to eventually make it public too though. key_hash: Field ) { - std::verify_proof( - verification_key, - proof, - public_inputs, - key_hash - ); + std::verify_proof(verification_key, proof, public_inputs, key_hash); } diff --git a/noir/verify_honk_proof/src/main.nr b/noir/verify_honk_proof/src/main.nr index 672e3f4f9dc..673290ed171 100644 --- a/noir/verify_honk_proof/src/main.nr +++ b/noir/verify_honk_proof/src/main.nr @@ -1,16 +1,17 @@ // This circuit aggregates a single Honk proof from `assert_statement_recursive`. global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 409; +global HONK_IDENTIFIER : u32 = 1; 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// WORKTODO: Can we delete this program? noir needed it for some reason but its not used in bberg + key_hash: Field ) { std::verify_proof(verification_key, proof, public_inputs, key_hash); }