diff --git a/Cargo.lock b/Cargo.lock index 87314665107..deb772e2807 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,7 @@ dependencies = [ "cfg-if", "hex", "num-bigint", + "num-traits", "serde", ] diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index 7d059e9e4be..10027519d6d 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -16,6 +16,7 @@ repository.workspace = true hex.workspace = true num-bigint.workspace = true serde.workspace = true +num-traits.workspace = true ark-bn254 = { version = "^0.4.0", optional = true, default-features = false, features = [ "curve", diff --git a/acvm-repo/acir_field/src/lib.rs b/acvm-repo/acir_field/src/lib.rs index 6d4547868cc..eafe4bb2ad4 100644 --- a/acvm-repo/acir_field/src/lib.rs +++ b/acvm-repo/acir_field/src/lib.rs @@ -3,6 +3,9 @@ #![warn(clippy::semicolon_if_nothing_returned)] #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] +use num_bigint::BigUint; +use num_traits::Num; + cfg_if::cfg_if! { if #[cfg(feature = "bn254")] { mod generic_ark; @@ -18,12 +21,33 @@ cfg_if::cfg_if! { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum FieldOptions { BN254, BLS12_381, } +impl FieldOptions { + pub fn to_string(&self) -> &str { + match self { + FieldOptions::BN254 => "bn254", + FieldOptions::BLS12_381 => "bls12_381", + } + } + + pub fn is_native_field(str: &str) -> bool { + let big_num = if let Some(hex) = str.strip_prefix("0x") { + BigUint::from_str_radix(hex, 16) + } else { + BigUint::from_str_radix(str, 10) + }; + if let Ok(big_num) = big_num { + big_num == FieldElement::modulus() + } else { + CHOSEN_FIELD.to_string() == str + } + } +} // This is needed because features are additive through the dependency graph; if a dependency turns on the bn254, then it // will be turned on in all crates that depend on it #[macro_export] diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index dc99546f798..803ffbc41fe 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1,5 +1,6 @@ use std::vec; +use acvm::acir::acir_field::FieldOptions; use fm::FileId; use noirc_errors::Location; @@ -202,6 +203,13 @@ impl<'a> ModCollector<'a> { let module = ModuleId { krate, local_id: self.module_id }; for function in functions { + // check if optional field attribute is compatible with native field + if let Some(field) = function.attributes().get_field_attribute() { + if !FieldOptions::is_native_field(&field) { + continue; + } + } + let name = function.name_ident().clone(); let func_id = context.def_interner.push_empty_fn(); diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index ad81b163801..2ad2f3902b1 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -395,6 +395,15 @@ impl Attributes { _ => None, }) } + + pub fn get_field_attribute(&self) -> Option { + for secondary in &self.secondary { + if let SecondaryAttribute::Field(field) = secondary { + return Some(field.to_lowercase()); + } + } + None + } } /// An Attribute can be either a Primary Attribute or a Secondary Attribute @@ -466,6 +475,10 @@ impl Attribute { None => return Err(malformed_scope), } } + ["field", name] => { + validate(name)?; + Attribute::Secondary(SecondaryAttribute::Field(name.to_string())) + } // Secondary attributes ["deprecated"] => Attribute::Secondary(SecondaryAttribute::Deprecated(None)), ["contract_library_method"] => { @@ -550,6 +563,7 @@ pub enum SecondaryAttribute { // the entry point. ContractLibraryMethod, Event, + Field(String), Custom(String), } @@ -563,6 +577,7 @@ impl fmt::Display for SecondaryAttribute { SecondaryAttribute::Custom(ref k) => write!(f, "#[{k}]"), SecondaryAttribute::ContractLibraryMethod => write!(f, "#[contract_library_method]"), SecondaryAttribute::Event => write!(f, "#[event]"), + SecondaryAttribute::Field(ref k) => write!(f, "#[field({k})]"), } } } @@ -583,7 +598,7 @@ impl AsRef for SecondaryAttribute { match self { SecondaryAttribute::Deprecated(Some(string)) => string, SecondaryAttribute::Deprecated(None) => "", - SecondaryAttribute::Custom(string) => string, + SecondaryAttribute::Custom(string) | SecondaryAttribute::Field(string) => string, SecondaryAttribute::ContractLibraryMethod => "", SecondaryAttribute::Event => "", } diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/Nargo.toml b/tooling/nargo_cli/tests/execution_success/field_attribute/Nargo.toml new file mode 100644 index 00000000000..f625d7e41f2 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/field_attribute/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "field_attribute" +type = "bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/Prover.toml b/tooling/nargo_cli/tests/execution_success/field_attribute/Prover.toml new file mode 100644 index 00000000000..07890234a19 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/field_attribute/Prover.toml @@ -0,0 +1 @@ +x = "3" diff --git a/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr b/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr new file mode 100644 index 00000000000..d6d71781899 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/field_attribute/src/main.nr @@ -0,0 +1,19 @@ +// Test integer addition: 3 + 4 = 7 +fn main(mut x: u32) { + assert(x > foo()); +} + +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} \ No newline at end of file