diff --git a/.noir-sync-commit b/.noir-sync-commit index 19e9a0d3012..bf36994bc1b 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -0cf2e2a1b8d247bed03ba5b7b1be5cd30f0d51b2 +7ea0e914625789e65b0d5908f7f0b76a9fd32652 diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index e145398e26e..6959e781579 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -159,10 +159,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "arena" -version = "0.28.0" - [[package]] name = "ark-bn254" version = "0.4.0" @@ -1210,6 +1206,10 @@ dependencies = [ "toml", ] +[[package]] +name = "noirc_arena" +version = "0.28.0" + [[package]] name = "noirc_driver" version = "0.28.0" @@ -1271,13 +1271,13 @@ name = "noirc_frontend" version = "0.28.0" dependencies = [ "acvm", - "arena", "chumsky", "fm", "im", "iter-extended", "lalrpop", "lalrpop-util", + "noirc_arena", "noirc_errors", "noirc_printable_type", "petgraph", diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 8a8ccfdbf8a..eaed1c08bec 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -233,10 +233,6 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -[[package]] -name = "arena" -version = "0.28.0" - [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -3099,6 +3095,10 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "noirc_arena" +version = "0.28.0" + [[package]] name = "noirc_driver" version = "0.28.0" @@ -3160,7 +3160,6 @@ name = "noirc_frontend" version = "0.28.0" dependencies = [ "acvm", - "arena", "base64 0.21.2", "chumsky", "fm", @@ -3168,6 +3167,7 @@ dependencies = [ "iter-extended", "lalrpop", "lalrpop-util", + "noirc_arena", "noirc_errors", "noirc_printable_type", "petgraph", diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 108d179b9ca..ed0aef0d35b 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -4,6 +4,7 @@ members = [ # Aztec Macro crate for metaprogramming "aztec_macros", # Compiler crates + "compiler/noirc_arena", "compiler/noirc_evaluator", "compiler/noirc_frontend", "compiler/noirc_errors", @@ -11,10 +12,7 @@ members = [ "compiler/noirc_printable_type", "compiler/fm", "compiler/wasm", - # Utility crates used by the Noir compiler - "compiler/utils/arena", - "compiler/utils/iter-extended", - # Crates related to tooling built ontop of the Noir compiler + # Crates related to tooling built on top of the Noir compiler "tooling/backend_interface", "tooling/bb_abstraction_leaks", "tooling/lsp", @@ -35,6 +33,8 @@ members = [ "acvm-repo/brillig_vm", "acvm-repo/blackbox_solver", "acvm-repo/bn254_blackbox_solver", + # Utility crates + "utils/iter-extended", ] default-members = ["tooling/nargo_cli", "tooling/acvm_cli"] resolver = "2" @@ -61,9 +61,8 @@ acvm_blackbox_solver = { version = "0.44.0", path = "acvm-repo/blackbox_solver", bn254_blackbox_solver = { version = "0.44.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies -arena = { path = "compiler/utils/arena" } fm = { path = "compiler/fm" } -iter-extended = { path = "compiler/utils/iter-extended" } +noirc_arena = { path = "compiler/noirc_arena" } noirc_driver = { path = "compiler/noirc_driver" } noirc_errors = { path = "compiler/noirc_errors" } noirc_evaluator = { path = "compiler/noirc_evaluator" } @@ -80,6 +79,9 @@ noirc_abi = { path = "tooling/noirc_abi" } bb_abstraction_leaks = { path = "tooling/bb_abstraction_leaks" } acvm_cli = { path = "tooling/acvm_cli" } +# Misc utils crates +iter-extended = { path = "utils/iter-extended" } + # LSP async-lsp = { version = "0.1.0", default-features = false } lsp-types = "0.94.1" diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index ee93413ab85..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/compiler/utils/arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml similarity index 86% rename from noir/noir-repo/compiler/utils/arena/Cargo.toml rename to noir/noir-repo/compiler/noirc_arena/Cargo.toml index f6bd764ee62..b94f997b7b9 100644 --- a/noir/noir-repo/compiler/utils/arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "arena" +name = "noirc_arena" version.workspace = true authors.workspace = true edition.workspace = true diff --git a/noir/noir-repo/compiler/utils/arena/src/lib.rs b/noir/noir-repo/compiler/noirc_arena/src/lib.rs similarity index 100% rename from noir/noir-repo/compiler/utils/arena/src/lib.rs rename to noir/noir-repo/compiler/noirc_arena/src/lib.rs diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 3482a8c25c7..f9fd5f41f77 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -9,10 +9,7 @@ use std::collections::BTreeSet; -use crate::{ - brillig::Brillig, - errors::{RuntimeError, SsaReport}, -}; +use crate::errors::{RuntimeError, SsaReport}; use acvm::acir::{ circuit::{ brillig::BrilligBytecode, Circuit, ExpressionWidth, Program as AcirProgram, PublicInputs, @@ -318,10 +315,6 @@ impl SsaBuilder { Ok(self.print(msg)) } - fn to_brillig(&self, print_brillig_trace: bool) -> Brillig { - self.ssa.to_brillig(print_brillig_trace) - } - fn print(self, msg: &str) -> Self { if self.print_ssa_passes { println!("{msg}\n{}", self.ssa); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 0b04d1b63ab..9f305a28c25 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -420,55 +420,6 @@ impl GeneratedAcir { Ok(limb_witnesses) } - /// Returns an expression which represents `lhs * rhs` - /// - /// If one has multiplicative term and the other is of degree one or more, - /// the function creates [intermediate variables][`Witness`] accordingly. - /// There are two cases where we can optimize the multiplication between two expressions: - /// 1. If the sum of the degrees of both expressions is at most 2, then we can just multiply them - /// as each term in the result will be degree-2. - /// 2. If one expression is a constant, then we can just multiply the constant with the other expression - /// - /// (1) is because an [`Expression`] can hold at most a degree-2 univariate polynomial - /// which is what you get when you multiply two degree-1 univariate polynomials. - pub(crate) fn mul_with_witness(&mut self, lhs: &Expression, rhs: &Expression) -> Expression { - use std::borrow::Cow; - let lhs_is_linear = lhs.is_linear(); - let rhs_is_linear = rhs.is_linear(); - - // Case 1: The sum of the degrees of both expressions is at most 2. - // - // If one of the expressions is constant then it does not increase the degree when multiplying by another expression. - // If both of the expressions are linear (degree <=1) then the product will be at most degree 2. - let both_are_linear = lhs_is_linear && rhs_is_linear; - let either_is_const = lhs.is_const() || rhs.is_const(); - if both_are_linear || either_is_const { - return (lhs * rhs).expect("Both expressions are degree <= 1"); - } - - // Case 2: One or both of the sides needs to be reduced to a degree-1 univariate polynomial - let lhs_reduced = if lhs_is_linear { - Cow::Borrowed(lhs) - } else { - Cow::Owned(self.get_or_create_witness(lhs).into()) - }; - - // If the lhs and rhs are the same, then we do not need to reduce - // rhs, we only need to square the lhs. - if lhs == rhs { - return (&*lhs_reduced * &*lhs_reduced) - .expect("Both expressions are reduced to be degree <= 1"); - }; - - let rhs_reduced = if rhs_is_linear { - Cow::Borrowed(rhs) - } else { - Cow::Owned(self.get_or_create_witness(rhs).into()) - }; - - (&*lhs_reduced * &*rhs_reduced).expect("Both expressions are reduced to be degree <= 1") - } - /// Adds an inversion brillig opcode. /// /// This code will invert `expr` without applying constraints 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 ee236df8eac..6c7d78acdea 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 @@ -3277,18 +3277,15 @@ mod test { // This check right now expects to only call one Brillig function. let mut num_normal_brillig_calls = 0; for (i, opcode) in opcodes.iter().enumerate() { - match opcode { - Opcode::BrilligCall { id, .. } => { - if brillig_stdlib_function_locations.get(&OpcodeLocation::Acir(i)).is_some() { - // We should have already checked Brillig stdlib functions and only want to check normal Brillig calls here - continue; - } - // We only generate one normal Brillig call so we should expect a function ID of `0` - let expected_id = 0u32; - assert_eq!(*id, expected_id, "Expected an id of {expected_id} but got {id}"); - num_normal_brillig_calls += 1; + if let Opcode::BrilligCall { id, .. } = opcode { + if brillig_stdlib_function_locations.get(&OpcodeLocation::Acir(i)).is_some() { + // We should have already checked Brillig stdlib functions and only want to check normal Brillig calls here + continue; } - _ => {} + // We only generate one normal Brillig call so we should expect a function ID of `0` + let expected_id = 0u32; + assert_eq!(*id, expected_id, "Expected an id of {expected_id} but got {id}"); + num_normal_brillig_calls += 1; } } 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 276a8247cea..fdd17097d19 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 @@ -11,7 +11,6 @@ use noirc_frontend::monomorphization::ast::{FuncId, Program}; use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::basic_block::BasicBlockId; -use crate::ssa::ir::dfg::DataFlowGraph; use crate::ssa::ir::function::{Function, RuntimeType}; use crate::ssa::ir::function::{FunctionId as IrFunctionId, InlineType}; use crate::ssa::ir::instruction::BinaryOp; @@ -1005,72 +1004,6 @@ fn operator_requires_swapped_operands(op: BinaryOpKind) -> bool { matches!(op, Greater | LessEqual) } -/// If the operation requires its result to be truncated because it is an integer, the maximum -/// number of bits that result may occupy is returned. -fn operator_result_max_bit_size_to_truncate( - op: BinaryOpKind, - lhs: ValueId, - rhs: ValueId, - dfg: &DataFlowGraph, -) -> Option { - let lhs_type = dfg.type_of_value(lhs); - let rhs_type = dfg.type_of_value(rhs); - - let get_bit_size = |typ| match typ { - Type::Numeric(NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size }) => { - Some(bit_size) - } - _ => None, - }; - - let lhs_bit_size = get_bit_size(lhs_type)?; - let rhs_bit_size = get_bit_size(rhs_type)?; - use BinaryOpKind::*; - match op { - Add => Some(std::cmp::max(lhs_bit_size, rhs_bit_size) + 1), - Subtract => Some(std::cmp::max(lhs_bit_size, rhs_bit_size) + 1), - Multiply => { - if lhs_bit_size == 1 || rhs_bit_size == 1 { - // Truncation is unnecessary as multiplication by a boolean value cannot cause an overflow. - None - } else { - Some(lhs_bit_size + rhs_bit_size) - } - } - - ShiftLeft => { - if let Some(rhs_constant) = dfg.get_numeric_constant(rhs) { - // Happy case is that we know precisely by how many bits the the integer will - // increase: lhs_bit_size + rhs - return Some(lhs_bit_size + (rhs_constant.to_u128() as u32)); - } - // Unhappy case is that we don't yet know the rhs value, (even though it will - // eventually have to resolve to a constant). The best we can is assume the value of - // rhs to be the maximum value of it's numeric type. If that turns out to be larger - // than the native field's bit size, we full back to using that. - - // The formula for calculating the max bit size of a left shift is: - // lhs_bit_size + 2^{rhs_bit_size} - 1 - // Inferring the max bit size of left shift from its operands can result in huge - // number, that might not only be larger than the native field's max bit size, but - // furthermore might not be representable as a u32. Hence we use overflow checks and - // fallback to the native field's max bits. - let field_max_bits = FieldElement::max_num_bits(); - let (rhs_bit_size_pow_2, overflows) = 2_u32.overflowing_pow(rhs_bit_size); - if overflows { - return Some(field_max_bits); - } - let (max_bits_plus_1, overflows) = rhs_bit_size_pow_2.overflowing_add(lhs_bit_size); - if overflows { - return Some(field_max_bits); - } - let max_bit_size = std::cmp::min(max_bits_plus_1 - 1, field_max_bits); - Some(max_bit_size) - } - _ => None, - } -} - /// Converts the given operator to the appropriate BinaryOp. /// Take care when using this to insert a binary instruction: this requires /// checking operator_requires_not and operator_requires_swapped_operands diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index 84c9393fa37..0430d214d53 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -10,10 +10,10 @@ license.workspace = true [dependencies] acvm.workspace = true +noirc_arena.workspace = true noirc_errors.workspace = true noirc_printable_type.workspace = true fm.workspace = true -arena.workspace = true iter-extended.workspace = true chumsky.workspace = true thiserror.workspace = true diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs index 67d9a006b22..8de444252ab 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -1,11 +1,11 @@ -use crate::Type; +use crate::{hir::def_collector::dc_crate::CompilationError, Type}; use acvm::FieldElement; -use noirc_errors::Location; +use noirc_errors::{CustomDiagnostic, Location}; use super::value::Value; /// The possible errors that can halt the interpreter. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum InterpreterError { ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, TypeMismatch { expected: Type, value: Value, location: Location }, @@ -16,7 +16,7 @@ pub enum InterpreterError { NonBoolUsedInIf { value: Value, location: Location }, NonBoolUsedInConstrain { value: Value, location: Location }, FailingConstraint { message: Option, location: Location }, - NoMethodFound { object: Value, typ: Type, location: Location }, + NoMethodFound { name: String, typ: Type, location: Location }, NonIntegerUsedInLoop { value: Value, location: Location }, NonPointerDereferenced { value: Value, location: Location }, NonTupleOrStructInMemberAccess { value: Value, location: Location }, @@ -51,3 +51,221 @@ pub enum InterpreterError { #[allow(unused)] pub(super) type IResult = std::result::Result; + +impl InterpreterError { + pub fn into_compilation_error_pair(self) -> (CompilationError, fm::FileId) { + let location = self.get_location(); + (CompilationError::InterpreterError(self), location.file) + } + + pub fn get_location(&self) -> Location { + match self { + InterpreterError::ArgumentCountMismatch { call_location: location, .. } + | InterpreterError::TypeMismatch { location, .. } + | InterpreterError::NonComptimeVarReferenced { location, .. } + | InterpreterError::IntegerOutOfRangeForType { location, .. } + | InterpreterError::ErrorNodeEncountered { location, .. } + | InterpreterError::NonFunctionCalled { location, .. } + | InterpreterError::NonBoolUsedInIf { location, .. } + | InterpreterError::NonBoolUsedInConstrain { location, .. } + | InterpreterError::FailingConstraint { location, .. } + | InterpreterError::NoMethodFound { location, .. } + | InterpreterError::NonIntegerUsedInLoop { location, .. } + | InterpreterError::NonPointerDereferenced { location, .. } + | InterpreterError::NonTupleOrStructInMemberAccess { location, .. } + | InterpreterError::NonArrayIndexed { location, .. } + | InterpreterError::NonIntegerUsedAsIndex { location, .. } + | InterpreterError::NonIntegerIntegerLiteral { location, .. } + | InterpreterError::NonIntegerArrayLength { location, .. } + | InterpreterError::NonNumericCasted { location, .. } + | InterpreterError::IndexOutOfBounds { location, .. } + | InterpreterError::ExpectedStructToHaveField { location, .. } + | InterpreterError::TypeUnsupported { location, .. } + | InterpreterError::InvalidValueForUnary { location, .. } + | InterpreterError::InvalidValuesForBinary { location, .. } + | InterpreterError::CastToNonNumericType { location, .. } + | InterpreterError::QuoteInRuntimeCode { location, .. } + | InterpreterError::NonStructInConstructor { location, .. } + | InterpreterError::CannotInlineMacro { location, .. } + | InterpreterError::UnquoteFoundDuringEvaluation { location, .. } + | InterpreterError::Unimplemented { location, .. } + | InterpreterError::BreakNotInLoop { location, .. } + | InterpreterError::ContinueNotInLoop { location, .. } => *location, + InterpreterError::Break | InterpreterError::Continue => { + panic!("Tried to get the location of Break/Continue error!") + } + } + } +} + +impl From for CustomDiagnostic { + fn from(error: InterpreterError) -> Self { + match error { + InterpreterError::ArgumentCountMismatch { expected, actual, call_location } => { + let only = if expected > actual { "only " } else { "" }; + let plural = if expected == 1 { "" } else { "s" }; + let was_were = if actual == 1 { "was" } else { "were" }; + let msg = format!( + "Expected {expected} argument{plural}, but {only}{actual} {was_were} provided" + ); + + let few_many = if actual < expected { "few" } else { "many" }; + let secondary = format!("Too {few_many} arguments"); + CustomDiagnostic::simple_error(msg, secondary, call_location.span) + } + InterpreterError::TypeMismatch { expected, value, location } => { + let typ = value.get_type(); + let msg = format!("Expected `{expected}` but a value of type `{typ}` was given"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::NonComptimeVarReferenced { name, location } => { + let msg = format!("Non-comptime variable `{name}` referenced in comptime code"); + let secondary = "Non-comptime variables can't be used in comptime code".to_string(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::IntegerOutOfRangeForType { value, typ, location } => { + let int = match value.try_into_u128() { + Some(int) => int.to_string(), + None => value.to_string(), + }; + let msg = format!("{int} is outside the range of the {typ} type"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::ErrorNodeEncountered { location } => { + let msg = "Internal Compiler Error: Error node encountered".to_string(); + let secondary = "This is a bug, please report this if found!".to_string(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonFunctionCalled { value, location } => { + let msg = "Only functions may be called".to_string(); + let secondary = format!("Expression has type {}", value.get_type()); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonBoolUsedInIf { value, location } => { + let msg = format!("Expected a `bool` but found `{}`", value.get_type()); + let secondary = "If conditions must be a boolean value".to_string(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonBoolUsedInConstrain { value, location } => { + let msg = format!("Expected a `bool` but found `{}`", value.get_type()); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::FailingConstraint { message, location } => { + let (primary, secondary) = match message { + Some(msg) => (format!("{msg:?}"), "Assertion failed".into()), + None => ("Assertion failed".into(), String::new()), + }; + CustomDiagnostic::simple_error(primary, secondary, location.span) + } + InterpreterError::NoMethodFound { name, typ, location } => { + let msg = format!("No method named `{name}` found for type `{typ}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::NonIntegerUsedInLoop { value, location } => { + let typ = value.get_type(); + let msg = format!("Non-integer type `{typ}` used in for loop"); + let secondary = if matches!(typ.as_ref(), &Type::FieldElement) { + "`field` is not an integer type, try `u64` instead".to_string() + } else { + String::new() + }; + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonPointerDereferenced { value, location } => { + let typ = value.get_type(); + let msg = format!("Only references may be dereferenced, but found `{typ}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::NonTupleOrStructInMemberAccess { value, location } => { + let msg = format!("The type `{}` has no fields to access", value.get_type()); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::NonArrayIndexed { value, location } => { + let msg = format!("Expected an array or slice but found a(n) {}", value.get_type()); + let secondary = "Only arrays or slices may be indexed".into(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonIntegerUsedAsIndex { value, location } => { + let msg = format!("Expected an integer but found a(n) {}", value.get_type()); + let secondary = + "Only integers may be indexed. Note that this excludes `field`s".into(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonIntegerIntegerLiteral { typ, location } => { + let msg = format!("This integer literal somehow has the type `{typ}`"); + let secondary = "This is likely a bug".into(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonIntegerArrayLength { typ, location } => { + let msg = format!("Non-integer array length: `{typ}`"); + let secondary = "Array lengths must be integers".into(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::NonNumericCasted { value, location } => { + let msg = "Only numeric types may be casted".into(); + let secondary = format!("`{}` is non-numeric", value.get_type()); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::IndexOutOfBounds { index, length, location } => { + let msg = format!("{index} is out of bounds for the array of length {length}"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::ExpectedStructToHaveField { value, field_name, location } => { + let typ = value.get_type(); + let msg = format!("The type `{typ}` has no field named `{field_name}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::TypeUnsupported { typ, location } => { + let msg = + format!("The type `{typ}` is currently unsupported in comptime expressions"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::InvalidValueForUnary { value, operator, location } => { + let msg = format!("`{}` cannot be used with unary {operator}", value.get_type()); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::InvalidValuesForBinary { lhs, rhs, operator, location } => { + let lhs = lhs.get_type(); + let rhs = rhs.get_type(); + let msg = format!("No implementation for `{lhs}` {operator} `{rhs}`",); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::CastToNonNumericType { typ, location } => { + let msg = format!("Cannot cast to non-numeric type `{typ}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::QuoteInRuntimeCode { location } => { + let msg = "`quote` may only be used in comptime code".into(); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::NonStructInConstructor { typ, location } => { + let msg = format!("`{typ}` is not a struct type"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::CannotInlineMacro { value, location } => { + let msg = "Cannot inline value into runtime code if it contains references".into(); + let secondary = format!("Cannot inline value {value:?}"); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::UnquoteFoundDuringEvaluation { location } => { + let msg = "Unquote found during comptime evaluation".into(); + let secondary = "This is a bug".into(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::Unimplemented { item, location } => { + let msg = format!("{item} is currently unimplemented"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::BreakNotInLoop { location } => { + let msg = "There is no loop to break out of!".into(); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::ContinueNotInLoop { location } => { + let msg = "There is no loop to continue!".into(); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), + InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), + } + } +} 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 c01c985a40c..4bdd4eaec9a 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 @@ -506,11 +506,8 @@ impl<'a> Interpreter<'a> { Value::U64(value) => Ok(Value::U64(0 - value)), value => { let location = self.interner.expr_location(&id); - Err(InterpreterError::InvalidValueForUnary { - value, - location, - operator: "minus", - }) + let operator = "minus"; + Err(InterpreterError::InvalidValueForUnary { value, location, operator }) } }, crate::ast::UnaryOp::Not => match rhs { @@ -880,7 +877,7 @@ impl<'a> Interpreter<'a> { if let Some(method) = method { self.call_function(method, arguments, location) } else { - Err(InterpreterError::NoMethodFound { object, typ, location }) + Err(InterpreterError::NoMethodFound { name: method_name.clone(), typ, location }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs index 26e05d675b3..148fa56b4cb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -5,4 +5,5 @@ mod scan; mod tests; mod value; +pub use errors::InterpreterError; pub use interpreter::Interpreter; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 7805f36cdb2..d8839b33ff4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -1,7 +1,7 @@ use super::dc_mod::collect_defs; use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; -use crate::hir::comptime::Interpreter; +use crate::hir::comptime::{Interpreter, InterpreterError}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; @@ -155,6 +155,7 @@ pub enum CompilationError { DefinitionError(DefCollectorErrorKind), ResolverError(ResolverError), TypeError(TypeCheckError), + InterpreterError(InterpreterError), } impl From for CustomDiagnostic { @@ -164,6 +165,7 @@ impl From for CustomDiagnostic { CompilationError::DefinitionError(error) => error.into(), CompilationError::ResolverError(error) => error.into(), CompilationError::TypeError(error) => error.into(), + CompilationError::InterpreterError(error) => error.into(), } } } @@ -500,13 +502,15 @@ impl ResolvedModule { } /// Evaluate all `comptime` expressions in this module - fn evaluate_comptime(&self, interner: &mut NodeInterner) { + fn evaluate_comptime(&mut self, interner: &mut NodeInterner) { let mut interpreter = Interpreter::new(interner); for (_file, function) in &self.functions { - // .unwrap() is temporary here until we can convert - // from InterpreterError to (CompilationError, FileId) - interpreter.scan_function(*function).unwrap(); + // The file returned by the error may be different than the file the + // function is in so only use the error's file id. + if let Err(error) = interpreter.scan_function(*function) { + self.errors.push(error.into_compilation_error_pair()); + } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs index 7c0090ff95b..590c2e3d6b6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -5,8 +5,8 @@ use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, GlobalId, NodeInterner, StructId}; use crate::parser::{parse_program, ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; -use arena::{Arena, Index}; use fm::{FileId, FileManager}; +use noirc_arena::{Arena, Index}; use noirc_errors::Location; use std::collections::{BTreeMap, HashMap}; mod module_def; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs index 44dab6dee3d..2a1ff1dd42c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -672,7 +672,7 @@ pub mod test { } fn local_module_id(&self) -> LocalModuleId { - LocalModuleId(arena::Index::unsafe_zeroed()) + LocalModuleId(noirc_arena::Index::unsafe_zeroed()) } fn module_id(&self) -> ModuleId { @@ -724,7 +724,7 @@ pub mod test { let mut def_maps = BTreeMap::new(); let file = FileId::default(); - let mut modules = arena::Arena::default(); + let mut modules = noirc_arena::Arena::default(); let location = Location::new(Default::default(), file); modules.insert(ModuleData::new(None, location, false)); 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 9d3a79820dc..5e4fa3df6c5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -2,9 +2,9 @@ use std::borrow::Cow; use std::collections::HashMap; use std::ops::Deref; -use arena::{Arena, Index}; use fm::FileId; use iter_extended::vecmap; +use noirc_arena::{Arena, Index}; use noirc_errors::{Location, Span, Spanned}; use petgraph::algo::tarjan_scc; use petgraph::prelude::DiGraph; 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 b5f1b1d0c64..ac8c96a092e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/resolve_locations.rs @@ -1,4 +1,4 @@ -use arena::Index; +use noirc_arena::Index; use noirc_errors::Location; use crate::hir_def::expr::HirExpression; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 31bf2245b1f..c6cef568ed8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -31,8 +31,8 @@ mod test { hir::def_map::{CrateDefMap, LocalModuleId}, parse_program, }; - use arena::Arena; use fm::FileManager; + use noirc_arena::Arena; pub(crate) fn has_parser_error(errors: &[(CompilationError, FileId)]) -> bool { errors.iter().any(|(e, _f)| matches!(e, CompilationError::ParseError(_))) diff --git a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index 7329880c7a7..efa52b2c3f2 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/noir-repo/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -13,18 +13,21 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## sha256 Given an array of bytes, returns the resulting sha256 hash. +Specify a message_size to hash only the first `message_size` bytes of the input. #include_code sha256 noir_stdlib/src/hash.nr rust example: +#include_code sha256_var test_programs/execution_success/sha256/src/main.nr rust ```rust fn main() { let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::sha256(x); + let hash = std::sha256::sha256_var(x, 4); } ``` + ## blake2s diff --git a/noir/noir-repo/flake.lock b/noir/noir-repo/flake.lock deleted file mode 100644 index 5a9f9470a1f..00000000000 --- a/noir/noir-repo/flake.lock +++ /dev/null @@ -1,170 +0,0 @@ -{ - "nodes": { - "crane": { - "inputs": { - "flake-compat": [ - "flake-compat" - ], - "flake-utils": [ - "flake-utils" - ], - "nixpkgs": [ - "nixpkgs" - ], - "rust-overlay": "rust-overlay" - }, - "locked": { - "lastModified": 1681177078, - "narHash": "sha256-ZNIjBDou2GOabcpctiQykEQVkI8BDwk7TyvlWlI4myE=", - "owner": "ipetkov", - "repo": "crane", - "rev": "0c9f468ff00576577d83f5019a66c557ede5acf6", - "type": "github" - }, - "original": { - "owner": "ipetkov", - "repo": "crane", - "type": "github" - } - }, - "fenix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1694499657, - "narHash": "sha256-u/fZtLtN7VcDrMMVrdsFy93PEkaiK+tNpJT9on4SGdU=", - "owner": "nix-community", - "repo": "fenix", - "rev": "2895ff377cbb3cb6f5dd92066734b0447cb04e20", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1695559356, - "narHash": "sha256-kXZ1pUoImD9OEbPCwpTz4tHsNTr4CIyIfXb3ocuR8sI=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "261abe8a44a7e8392598d038d2e01f7b33cf26d0", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "crane": "crane", - "fenix": "fenix", - "flake-compat": "flake-compat", - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } - }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1694421477, - "narHash": "sha256-df6YZzR57VFzkOPwIohJfC0fRwgq6yUPbMJkKAtQyAE=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "cc6c8209cbaf7df55013977cf5cc8488d6b7ff1c", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, - "rust-overlay": { - "inputs": { - "flake-utils": [ - "crane", - "flake-utils" - ], - "nixpkgs": [ - "crane", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1694484610, - "narHash": "sha256-aeSDkp7fkAqtVjW3QUn7vq7BKNlFul/BiGgdv7rK+mA=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "c5b977a7e6a295697fa1f9c42174fd6313b38df4", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/noir/noir-repo/noir_stdlib/src/hash.nr b/noir/noir-repo/noir_stdlib/src/hash.nr index 26a9fa6c2c0..ef1cb0889d7 100644 --- a/noir/noir-repo/noir_stdlib/src/hash.nr +++ b/noir/noir-repo/noir_stdlib/src/hash.nr @@ -4,6 +4,7 @@ mod poseidon2; use crate::default::Default; use crate::uint128::U128; +use crate::sha256::{digest, sha256_var}; #[foreign(sha256)] // docs:start:sha256 diff --git a/noir/noir-repo/noir_stdlib/src/sha256.nr b/noir/noir-repo/noir_stdlib/src/sha256.nr index 8ca6808568d..d856043fcfa 100644 --- a/noir/noir-repo/noir_stdlib/src/sha256.nr +++ b/noir/noir-repo/noir_stdlib/src/sha256.nr @@ -17,19 +17,42 @@ fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] { } // SHA-256 hash function pub fn digest(msg: [u8; N]) -> [u8; 32] { + sha256_var(msg, N) +} + +fn hash_final_block(msg_block: [u8; 64], mut state: [u32; 8]) -> [u8; 32] { + let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes + + // Hash final padded block + state = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), state); + + // Return final hash as byte array + for j in 0..8 { + let h_bytes = (state[7 - j] as Field).to_le_bytes(4); + for k in 0..4 { + out_h[31 - 4*j - k] = h_bytes[k]; + } + } + + out_h +} + +// Variable size SHA-256 hash +pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { let mut msg_block: [u8; 64] = [0; 64]; let mut h: [u32; 8] = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]; // Intermediate hash, starting with the canonical initial value - let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes let mut i: u64 = 0; // Message byte pointer for k in 0..N { - // Populate msg_block - msg_block[i] = msg[k]; - i = i + 1; - if i == 64 { - // Enough to hash block - h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h); + if k < message_size { + // Populate msg_block + msg_block[i] = msg[k]; + i = i + 1; + if i == 64 { + // Enough to hash block + h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h); - i = 0; + i = 0; + } } } // Pad the rest such that we have a [u32; 2] block at the end representing the length @@ -53,7 +76,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { i = 0; } - let len = 8 * msg.len(); + let len = 8 * message_size; let len_bytes = (len as Field).to_le_bytes(8); for _i in 0..64 { // In any case, fill blocks up with zeros until the last 64 (i.e. until i = 56). @@ -67,16 +90,5 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { i += 8; } } - // Hash final padded block - h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h); - - // Return final hash as byte array - for j in 0..8 { - let h_bytes = (h[7 - j] as Field).to_le_bytes(4); - for k in 0..4 { - out_h[31 - 4*j - k] = h_bytes[k]; - } - } - - out_h + hash_final_block(msg_block, h) } diff --git a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr index fd5340e2384..d4240ded8b3 100644 --- a/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/sha256/src/main.nr @@ -14,6 +14,8 @@ use dep::std; 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 = std::hash::sha256([x as u8]); + // docs:start:sha256_var + let digest = std::hash::sha256_var([x as u8], 1); + // docs:end:sha256_var assert(digest == result); } 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 438e91ff302..b5782269674 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.27.0", + "version": "0.28.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index ee93413ab85..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/compiler/utils/iter-extended/Cargo.toml b/noir/noir-repo/utils/iter-extended/Cargo.toml similarity index 100% rename from noir/noir-repo/compiler/utils/iter-extended/Cargo.toml rename to noir/noir-repo/utils/iter-extended/Cargo.toml diff --git a/noir/noir-repo/compiler/utils/iter-extended/src/lib.rs b/noir/noir-repo/utils/iter-extended/src/lib.rs similarity index 100% rename from noir/noir-repo/compiler/utils/iter-extended/src/lib.rs rename to noir/noir-repo/utils/iter-extended/src/lib.rs