diff --git a/changelogs/unreleased/822-schaeff b/changelogs/unreleased/822-schaeff new file mode 100644 index 00000000..cf42ad15 --- /dev/null +++ b/changelogs/unreleased/822-schaeff @@ -0,0 +1 @@ +Make function selection stricter in function calls \ No newline at end of file diff --git a/zokrates_cli/examples/compile_errors/too_many_arguments.zok b/zokrates_cli/examples/compile_errors/too_many_arguments.zok new file mode 100644 index 00000000..3efabfdb --- /dev/null +++ b/zokrates_cli/examples/compile_errors/too_many_arguments.zok @@ -0,0 +1,5 @@ +def foo() -> field: + return 1 + +def main() -> field: + return foo(42) \ No newline at end of file diff --git a/zokrates_cli/examples/compile_errors/too_many_generics.zok b/zokrates_cli/examples/compile_errors/too_many_generics.zok new file mode 100644 index 00000000..f34a17f5 --- /dev/null +++ b/zokrates_cli/examples/compile_errors/too_many_generics.zok @@ -0,0 +1,5 @@ +def foo() -> field: + return 1 + +def main() -> field: + return foo::<42>() \ No newline at end of file diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index 3bd4f160..fc3f03f4 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -134,6 +134,7 @@ impl fmt::Display for ErrorInner { #[derive(Debug)] struct FunctionQuery<'ast, T> { id: Identifier<'ast>, + generics_count: Option, inputs: Vec>, /// Output types are optional as we try to infer them outputs: Vec>>, @@ -141,6 +142,17 @@ struct FunctionQuery<'ast, T> { impl<'ast, T: fmt::Display> fmt::Display for FunctionQuery<'ast, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(count) = self.generics_count { + write!( + f, + "<{}>", + (0..count) + .map(|_| String::from("_")) + .collect::>() + .join(", ") + )? + } + write!(f, "(")?; for (i, t) in self.inputs.iter().enumerate() { write!(f, "{}", t)?; @@ -181,11 +193,13 @@ impl<'ast, T: Field> FunctionQuery<'ast, T> { /// Create a new query. fn new( id: Identifier<'ast>, + generics: &Option>>>, inputs: &[Type<'ast, T>], outputs: &[Option>], ) -> Self { FunctionQuery { id, + generics_count: generics.as_ref().map(|g| g.len()), inputs: inputs.to_owned(), outputs: outputs.to_owned(), } @@ -194,6 +208,8 @@ impl<'ast, T: Field> FunctionQuery<'ast, T> { /// match a `FunctionKey` against this `FunctionQuery` fn match_func(&self, func: &DeclarationFunctionKey<'ast>) -> bool { self.id == func.id + && self.generics_count.map(|count| count == func.signature.generics.len()).unwrap_or(true) // we do not look at the values here, this will be checked when inlining anyway + && self.inputs.len() == func.signature.inputs.len() && self .inputs .iter() @@ -1340,7 +1356,7 @@ impl<'ast, T: Field> Checker<'ast, T> { let arguments_types: Vec<_> = arguments_checked.iter().map(|a| a.get_type()).collect(); - let query = FunctionQuery::new(&fun_id, &arguments_types, &assignee_types); + let query = FunctionQuery::new(&fun_id, &generics_checked, &arguments_types, &assignee_types); let functions = self.find_functions(&query); @@ -1883,7 +1899,8 @@ impl<'ast, T: Field> Checker<'ast, T> { // outside of multidef, function calls must have a single return value // we use type inference to determine the type of the return, so we don't specify it - let query = FunctionQuery::new(&fun_id, &arguments_types, &[None]); + let query = + FunctionQuery::new(&fun_id, &generics_checked, &arguments_types, &[None]); let functions = self.find_functions(&query); diff --git a/zokrates_core/src/typed_absy/types.rs b/zokrates_core/src/typed_absy/types.rs index 6622bddd..ac12cd59 100644 --- a/zokrates_core/src/typed_absy/types.rs +++ b/zokrates_core/src/typed_absy/types.rs @@ -993,6 +993,8 @@ pub mod signature { // we keep track of the value of constants in a map, as a given constant can only have one value let mut constants = ConcreteGenericsAssignment::default(); + assert_eq!(self.inputs.len(), signature.inputs.len()); + assert_eq!(self.outputs.len(), signature.outputs.len()); assert_eq!(self.generics.len(), values.len()); let decl_generics = self.generics.iter().map(|g| match g.clone().unwrap() {