Skip to content

Commit

Permalink
feat: better error message when trying to invoke struct function field (
Browse files Browse the repository at this point in the history
#6661)

Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
Co-authored-by: Akosh Farkash <aakoshh@gmail.com>
Co-authored-by: jfecher <jake@aztecprotocol.com>
  • Loading branch information
4 people authored Dec 2, 2024
1 parent 6acef6d commit ea7c04a
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 5 deletions.
22 changes: 17 additions & 5 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1321,11 +1321,23 @@ impl<'context> Elaborator<'context> {
{
Some(method_id) => Some(HirMethodReference::FuncId(method_id)),
None => {
self.push_err(TypeCheckError::UnresolvedMethodCall {
method_name: method_name.to_string(),
object_type: object_type.clone(),
span,
});
let has_field_with_function_type =
typ.borrow().get_fields_as_written().into_iter().any(|field| {
field.name.0.contents == method_name && field.typ.is_function()
});
if has_field_with_function_type {
self.push_err(TypeCheckError::CannotInvokeStructFieldFunctionType {
method_name: method_name.to_string(),
object_type: object_type.clone(),
span,
});
} else {
self.push_err(TypeCheckError::UnresolvedMethodCall {
method_name: method_name.to_string(),
object_type: object_type.clone(),
span,
});
}
None
}
}
Expand Down
9 changes: 9 additions & 0 deletions compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub enum TypeCheckError {
CannotMutateImmutableVariable { name: String, span: Span },
#[error("No method named '{method_name}' found for type '{object_type}'")]
UnresolvedMethodCall { method_name: String, object_type: Type, span: Span },
#[error("Cannot invoke function field '{method_name}' on type '{object_type}' as a method")]
CannotInvokeStructFieldFunctionType { method_name: String, object_type: Type, span: Span },
#[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")]
IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span },
#[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")]
Expand Down Expand Up @@ -511,6 +513,13 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic {
TypeCheckError::CyclicType { typ: _, span } => {
Diagnostic::simple_error(error.to_string(), "Cyclic types have unlimited size and are prohibited in Noir".into(), *span)
}
TypeCheckError::CannotInvokeStructFieldFunctionType { method_name, object_type, span } => {
Diagnostic::simple_error(
format!("Cannot invoke function field '{method_name}' on type '{object_type}' as a method"),
format!("to call the function stored in '{method_name}', surround the field access with parentheses: '(', ')'"),
*span,
)
},
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,14 @@ impl Type {
}
}

pub fn is_function(&self) -> bool {
match self.follow_bindings_shallow().as_ref() {
Type::Function(..) => true,
Type::Alias(alias_type, _) => alias_type.borrow().typ.is_function(),
_ => false,
}
}

/// True if this type can be used as a parameter to `main` or a contract function.
/// This is only false for unsized types like slices or slices that do not make sense
/// as a program input such as named generics or mutable references.
Expand Down
29 changes: 29 additions & 0 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3752,6 +3752,35 @@ fn allows_struct_with_generic_infix_type_as_main_input_3() {
assert_no_errors(src);
}

#[test]
fn errors_with_better_message_when_trying_to_invoke_struct_field_that_is_a_function() {
let src = r#"
pub struct Foo {
wrapped: fn(Field) -> bool,
}
impl Foo {
fn call(self) -> bool {
self.wrapped(1)
}
}
fn main() {}
"#;
let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

let CompilationError::TypeError(TypeCheckError::CannotInvokeStructFieldFunctionType {
method_name,
..
}) = &errors[0].0
else {
panic!("Expected a 'CannotInvokeStructFieldFunctionType' error, got {:?}", errors[0].0);
};

assert_eq!(method_name, "wrapped");
}

fn test_disallows_attribute_on_impl_method(
attr: &str,
check_error: impl FnOnce(&CompilationError),
Expand Down

0 comments on commit ea7c04a

Please sign in to comment.