diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index d4a4e521f27..731796d7e6c 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -2,6 +2,7 @@ use crate::ast::PathSegment; use crate::parse_program; use crate::parser::{ParsedModule, ParsedSubModule}; use crate::signed_field::SignedField; +use crate::token::FunctionAttributeKind; use crate::{ast, ast::Path, parser::ItemKind}; use fm::FileId; use noirc_errors::debug_info::{DebugFnId, DebugFunction}; @@ -107,6 +108,20 @@ impl DebugInstrumenter { } fn walk_fn(&mut self, func: &mut ast::FunctionDefinition) { + // Don't instrument functions that are not supposed to have a body + if let Some((func, _)) = &func.attributes.function { + match func.kind { + FunctionAttributeKind::Foreign(_) + | FunctionAttributeKind::Builtin(_) + | FunctionAttributeKind::Oracle(_) => return, + FunctionAttributeKind::Test(..) + | FunctionAttributeKind::Fold + | FunctionAttributeKind::NoPredicates + | FunctionAttributeKind::InlineAlways + | FunctionAttributeKind::FuzzingHarness(..) => (), + } + } + let func_name = func.name.to_string(); let func_args = func.parameters.iter().map(|param| pattern_to_string(¶m.pattern)).collect(); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index b62c450545c..aebbb4df17c 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -518,8 +518,22 @@ impl<'context> Elaborator<'context> { let (hir_func, body_type) = match kind { FunctionKind::Builtin | FunctionKind::LowLevel - | FunctionKind::Oracle - | FunctionKind::TraitFunctionWithoutBody => (HirFunction::empty(), Type::Error), + | FunctionKind::TraitFunctionWithoutBody => { + if !body.statements.is_empty() { + panic!( + "Builtin, low-level, and trait function declarations cannot have a body" + ); + } + (HirFunction::empty(), Type::Error) + } + FunctionKind::Oracle => { + if !body.statements.is_empty() { + self.push_err(ResolverError::OracleWithBody { + location: func_meta.name.location, + }); + } + (HirFunction::empty(), Type::Error) + } FunctionKind::Normal => { let return_type = func_meta.return_type(); let (block, body_type) = self.elaborate_block(body, Some(return_type)); diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 1353727e6ed..706eb2876c6 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -190,6 +190,8 @@ pub enum ResolverError { WildcardTypeDisallowed { location: Location }, #[error("References are not allowed in globals")] ReferencesNotAllowedInGlobals { location: Location }, + #[error("Functions marked with #[oracle] must have no body")] + OracleWithBody { location: Location }, } impl ResolverError { @@ -255,7 +257,8 @@ impl ResolverError { | ResolverError::AssociatedItemConstraintsNotAllowedInGenerics { location } | ResolverError::AmbiguousAssociatedType { location, .. } | ResolverError::WildcardTypeDisallowed { location } - | ResolverError::ReferencesNotAllowedInGlobals { location } => *location, + | ResolverError::ReferencesNotAllowedInGlobals { location } + | ResolverError::OracleWithBody { location } => *location, ResolverError::UnusedVariable { ident } | ResolverError::UnusedItem { ident, .. } | ResolverError::DuplicateField { field: ident } @@ -808,6 +811,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *location, ) } + ResolverError::OracleWithBody { location } => { + Diagnostic::simple_error( + "Functions marked with #[oracle] must have no body".to_string(), + "This function body will never be run so should be removed".to_string(), + *location, + ) } } + } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 5e2f9bfa212..26ccf95331f 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -7,6 +7,7 @@ mod enums; mod imports; mod metaprogramming; mod name_shadowing; +mod oracles; mod references; mod traits; mod turbofish; @@ -1321,19 +1322,6 @@ fn deny_fold_attribute_on_unconstrained() { check_errors!(src); } -#[test] -fn deny_oracle_attribute_on_non_unconstrained() { - let src = r#" - #[oracle(foo)] - ^^^^^^^^^^^^^^ Usage of the `#[oracle]` function attribute is only valid on unconstrained functions - pub fn foo(x: Field, y: Field) { - ~~~ Oracle functions must have the `unconstrained` keyword applied - assert(x != y); - } - "#; - check_errors!(src); -} - #[test] fn deny_abi_attribute_outside_of_contract() { let src = r#" diff --git a/compiler/noirc_frontend/src/tests/oracles.rs b/compiler/noirc_frontend/src/tests/oracles.rs new file mode 100644 index 00000000000..f1525d9c466 --- /dev/null +++ b/compiler/noirc_frontend/src/tests/oracles.rs @@ -0,0 +1,26 @@ +use crate::check_errors; + +#[test] +fn deny_oracle_attribute_on_non_unconstrained() { + let src = r#" + #[oracle(foo)] + ^^^^^^^^^^^^^^ Usage of the `#[oracle]` function attribute is only valid on unconstrained functions + pub fn foo(x: Field, y: Field) { + ~~~ Oracle functions must have the `unconstrained` keyword applied + } + "#; + check_errors!(src); +} + +#[test] +fn errors_if_oracle_declaration_has_function_body() { + let src = r#" + #[oracle(oracle_call)] + pub unconstrained fn oracle_call() { + ^^^^^^^^^^^ Functions marked with #[oracle] must have no body + ~~~~~~~~~~~ This function body will never be run so should be removed + assert(true); + } + "#; + check_errors!(src); +} diff --git a/test_programs/compile_failure/multiple_primary_attributes_fail/src/main.nr b/test_programs/compile_failure/multiple_primary_attributes_fail/src/main.nr index c8d8b0a1969..7b778ebabfc 100644 --- a/test_programs/compile_failure/multiple_primary_attributes_fail/src/main.nr +++ b/test_programs/compile_failure/multiple_primary_attributes_fail/src/main.nr @@ -1,6 +1,4 @@ #[oracle(oracleName)] #[builtin(builtinName)] -fn main(x: Field) -> pub Field { - x + 1 -} \ No newline at end of file +fn main(x: Field) -> pub Field {} diff --git a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml deleted file mode 100644 index 7c3fb8766bf..00000000000 --- a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "unconstrained_oracle" -type = "bin" -authors = [""] -compiler_version = ">=0.25.0" - -[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_oracle/src/main.nr b/test_programs/compile_failure/unconstrained_oracle/src/main.nr deleted file mode 100644 index abc7bf51ab8..00000000000 --- a/test_programs/compile_failure/unconstrained_oracle/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -#[oracle(getNoun)] -unconstrained fn external_fn() -> Field { - 100 / 5 -} - -fn main() { - let x = anon(); - assert(x * 5 == 100); -} diff --git a/tooling/ast_fuzzer/src/compare/comptime.rs b/tooling/ast_fuzzer/src/compare/comptime.rs index e23f9965355..74fea01c9ff 100644 --- a/tooling/ast_fuzzer/src/compare/comptime.rs +++ b/tooling/ast_fuzzer/src/compare/comptime.rs @@ -100,23 +100,23 @@ impl CompareComptime { // Include the print part of stdlib for the elaborator to be able to use the print oracle let import_print = r#" #[oracle(print)] - unconstrained fn print_oracle(with_newline: bool, input: T) {{}} + unconstrained fn print_oracle(with_newline: bool, input: T) {} - unconstrained fn print_unconstrained(with_newline: bool, input: T) {{ + unconstrained fn print_unconstrained(with_newline: bool, input: T) { print_oracle(with_newline, input); - }} + } - pub fn println(input: T) {{ - unsafe {{ + pub fn println(input: T) { + unsafe { print_unconstrained(true, input); - }} - }} + } + } - pub fn print(input: T) {{ - unsafe {{ + pub fn print(input: T) { + unsafe { print_unconstrained(false, input); - }} - }} + } + } "#; // Add comptime modifier for main diff --git a/tooling/nargo_cli/tests/snapshots/compile_failure/multiple_primary_attributes_fail/execute__tests__stderr.snap b/tooling/nargo_cli/tests/snapshots/compile_failure/multiple_primary_attributes_fail/execute__tests__stderr.snap index 83739932e0d..7a0639db2ee 100644 --- a/tooling/nargo_cli/tests/snapshots/compile_failure/multiple_primary_attributes_fail/execute__tests__stderr.snap +++ b/tooling/nargo_cli/tests/snapshots/compile_failure/multiple_primary_attributes_fail/execute__tests__stderr.snap @@ -8,7 +8,7 @@ error: Usage of the `#[oracle]` function attribute is only valid on unconstraine 2 │ #[oracle(oracleName)] │ --------------------- 3 │ #[builtin(builtinName)] -4 │ fn main(x: Field) -> pub Field { +4 │ fn main(x: Field) -> pub Field {} │ ---- Oracle functions must have the `unconstrained` keyword applied │ diff --git a/tooling/nargo_cli/tests/snapshots/compile_failure/unconstrained_oracle/execute__tests__stderr.snap b/tooling/nargo_cli/tests/snapshots/compile_failure/unconstrained_oracle/execute__tests__stderr.snap deleted file mode 100644 index c969f9a763e..00000000000 --- a/tooling/nargo_cli/tests/snapshots/compile_failure/unconstrained_oracle/execute__tests__stderr.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: tooling/nargo_cli/tests/execute.rs -expression: stderr ---- -warning: unused function external_fn - ┌─ src/main.nr:2:18 - │ -2 │ unconstrained fn external_fn() -> Field { - │ ----------- unused function - │ - -error: cannot find `anon` in this scope - ┌─ src/main.nr:7:13 - │ -7 │ let x = anon(); - │ ---- not found in this scope - │ - -Aborting due to 1 previous error