Skip to content

Commit

Permalink
fix(frontend): Continue type check if we are missing an unsafe block (#…
Browse files Browse the repository at this point in the history
…5720)

# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

No issue as found quickly and want to push a patch ASAP

## Summary\*

Removed the `return Type::Error` when we run into a missing unsafe
block. Also switched the call to `lookup_function_from_expr` inside
`type_check_call` to use an if let as this was causing panics when
checking higher order functions that come from params.

## Additional Context



## Documentation\*

Check one:
- [X] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
vezenovm authored Aug 14, 2024
1 parent 79593b4 commit 86de991
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 20 deletions.
26 changes: 12 additions & 14 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1328,22 +1328,20 @@ impl<'context> Elaborator<'context> {
if crossing_runtime_boundary {
if !self.in_unsafe_block {
self.push_err(TypeCheckError::Unsafe { span });
return Type::Error;
}

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 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);
Expand Down
12 changes: 6 additions & 6 deletions compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FuncId> {
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
Expand Down
51 changes: 51 additions & 0 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2482,12 +2482,63 @@ 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<let N: u32> {
limbs: [u64; N],
}
impl<let N: u32> BigNum<N> {
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<let N: u32> BigNumTrait for BigNum<N> {
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 {
Expand Down

0 comments on commit 86de991

Please sign in to comment.