diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 40f4336e0b5..06259e06248 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -48,6 +48,8 @@ pub enum RuntimeError { BigIntModulus { call_stack: CallStack }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, + #[error("All `oracle` methods should be wrapped in an unconstrained fn")] + UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, } // We avoid showing the actual lhs and rhs since most of the time they are just 0 @@ -139,6 +141,7 @@ impl RuntimeError { | RuntimeError::NestedSlice { call_stack, .. } | RuntimeError::BigIntModulus { call_stack, .. } | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } => call_stack, + RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } => call_stack, } } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 5a4fa021f1f..8390f480e3a 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -612,9 +612,11 @@ impl Context { self.ssa_values.insert(*result, output); } } - Value::ForeignFunction(_) => unreachable!( - "All `oracle` methods should be wrapped in an unconstrained fn" - ), + Value::ForeignFunction(_) => { + return Err(RuntimeError::UnconstrainedOracleReturnToConstrained { + call_stack: self.acir_context.get_call_stack(), + }) + } _ => unreachable!("expected calling a function but got {function_value:?}"), } } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 7eacc8eb2d1..3d834128688 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -122,6 +122,10 @@ pub enum TypeCheckError { "Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime" )] ConstrainedReferenceToUnconstrained { span: Span }, + #[error( + "Cannot pass a mutable reference from a unconstrained runtime to an constrained runtime" + )] + UnconstrainedReferenceToConstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, #[error("Only sized types may be used in the entry point to a program")] @@ -229,6 +233,7 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } + | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 7219f4d09c6..0b3dd022209 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -184,14 +184,18 @@ impl<'interner> TypeChecker<'interner> { let return_type = self.bind_function_type(function, args, span); // Check that we are not passing a slice from an unconstrained runtime to a constrained runtime - if is_current_func_constrained - && is_unconstrained_call - && return_type.contains_slice() - { - self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained { - span: self.interner.expr_span(expr_id), - }); - return Type::Error; + if is_current_func_constrained && is_unconstrained_call { + if return_type.contains_slice() { + self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained { + span: self.interner.expr_span(expr_id), + }); + return Type::Error; + } else if matches!(&return_type.follow_bindings(), Type::MutableReference(_)) { + self.errors.push(TypeCheckError::UnconstrainedReferenceToConstrained { + span: self.interner.expr_span(expr_id), + }); + return Type::Error; + } } return_type diff --git a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml new file mode 100644 index 00000000000..1081b5ab8e2 --- /dev/null +++ b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "unconstrained_oracle" +type = "bin" +authors = [""] +[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 new file mode 100644 index 00000000000..abc7bf51ab8 --- /dev/null +++ b/test_programs/compile_failure/unconstrained_oracle/src/main.nr @@ -0,0 +1,9 @@ +#[oracle(getNoun)] +unconstrained fn external_fn() -> Field { + 100 / 5 +} + +fn main() { + let x = anon(); + assert(x * 5 == 100); +} diff --git a/test_programs/compile_failure/unconstrained_ref/Nargo.toml b/test_programs/compile_failure/unconstrained_ref/Nargo.toml new file mode 100644 index 00000000000..b120a3b5532 --- /dev/null +++ b/test_programs/compile_failure/unconstrained_ref/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "unconstrained_ref" +type = "bin" +authors = [""] +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_ref/src/main.nr b/test_programs/compile_failure/unconstrained_ref/src/main.nr new file mode 100644 index 00000000000..bb4b2090ddb --- /dev/null +++ b/test_programs/compile_failure/unconstrained_ref/src/main.nr @@ -0,0 +1,8 @@ +unconstrained fn uncon_ref() -> &mut Field { + let lr = &mut 7; + lr +} + +fn main() { + let e = uncon_ref(); +} \ No newline at end of file