diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/tests/intrinsics.rs b/compiler/noirc_evaluator/src/ssa/interpreter/tests/intrinsics.rs index 4c4d8fc5f9b..67825e5e576 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/tests/intrinsics.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/tests/intrinsics.rs @@ -153,9 +153,7 @@ fn print_lambda() { let printed_output = expect_printed_output(src); - insta::assert_snapshot!(printed_output, @" - < Field>> - "); + insta::assert_snapshot!(printed_output, @"< Field>>"); } #[test] diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 00ad673943e..83d4a502e15 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -2722,12 +2722,16 @@ impl From<&Type> for PrintableType { Type::CheckedCast { to, .. } => to.as_ref().into(), Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), - Type::Function(arguments, return_type, env, unconstrained) => PrintableType::Function { - arguments: arguments.iter().map(|arg| arg.into()).collect(), - return_type: Box::new(return_type.as_ref().into()), - env: Box::new(env.as_ref().into()), - unconstrained: *unconstrained, - }, + Type::Function(arguments, return_type, env, _unconstrained) => { + // Mimicking `Monomorphizer::convert_type_helper`: functions are represented as a tuple of constrained and unconstrained version. + let make_function = |unconstrained| PrintableType::Function { + arguments: arguments.iter().map(|arg| arg.into()).collect(), + return_type: Box::new(return_type.as_ref().into()), + env: Box::new(env.as_ref().into()), + unconstrained, + }; + PrintableType::Tuple { types: vecmap([false, true], make_function) } + } Type::Reference(typ, mutable) => { PrintableType::Reference { typ: Box::new(typ.as_ref().into()), mutable: *mutable } } diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index 992b8381020..dbc8dc19b3a 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -494,11 +494,47 @@ impl AstPrinter { tuple: &[Expression], f: &mut Formatter, ) -> Result<(), std::fmt::Error> { + if self.print_function_tuple(tuple, f)? { + return Ok(()); + } write!(f, "(")?; self.print_comma_separated(tuple, f)?; write!(f, ")") } + /// Check if we have a tuple of (constrained, unconstrained) functions and if we want to print specials as std calls, + /// then assume that we would rather see `println(foo)` than `println((foo, foo))`, so we can render the AST as Noir + /// without duplicating into `println(((foo, foo), (foo, foo)))` if we print the AST and re-parse it, for example for comptime tests. + /// + /// Returns a flag to indicate if the items were handled. + fn print_function_tuple( + &mut self, + tuple: &[Expression], + f: &mut Formatter, + ) -> Result { + if !self.show_specials_as_std || tuple.len() != 2 { + return Ok(false); + } + + fn maybe_func(expr: &Expression) -> Option<&str> { + // The AST fuzzer generates Type::Function; the Monomorphizer would be Type::Tuple([Type::Function, Type::Function]) + if let Expression::Ident(Ident { typ: Type::Function(_, _, _, _), name, .. }) = expr { + Some(name.as_str()) + } else { + None + } + } + + match (maybe_func(&tuple[0]), maybe_func(&tuple[1])) { + (Some(c), Some(u)) if c == u => { + // Only print the first element. + self.print_expr(&tuple[0], f)?; + Ok(true) + } + _ => Ok(false), + } + } + fn get_called_function(expr: &Expression) -> Option<(bool, &Definition, &String)> { let is_unconstrained = |typ: &Type| match typ { Type::Function(_, _, _, unconstrained) => *unconstrained, diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index 1788b85f886..fff0dbb017d 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -59,6 +59,49 @@ pub enum PrintableType { Unit, } +/// Display type for the purpose of showing in function signatures. +impl std::fmt::Display for PrintableType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PrintableType::Field => write!(f, "Field"), + PrintableType::Array { length, typ } => write!(f, "[{typ}; {length}]"), + PrintableType::Slice { typ } => write!(f, "[{typ}]"), + PrintableType::Tuple { types } => { + let types = vecmap(types, ToString::to_string); + if types.len() == 1 { + write!(f, "({},)", types[0]) + } else { + write!(f, "({})", types.join(", ")) + } + } + PrintableType::SignedInteger { width } => write!(f, "i{width}"), + PrintableType::UnsignedInteger { width } => write!(f, "u{width}"), + PrintableType::Boolean => write!(f, "bool"), + PrintableType::Struct { name, fields: _ } => { + write!(f, "{name}") + } + PrintableType::Enum { name, variants: _ } => { + write!(f, "{name}") + } + PrintableType::String { length } => write!(f, "str<{length}>"), + PrintableType::FmtString { length, typ } => write!(f, "fmtstr<{length}, {typ}>"), + PrintableType::Function { arguments, return_type, env: _, unconstrained } => { + let cons = if *unconstrained { "unconstrained " } else { "" }; + let args = vecmap(arguments, ToString::to_string).join(", "); + write!(f, "{cons}fn({args}) -> {return_type}") + } + PrintableType::Reference { typ, mutable } => { + if *mutable { + write!(f, "&mut {typ}") + } else { + write!(f, "&{typ}") + } + } + PrintableType::Unit => write!(f, "()"), + } + } +} + /// This is what all formats eventually transform into /// For example, a toml file will parse into TomlTypes /// and those TomlTypes will be mapped to Value @@ -132,8 +175,8 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Op output.push_str("false"); } } - (PrintableValue::Field(_), PrintableType::Function { arguments, return_type, .. }) => { - output.push_str(&format!("< {return_type:?}>>",)); + (PrintableValue::Field(_), PrintableType::Function { .. }) => { + output.push_str(&format!("<<{typ}>>")); } (_, PrintableType::Reference { mutable: false, .. }) => { output.push_str("<>"); diff --git a/test_programs/execution_success/regression_10158/Nargo.toml b/test_programs/execution_success/regression_10158/Nargo.toml new file mode 100644 index 00000000000..91a6dee4fd2 --- /dev/null +++ b/test_programs/execution_success/regression_10158/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_10158" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/regression_10158/src/main.nr b/test_programs/execution_success/regression_10158/src/main.nr new file mode 100644 index 00000000000..61c9148a073 --- /dev/null +++ b/test_programs/execution_success/regression_10158/src/main.nr @@ -0,0 +1,7 @@ +pub fn main() { + println((foo, 10_u32)); +} + +fn foo(_a: u32, _b: bool) -> Field { + 0 +} diff --git a/tooling/ast_fuzzer/src/program/func.rs b/tooling/ast_fuzzer/src/program/func.rs index c1960f2db90..4a04c634135 100644 --- a/tooling/ast_fuzzer/src/program/func.rs +++ b/tooling/ast_fuzzer/src/program/func.rs @@ -1460,17 +1460,30 @@ impl<'a> FunctionContext<'a> { // Print one of the variables as-is. let (id, typ) = u.choose_iter(opts)?; + let id = *id; // The print oracle takes 2 parameters: the newline marker and the value, // but it takes 2 more arguments: the type descriptor and the format string marker, // which are inserted automatically by the monomorphizer. let param_types = vec![Type::Bool, typ.clone()]; let hir_type = types::to_hir_type(typ); - let ident = self.local_ident(*id); + let ident = self.local_ident(id); + + // Functions need to be passed as a tuple. + let arg = if types::is_function(&ident.typ) { + Expression::Tuple(vec![ + Expression::Ident(ident), + Expression::Ident(self.local_ident(id)), + ]) + } else { + Expression::Ident(ident) + }; + let mut args = vec![ expr::lit_bool(true), // include newline, - Expression::Ident(ident), + arg, ]; + append_printable_type_info_for_type(hir_type, &mut args); let print_oracle_ident = Ident { diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index c26bec9a5f1..67ef33311f0 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -72,7 +72,7 @@ fn main() -> Result<(), String> { /// Some tests are explicitly ignored in brillig due to them failing. /// These should be fixed and removed from this list. -const IGNORED_BRILLIG_TESTS: [&str; 10] = [ +const IGNORED_BRILLIG_TESTS: [&str; 11] = [ // bit sizes for bigint operation doesn't match up. "bigint", // ICE due to looking for function which doesn't exist. @@ -86,6 +86,8 @@ const IGNORED_BRILLIG_TESTS: [&str; 10] = [ "fold_numeric_generic_poseidon", // Expected to fail as test asserts on which runtime it is in. "is_unconstrained", + // The output depends on function IDs of lambdas, and with --force-brillig we only get one kind. + "regression_10158", ]; /// Tests which aren't expected to work with the default minimum inliner cases. diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/debug_logs/execute__tests__stdout.snap b/tooling/nargo_cli/tests/snapshots/execution_success/debug_logs/execute__tests__stdout.snap index 72f3a447110..80e691f24cc 100644 --- a/tooling/nargo_cli/tests/snapshots/execution_success/debug_logs/execute__tests__stdout.snap +++ b/tooling/nargo_cli/tests/snapshots/execution_success/debug_logs/execute__tests__stdout.snap @@ -30,10 +30,10 @@ label_five_vals: 12345 array_five_vals: [0x01, 0x02, 0x03, 0x04, 0x05], label_five_vals: 12345 first_array: [0x01, 0x02, 0x03], second_array: [0x04, 0x05, 0x06] arrays_nested: [[0x01, 0x02, 0x03], [0x04, 0x05, 0x06]] -free_lambda: < Field>>, sentinel: 25 -< Field>> -closured_lambda: < Field>>, sentinel: 1 -< Field>> +free_lambda: (< Field>>, < Field>>), sentinel: 8888 +(< Field>>, < Field>>) +closured_lambda: (< Field>>, < Field>>), sentinel: 8888 +(< Field>>, < Field>>) slice_of_tuples: &[(11, 22), (33, 44)], sentinel: 8888 &[(11, 22), (33, 44)] slice_of_tuples: &[(11, 22), (33, 44)], sentinel: 8888 diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_10158/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_10158/execute__tests__expanded.snap new file mode 100644 index 00000000000..1c82f5da2b4 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_10158/execute__tests__expanded.snap @@ -0,0 +1,11 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub fn main() { + println((foo, 10_u32)); +} + +fn foo(_a: u32, _b: bool) -> Field { + 0_Field +} diff --git a/tooling/nargo_cli/tests/snapshots/execution_success/regression_10158/execute__tests__stdout.snap b/tooling/nargo_cli/tests/snapshots/execution_success/regression_10158/execute__tests__stdout.snap new file mode 100644 index 00000000000..44c1553b99b --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/execution_success/regression_10158/execute__tests__stdout.snap @@ -0,0 +1,5 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: stdout +--- +((< Field>>, < Field>>), 10)