From 729c5eacb30d08752c058d5839aa8bb53e39483d Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 14:54:09 -0300 Subject: [PATCH 1/9] Special handling of interpolating quoted value inside format string --- .../src/hir/comptime/interpreter.rs | 14 +++- .../noirc_frontend/src/hir/comptime/value.rs | 73 ++++++++++++------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index d8e62b66eca..9f559b7c5e6 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -586,7 +586,19 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { consuming = false; if let Some(value) = values.pop_front() { - result.push_str(&value.display(self.elaborator.interner).to_string()); + // When interpolating a quoted value inside a format string, we don't include the + // surrounding `quote {` ... `}` as if we are unquoting the quoted value inside the string. + if let Value::Quoted(tokens) = value { + for (index, token) in tokens.iter().enumerate() { + if index > 0 { + result.push(' '); + } + result + .push_str(&token.display(self.elaborator.interner).to_string()); + } + } else { + result.push_str(&value.display(self.elaborator.interner).to_string()); + } } } other if !consuming => { diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index c5818c20c57..5210bd76ef7 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -568,33 +568,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { write!(f, "quote {{")?; for token in tokens.iter() { write!(f, " ")?; - - match token { - Token::QuotedType(id) => { - write!(f, "{}", self.interner.get_quoted_type(*id))?; - } - Token::InternedExpr(id) => { - let value = Value::expression(ExpressionKind::Interned(*id)); - value.display(self.interner).fmt(f)?; - } - Token::InternedStatement(id) => { - let value = Value::statement(StatementKind::Interned(*id)); - value.display(self.interner).fmt(f)?; - } - Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); - value.display(self.interner).fmt(f)?; - } - Token::InternedUnresolvedTypeData(id) => { - let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); - value.display(self.interner).fmt(f)?; - } - Token::UnquoteMarker(id) => { - let value = Value::TypedExpr(TypedExpr::ExprId(*id)); - value.display(self.interner).fmt(f)?; - } - other => write!(f, "{other}")?, - } + token.display(self.interner).fmt(f)?; } write!(f, " }}") } @@ -676,6 +650,51 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { } } +impl Token { + pub fn display<'token, 'interner>( + &'token self, + interner: &'interner NodeInterner, + ) -> TokenPrinter<'token, 'interner> { + TokenPrinter { token: self, interner } + } +} + +pub struct TokenPrinter<'token, 'interner> { + token: &'token Token, + interner: &'interner NodeInterner, +} + +impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.token { + Token::QuotedType(id) => { + write!(f, "{}", self.interner.get_quoted_type(*id)) + } + Token::InternedExpr(id) => { + let value = Value::expression(ExpressionKind::Interned(*id)); + value.display(self.interner).fmt(f) + } + Token::InternedStatement(id) => { + let value = Value::statement(StatementKind::Interned(*id)); + value.display(self.interner).fmt(f) + } + Token::InternedLValue(id) => { + let value = Value::lvalue(LValue::Interned(*id, Span::default())); + value.display(self.interner).fmt(f) + } + Token::InternedUnresolvedTypeData(id) => { + let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); + value.display(self.interner).fmt(f) + } + Token::UnquoteMarker(id) => { + let value = Value::TypedExpr(TypedExpr::ExprId(*id)); + value.display(self.interner).fmt(f) + } + other => write!(f, "{other}"), + } + } +} + fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitConstraint) -> String { let trait_ = interner.get_trait(trait_constraint.trait_id); format!("{}: {}{}", trait_constraint.typ, trait_.name, trait_constraint.trait_generics) From a42b952aa70f9776d4ad3eb1962789d9d559e991 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 15:12:06 -0300 Subject: [PATCH 2/9] WIP: fmtstring::as_identifier --- noir_stdlib/src/meta/format_string.nr | 7 +++++++ noir_stdlib/src/meta/mod.nr | 1 + 2 files changed, 8 insertions(+) create mode 100644 noir_stdlib/src/meta/format_string.nr diff --git a/noir_stdlib/src/meta/format_string.nr b/noir_stdlib/src/meta/format_string.nr new file mode 100644 index 00000000000..7ea97949941 --- /dev/null +++ b/noir_stdlib/src/meta/format_string.nr @@ -0,0 +1,7 @@ +use crate::option::Option; + +impl fmtstr { + fn as_identifier(self) -> Option { + Option::none() + } +} diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index 24398054467..9fc399ddbf9 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -1,4 +1,5 @@ mod expr; +mod format_string; mod function_def; mod module; mod op; From 010f8d903fa36df721ab106a88f896a1bd1cfb34 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 15:57:54 -0300 Subject: [PATCH 3/9] feat: add `fmtstr::contents` --- .../src/hir/comptime/interpreter/builtin.rs | 25 ++++++++++++++++--- .../interpreter/builtin/builtin_helpers.rs | 14 +++++++++++ .../docs/noir/standard_library/meta/fmtstr.md | 13 ++++++++++ noir_stdlib/src/meta/format_string.nr | 9 +++---- .../comptime_fmt_strings/src/main.nr | 14 +++++++++++ 5 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 docs/docs/noir/standard_library/meta/fmtstr.md diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 070749e45ba..2476b038849 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -7,9 +7,9 @@ use acvm::{AcirField, FieldElement}; use builtin_helpers::{ block_expression_to_value, check_argument_count, check_function_not_yet_resolved, check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field, - get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, - get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, get_u32, - get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse, + get_format_string, get_function_def, get_module, get_quoted, get_slice, get_struct, + get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, + get_u32, get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse, replace_func_meta_parameters, replace_func_meta_return_type, }; use chumsky::{prelude::choice, Parser}; @@ -32,6 +32,7 @@ use crate::{ InterpreterError, Value, }, hir_def::function::FunctionBody, + lexer::Lexer, macros_api::{HirExpression, HirLiteral, ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, parser::{self}, @@ -95,6 +96,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_is_continue" => expr_is_continue(interner, arguments, location), "expr_resolve" => expr_resolve(self, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), + "fmtstr_contents" => fmtstr_contents(interner, arguments, location), "function_def_body" => function_def_body(interner, arguments, location), "function_def_has_named_attribute" => { function_def_has_named_attribute(interner, arguments, location) @@ -1575,6 +1577,23 @@ fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> Expr expr_value } +// fn quoted(self) -> Quoted +fn fmtstr_contents( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let (string, _) = get_format_string(interner, self_argument)?; + let (tokens, _) = Lexer::lex(&string); + let mut tokens: Vec<_> = tokens.0.into_iter().map(|token| token.into_token()).collect(); + if let Some(Token::EOF) = tokens.last() { + tokens.pop(); + } + + Ok(Value::Quoted(Rc::new(tokens))) +} + // fn body(self) -> Expr fn function_def_body( interner: &NodeInterner, diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 14a0e177544..ff3da6d253f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -189,6 +189,20 @@ pub(crate) fn get_expr( } } +pub(crate) fn get_format_string( + interner: &NodeInterner, + (value, location): (Value, Location), +) -> IResult<(Rc, Type)> { + match value { + Value::FormatString(value, typ) => Ok((value, typ)), + value => { + let n = Box::new(interner.next_type_variable()); + let e = Box::new(interner.next_type_variable()); + type_mismatch(value, Type::FmtString(n, e), location) + } + } +} + pub(crate) fn get_function_def((value, location): (Value, Location)) -> IResult { match value { Value::FunctionDefinition(id) => Ok(id), diff --git a/docs/docs/noir/standard_library/meta/fmtstr.md b/docs/docs/noir/standard_library/meta/fmtstr.md new file mode 100644 index 00000000000..61060d88597 --- /dev/null +++ b/docs/docs/noir/standard_library/meta/fmtstr.md @@ -0,0 +1,13 @@ +--- +title: fmtstr +--- + +`std::meta::format_string` contains comptime methods on the `fmtstr` type for format strings. + +## Methods + +### contents + +#include_code quoted noir_stdlib/src/meta/format_string.nr rust + +Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/noir_stdlib/src/meta/format_string.nr b/noir_stdlib/src/meta/format_string.nr index 7ea97949941..ab6cf334426 100644 --- a/noir_stdlib/src/meta/format_string.nr +++ b/noir_stdlib/src/meta/format_string.nr @@ -1,7 +1,6 @@ -use crate::option::Option; - impl fmtstr { - fn as_identifier(self) -> Option { - Option::none() - } + #[builtin(fmtstr_contents)] + // docs:start:contents + fn contents(self) -> Quoted {} + // docs:end:contents } diff --git a/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr b/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr index 705a1b2ab4e..528b61a2482 100644 --- a/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr +++ b/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr @@ -12,4 +12,18 @@ fn main() { }; assert_eq(s1, "x is 4, fake interpolation: {y}, y is 5"); assert_eq(s2, "\0\0\0\0"); + + // Mainly test fmtstr::contents + call!(glue(quote { hello }, quote { world })); +} + +fn glue(x: Quoted, y: Quoted) -> Quoted { + f"{x}_{y}".contents() } + +fn hello_world() {} + +comptime fn call(x: Quoted) -> Quoted { + quote { $x() } +} + From cc622d119c5f962a53cf676ad054ce09807defe0 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 16:13:00 -0300 Subject: [PATCH 4/9] Fix docs --- docs/docs/noir/standard_library/meta/fmtstr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/noir/standard_library/meta/fmtstr.md b/docs/docs/noir/standard_library/meta/fmtstr.md index 61060d88597..31d5cd0b42f 100644 --- a/docs/docs/noir/standard_library/meta/fmtstr.md +++ b/docs/docs/noir/standard_library/meta/fmtstr.md @@ -8,6 +8,6 @@ title: fmtstr ### contents -#include_code quoted noir_stdlib/src/meta/format_string.nr rust +#include_code contents noir_stdlib/src/meta/format_string.nr rust Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file From 357d48f13e206eaab70a439601dcbef35ae3f033 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 16:29:56 -0300 Subject: [PATCH 5/9] Fix completion test --- tooling/lsp/src/requests/completion/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/lsp/src/requests/completion/tests.rs b/tooling/lsp/src/requests/completion/tests.rs index ca959f5d5ca..a7cfa77a73d 100644 --- a/tooling/lsp/src/requests/completion/tests.rs +++ b/tooling/lsp/src/requests/completion/tests.rs @@ -336,7 +336,7 @@ mod completion_tests { fo>|< } "#; - assert_completion(src, vec![module_completion_item("foobar")]).await; + assert_completion_excluding_auto_import(src, vec![module_completion_item("foobar")]).await; } #[test] From e393775837ca8cea92d5188ab54caa2f02f69d57 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 16:45:03 -0300 Subject: [PATCH 6/9] Fix comment --- compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 2476b038849..064e67ec93e 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1577,7 +1577,7 @@ fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> Expr expr_value } -// fn quoted(self) -> Quoted +// fn contents(self) -> Quoted fn fmtstr_contents( interner: &NodeInterner, arguments: Vec<(Value, Location)>, From 1ea3eabe1c979ea27fe44f26a3cd107f8125a4fd Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 17:18:58 -0300 Subject: [PATCH 7/9] nargo fmt --- .../compile_success_empty/unquote_struct/src/main.nr | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test_programs/compile_success_empty/unquote_struct/src/main.nr b/test_programs/compile_success_empty/unquote_struct/src/main.nr index e90711dd710..603440b5c76 100644 --- a/test_programs/compile_success_empty/unquote_struct/src/main.nr +++ b/test_programs/compile_success_empty/unquote_struct/src/main.nr @@ -10,11 +10,13 @@ fn foo(x: Field, y: u32) -> u32 { // Given a function, wrap its parameters in a struct definition comptime fn output_struct(f: FunctionDefinition) -> Quoted { - let fields = f.parameters().map(|param: (Quoted, Type)| { + let fields = f.parameters().map( + |param: (Quoted, Type)| { let name = param.0; let typ = param.1; quote { $name: $typ, } - }).join(quote {}); + } + ).join(quote {}); quote { struct Foo { $fields } From b6d74397ede2f2ea1e9fd859e3a5262a8e0555fc Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 17:26:11 -0300 Subject: [PATCH 8/9] Move doc file --- docs/docs/noir/standard_library/{meta => }/fmtstr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/docs/noir/standard_library/{meta => }/fmtstr.md (70%) diff --git a/docs/docs/noir/standard_library/meta/fmtstr.md b/docs/docs/noir/standard_library/fmtstr.md similarity index 70% rename from docs/docs/noir/standard_library/meta/fmtstr.md rename to docs/docs/noir/standard_library/fmtstr.md index 31d5cd0b42f..49c474db027 100644 --- a/docs/docs/noir/standard_library/meta/fmtstr.md +++ b/docs/docs/noir/standard_library/fmtstr.md @@ -2,7 +2,7 @@ title: fmtstr --- -`std::meta::format_string` contains comptime methods on the `fmtstr` type for format strings. +`fmtstr` is the type resulting from using format string (`f"..."`). ## Methods From da830ae6fe2e877b96e0bd2f7c83dc9dca615037 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Wed, 4 Sep 2024 17:27:33 -0300 Subject: [PATCH 9/9] contents -> quoted_contents --- .../src/hir/comptime/interpreter/builtin.rs | 6 +++--- docs/docs/noir/standard_library/fmtstr.md | 4 ++-- noir_stdlib/src/meta/format_string.nr | 8 ++++---- .../comptime_fmt_strings/src/main.nr | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 54dfe2847ae..d2c9e4ffc0c 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -96,7 +96,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_is_continue" => expr_is_continue(interner, arguments, location), "expr_resolve" => expr_resolve(self, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), - "fmtstr_contents" => fmtstr_contents(interner, arguments, location), + "fmtstr_quoted_contents" => fmtstr_quoted_contents(interner, arguments, location), "function_def_body" => function_def_body(interner, arguments, location), "function_def_has_named_attribute" => { function_def_has_named_attribute(interner, arguments, location) @@ -1578,8 +1578,8 @@ fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> Expr expr_value } -// fn contents(self) -> Quoted -fn fmtstr_contents( +// fn quoted_contents(self) -> Quoted +fn fmtstr_quoted_contents( interner: &NodeInterner, arguments: Vec<(Value, Location)>, location: Location, diff --git a/docs/docs/noir/standard_library/fmtstr.md b/docs/docs/noir/standard_library/fmtstr.md index 49c474db027..293793e23ff 100644 --- a/docs/docs/noir/standard_library/fmtstr.md +++ b/docs/docs/noir/standard_library/fmtstr.md @@ -6,8 +6,8 @@ title: fmtstr ## Methods -### contents +### quoted_contents -#include_code contents noir_stdlib/src/meta/format_string.nr rust +#include_code quoted_contents noir_stdlib/src/meta/format_string.nr rust Returns the format string contents (that is, without the leading and trailing double quotes) as a `Quoted` value. \ No newline at end of file diff --git a/noir_stdlib/src/meta/format_string.nr b/noir_stdlib/src/meta/format_string.nr index ab6cf334426..44b69719efe 100644 --- a/noir_stdlib/src/meta/format_string.nr +++ b/noir_stdlib/src/meta/format_string.nr @@ -1,6 +1,6 @@ impl fmtstr { - #[builtin(fmtstr_contents)] - // docs:start:contents - fn contents(self) -> Quoted {} - // docs:end:contents + #[builtin(fmtstr_quoted_contents)] + // docs:start:quoted_contents + fn quoted_contents(self) -> Quoted {} + // docs:end:quoted_contents } diff --git a/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr b/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr index 528b61a2482..0e2d459a00f 100644 --- a/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr +++ b/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr @@ -13,12 +13,12 @@ fn main() { assert_eq(s1, "x is 4, fake interpolation: {y}, y is 5"); assert_eq(s2, "\0\0\0\0"); - // Mainly test fmtstr::contents + // Mainly test fmtstr::quoted_contents call!(glue(quote { hello }, quote { world })); } fn glue(x: Quoted, y: Quoted) -> Quoted { - f"{x}_{y}".contents() + f"{x}_{y}".quoted_contents() } fn hello_world() {}