From 423c924c0012bb20107dce3154893aec0177ab6c Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 12 Sep 2024 15:56:08 -0500 Subject: [PATCH 1/6] Add CtString --- aztec_macros/src/utils/parse_utils.rs | 2 +- compiler/noirc_frontend/src/ast/traits.rs | 13 ++- compiler/noirc_frontend/src/ast/visitor.rs | 2 +- .../noirc_frontend/src/elaborator/traits.rs | 6 +- .../src/hir/comptime/interpreter/builtin.rs | 36 ++++++- .../interpreter/builtin/builtin_helpers.rs | 7 ++ .../noirc_frontend/src/hir/comptime/value.rs | 7 +- .../src/hir/def_collector/dc_mod.rs | 7 +- compiler/noirc_frontend/src/hir_def/types.rs | 2 + compiler/noirc_frontend/src/lexer/token.rs | 3 + .../src/parser/parser/function.rs | 2 +- .../src/parser/parser/traits.rs | 15 ++- .../noirc_frontend/src/parser/parser/types.rs | 7 ++ .../noir/standard_library/meta/ctstring.md | 71 ++++++++++++++ noir_stdlib/src/meta/ctstring.nr | 95 +++++++++++++++++++ noir_stdlib/src/meta/mod.nr | 1 + .../lsp/src/requests/completion/builtins.rs | 2 + 17 files changed, 260 insertions(+), 18 deletions(-) create mode 100644 docs/docs/noir/standard_library/meta/ctstring.md create mode 100644 noir_stdlib/src/meta/ctstring.nr diff --git a/aztec_macros/src/utils/parse_utils.rs b/aztec_macros/src/utils/parse_utils.rs index 712afbc248b..3503c61702e 100644 --- a/aztec_macros/src/utils/parse_utils.rs +++ b/aztec_macros/src/utils/parse_utils.rs @@ -108,7 +108,7 @@ fn empty_noir_function(noir_function: &mut NoirFunction) { fn empty_trait_item(trait_item: &mut TraitItem) { match trait_item { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } => { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body, is_unconstrained: _, visibility: _, is_comptime: _ } => { empty_ident(name); empty_unresolved_generics(generics); for (name, typ) in parameters.iter_mut() { diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index 0de1dbaa021..b84312e2ecc 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -10,7 +10,7 @@ use crate::ast::{ use crate::macros_api::SecondaryAttribute; use crate::node_interner::TraitId; -use super::{Documented, GenericTypeArgs}; +use super::{Documented, GenericTypeArgs, ItemVisibility}; /// AST node for trait definitions: /// `trait name { ... items ... }` @@ -29,6 +29,9 @@ pub struct NoirTrait { #[derive(Clone, Debug)] pub enum TraitItem { Function { + is_unconstrained: bool, + visibility: ItemVisibility, + is_comptime: bool, name: Ident, generics: UnresolvedGenerics, parameters: Vec<(Ident, UnresolvedType)>, @@ -146,7 +149,7 @@ impl Display for NoirTrait { impl Display for TraitItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } => { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body, is_unconstrained, visibility, is_comptime } => { let generics = vecmap(generics, |generic| generic.to_string()); let parameters = vecmap(parameters, |(name, typ)| format!("{name}: {typ}")); let where_clause = vecmap(where_clause, ToString::to_string); @@ -155,9 +158,13 @@ impl Display for TraitItem { let parameters = parameters.join(", "); let where_clause = where_clause.join(", "); + let unconstrained = if *is_unconstrained { "unconstrained " } else { "" }; + let visibility = if *visibility == ItemVisibility::Private { "".to_string() } else { visibility.to_string() }; + let is_comptime = if *is_comptime { "comptime " } else { "" }; + write!( f, - "fn {name}<{generics}>({parameters}) -> {return_type} where {where_clause}" + "{unconstrained}{visibility}{is_comptime}fn {name}<{generics}>({parameters}) -> {return_type} where {where_clause}" )?; if let Some(body) = body { diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 47083a5043b..82057d551af 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -655,7 +655,7 @@ impl TraitItem { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } => { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body, is_unconstrained: _, visibility: _, is_comptime: _ } => { if visitor.visit_trait_item_function( name, generics, diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index d6bfd3aa647..b6367a8431f 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -74,6 +74,9 @@ impl<'context> Elaborator<'context> { return_type, where_clause, body: _, + is_unconstrained, + visibility: _, + is_comptime: _, } = &item.item { self.recover_generics(|this| { @@ -134,9 +137,8 @@ impl<'context> Elaborator<'context> { }; let no_environment = Box::new(Type::Unit); - // TODO: unconstrained let function_type = - Type::Function(arguments, Box::new(return_type), no_environment, false); + Type::Function(arguments, Box::new(return_type), no_environment, *is_unconstrained); functions.push(TraitFunction { name: name.clone(), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index d7632eda2ce..0f90e259814 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -40,7 +40,7 @@ use crate::{ Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; -use self::builtin_helpers::{eq_item, get_array, get_str, get_u8, hash_item}; +use self::builtin_helpers::{eq_item, get_array, get_str, get_u8, hash_item, get_ctstring}; use super::Interpreter; pub(crate) mod builtin_helpers; @@ -60,6 +60,8 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_len" => array_len(interner, arguments, location), "assert_constant" => Ok(Value::Bool(true)), "as_slice" => as_slice(interner, arguments, location), + "ctstring_eq" => ctstring_eq(arguments, location), + "ctstring_hash" => ctstring_hash(arguments, location), "expr_as_array" => expr_as_array(interner, arguments, return_type, location), "expr_as_assert" => expr_as_assert(interner, arguments, return_type, location), "expr_as_assert_eq" => expr_as_assert_eq(interner, arguments, return_type, location), @@ -97,6 +99,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_as_ctstring" => fmtstr_as_ctstring(interner, arguments, location), "fmtstr_quoted_contents" => fmtstr_quoted_contents(interner, arguments, location), "fresh_type_variable" => fresh_type_variable(interner), "function_def_add_attribute" => function_def_add_attribute(self, arguments, location), @@ -151,6 +154,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "slice_push_front" => slice_push_front(interner, arguments, location), "slice_remove" => slice_remove(interner, arguments, location, call_stack), "str_as_bytes" => str_as_bytes(interner, arguments, location), + "str_as_ctstring" => str_as_ctstring(interner, arguments, location), "struct_def_add_attribute" => struct_def_add_attribute(interner, arguments, location), "struct_def_add_generic" => struct_def_add_generic(interner, arguments, location), "struct_def_as_type" => struct_def_as_type(interner, arguments, location), @@ -297,6 +301,17 @@ fn str_as_bytes( Ok(Value::Array(bytes, byte_array_type)) } +// fn str_as_ctstring(self) -> CtString +fn str_as_ctstring( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let string = get_str(interner, self_argument)?; + Ok(Value::CtString(string)) +} + // fn add_attribute(self, attribute: str) fn struct_def_add_attribute( interner: &mut NodeInterner, @@ -1868,6 +1883,17 @@ fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> Expr expr_value } +// fn fmtstr_as_ctstring(self) -> CtString +fn fmtstr_as_ctstring( + 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)?; + Ok(Value::CtString(string)) +} + // fn quoted_contents(self) -> Quoted fn fmtstr_quoted_contents( interner: &NodeInterner, @@ -2441,3 +2467,11 @@ pub(crate) fn extract_option_generic_type(typ: Type) -> Type { generics.pop().expect("Expected Option to have a T generic type") } + +fn ctstring_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + eq_item(arguments, location, get_ctstring) +} + +fn ctstring_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_ctstring) +} 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 e50bb14b2d6..e1f08644e39 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 @@ -126,6 +126,13 @@ pub(crate) fn get_str( } } +pub(crate) fn get_ctstring((value, location): (Value, Location)) -> IResult> { + match value { + Value::CtString(string) => Ok(string), + value => type_mismatch(value, Type::Quoted(QuotedType::CtString), location), + } +} + pub(crate) fn get_tuple( interner: &NodeInterner, (value, location): (Value, Location), diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 4eee59489a9..8636ede28af 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -49,6 +49,7 @@ pub enum Value { U64(u64), String(Rc), FormatString(Rc, Type), + CtString(Rc), Function(FuncId, Type, Rc), Closure(HirLambda, Vec, Type), Tuple(Vec), @@ -151,6 +152,7 @@ impl Value { Value::Expr(_) => Type::Quoted(QuotedType::Expr), Value::TypedExpr(_) => Type::Quoted(QuotedType::TypedExpr), Value::UnresolvedType(_) => Type::Quoted(QuotedType::UnresolvedType), + Value::CtString(_) => Type::Quoted(QuotedType::CtString), }) } @@ -202,7 +204,7 @@ impl Value { Value::U64(value) => { ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) } - Value::String(value) => ExpressionKind::Literal(Literal::Str(unwrap_rc(value))), + Value::String(value) | Value::CtString(value) => ExpressionKind::Literal(Literal::Str(unwrap_rc(value))), // Format strings are lowered as normal strings since they are already interpolated. Value::FormatString(value, _) => { ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) @@ -349,7 +351,7 @@ impl Value { Value::U64(value) => { HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) } - Value::String(value) => HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))), + Value::String(value) | Value::CtString(value) => HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))), // Format strings are lowered as normal strings since they are already interpolated. Value::FormatString(value, _) => { HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) @@ -589,6 +591,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::U32(value) => write!(f, "{value}"), Value::U64(value) => write!(f, "{value}"), Value::String(value) => write!(f, "{value}"), + Value::CtString(value) => write!(f, "{value}"), Value::FormatString(value, _) => write!(f, "{value}"), Value::Function(..) => write!(f, "(function)"), Value::Closure(_, _, _) => write!(f, "(closure)"), diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 57c7fdd9cdb..ea14f2e3346 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -424,6 +424,9 @@ impl<'a> ModCollector<'a> { return_type, where_clause, body, + is_unconstrained, + visibility: _, + is_comptime, } => { let func_id = context.def_interner.push_empty_fn(); method_ids.insert(name.to_string(), func_id); @@ -434,9 +437,9 @@ impl<'a> ModCollector<'a> { visibility: ItemVisibility::Public, // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 attributes: crate::token::Attributes::empty(), - is_unconstrained: false, + is_unconstrained: *is_unconstrained, generic_count: generics.len(), - is_comptime: false, + is_comptime: *is_comptime, name_location: location, }; diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index bcbcc8ad789..382c158582e 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -161,6 +161,7 @@ pub enum QuotedType { UnresolvedType, FunctionDefinition, Module, + CtString, } /// A list of TypeVariableIds to bind to a type. Storing the @@ -759,6 +760,7 @@ impl std::fmt::Display for QuotedType { QuotedType::UnresolvedType => write!(f, "UnresolvedType"), QuotedType::FunctionDefinition => write!(f, "FunctionDefinition"), QuotedType::Module => write!(f, "Module"), + QuotedType::CtString => write!(f, "CtString"), } } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 3b615e91f8f..6bc1295ca0b 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -983,6 +983,7 @@ pub enum Keyword { Continue, Contract, Crate, + CtString, Dep, Else, Expr, @@ -1039,6 +1040,7 @@ impl fmt::Display for Keyword { Keyword::Continue => write!(f, "continue"), Keyword::Contract => write!(f, "contract"), Keyword::Crate => write!(f, "crate"), + Keyword::CtString => write!(f, "CtString"), Keyword::Dep => write!(f, "dep"), Keyword::Else => write!(f, "else"), Keyword::Expr => write!(f, "Expr"), @@ -1098,6 +1100,7 @@ impl Keyword { "continue" => Keyword::Continue, "contract" => Keyword::Contract, "crate" => Keyword::Crate, + "CtString" => Keyword::CtString, "dep" => Keyword::Dep, "else" => Keyword::Else, "Expr" => Keyword::Expr, diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 05138bfffd9..774c4a8b3c8 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -78,7 +78,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser<(bool, ItemVisibility, bool)> { +pub(super) fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { keyword(Keyword::Unconstrained).or_not().then(item_visibility()).then(maybe_comp_time()).map( |((unconstrained, visibility), comptime)| (unconstrained.is_some(), visibility, comptime), ) diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 58db2465f22..cb5bc5dac5b 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -2,7 +2,7 @@ use chumsky::prelude::*; use super::attributes::{attributes, validate_secondary_attributes}; use super::doc_comments::outer_doc_comments; -use super::function::function_return_type; +use super::function::{function_return_type, function_modifiers}; use super::path::path_no_turbofish; use super::{ block, expression, fresh_statement, function, function_declaration_parameters, let_statement, @@ -101,15 +101,20 @@ fn trait_function_declaration() -> impl NoirParser { } }); - keyword(Keyword::Fn) - .ignore_then(ident()) + function_modifiers() + .then_ignore(keyword(Keyword::Fn)) + .then(ident()) .then(function::generics()) .then(parenthesized(function_declaration_parameters())) .then(function_return_type().map(|(_, typ)| typ)) .then(where_clause()) .then(trait_function_body_or_semicolon_or_error) - .map(|(((((name, generics), parameters), return_type), where_clause), body)| { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } + .map(|((((((modifiers, name), generics), parameters), return_type), where_clause), body)| { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body, + is_unconstrained: modifiers.0, + visibility: modifiers.1, + is_comptime: modifiers.2, + } }) } diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 9dabb8b83b6..d4458190d03 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -89,6 +89,7 @@ pub(super) fn comptime_type() -> impl NoirParser { top_level_item_type(), quoted_type(), typed_expr_type(), + comptime_string_type(), )) } @@ -166,6 +167,12 @@ fn typed_expr_type() -> impl NoirParser { .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TypedExpr).with_span(span)) } +/// This is the `CtString` type for dynamically-sized compile-time strings +fn comptime_string_type() -> impl NoirParser { + keyword(Keyword::CtString) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::CtString).with_span(span)) +} + /// This is the type of an already resolved type. /// The only way this can appear in the token input is if an already resolved `Type` object /// was spliced into a macro's token stream via the `$` operator. diff --git a/docs/docs/noir/standard_library/meta/ctstring.md b/docs/docs/noir/standard_library/meta/ctstring.md new file mode 100644 index 00000000000..30c257120a3 --- /dev/null +++ b/docs/docs/noir/standard_library/meta/ctstring.md @@ -0,0 +1,71 @@ +--- +title: CtString +--- + +`std::meta::ctstring` contains methods on the built-in `CtString` type which is +a compile-time, dynamically-sized string type. Compared to `str` and `fmtstr`, +`CtString` is useful because its size does not need to be specified in its type. This +can be used for formatting items at compile-time or general string handling in `comptime` +code. + +Since `fmtstr`s can be converted into `CtString`s, you can make use of their formatting +abilities in CtStrings by formatting in `fmtstr`s then converting the result to a CtString +afterward. + +## Traits + +### AsCtString + +#include_code as-ctstring noir_stdlib/src/meta/ctstring.nr rust + +Converts an object into a compile-time string. + +Implementations: + +```rust +impl AsCtString for str { ... } +impl AsCtString for fmtstr { ... } +``` + +## Methods + +### new + +#include_code new noir_stdlib/src/meta/ctstring.nr rust + +Creates an empty `CtString`. + +### append_str + +#include_code append_str noir_stdlib/src/meta/ctstring.nr rust + +Returns a new CtString with the given str appended onto the end. + +### append_fmtstr + +#include_code append_fmtstr noir_stdlib/src/meta/ctstring.nr rust + +Returns a new CtString with the given fmtstr appended onto the end. + +### as_quoted_str + +#include_code as_quoted_str noir_stdlib/src/meta/ctstring.nr rust + +Returns a quoted string literal from this string's contents. + +There is no direct conversion from a `CtString` to a `str` since +the size would not be known. To get around this, this function can +be used in combination with macro insertion (`!`) to insert this string +literal at this function's call site. + +Example: + +#include_code as_quoted_str_example noir_stdlib/src/meta/ctstring.nr rust + +## Trait Implementations + +```rust +impl Eq for CtString +impl Hash for CtString +impl Append for CtString +``` diff --git a/noir_stdlib/src/meta/ctstring.nr b/noir_stdlib/src/meta/ctstring.nr new file mode 100644 index 00000000000..f046d2d7354 --- /dev/null +++ b/noir_stdlib/src/meta/ctstring.nr @@ -0,0 +1,95 @@ +use crate::append::Append; + +impl CtString { + // docs:start:new + comptime fn new() -> Self { + // docs::end::new + "".as_ctstring() + } + + // Bug: using &mut self as the object results in this method not being found + // docs:start:append_str + comptime fn append_str(self, s: str) -> Self { + // docs:end:append_str + f"{self}{s}".as_ctstring() + } + + // docs:start:append_fmtstr + comptime fn append_fmtstr(self, s: fmtstr) -> Self { + // docs:end:append_fmtstr + f"{self}{s}".as_ctstring() + } + + /// CtString cannot directly return a str since the size would not be known. + /// To get around this, we return a quoted str and the underlying str can + /// be accessed using macro insertion `foo.as_quoted_str!()`. + // docs:start:as_quoted_str + comptime fn as_quoted_str(self) -> Quoted { + // docs:end:as_quoted_str + quote { $self } + } +} + +impl Append for CtString { + fn empty() -> Self { + "".as_ctstring() + } + + fn append(self, other: Self) -> Self { + f"{self}{other}".as_ctstring() + } +} + +// docs:start:as-ctstring +trait AsCtString { + comptime fn as_ctstring(self) -> CtString; +} +// docs:end:as-ctstring + +impl AsCtString for str { + comptime fn as_ctstring(self) -> CtString { + str_as_ctstring(self) + } +} + +impl AsCtString for fmtstr { + comptime fn as_ctstring(self) -> CtString { + fmtstr_as_ctstring(self) + } +} + +impl crate::cmp::Eq for CtString { + comptime fn eq(self, other: Self) -> bool { + ctstring_eq(self, other) + } +} + +impl crate::hash::Hash for CtString { + comptime fn hash(self, state: &mut H) where H: crate::hash::Hasher { + state.write(ctstring_hash(self)); + } +} + +#[builtin(str_as_ctstring)] +comptime fn str_as_ctstring(_s: str) -> CtString {} + +#[builtin(fmtstr_as_ctstring)] +comptime fn fmtstr_as_ctstring(_s: fmtstr) -> CtString {} + +#[builtin(ctstring_eq)] +comptime fn ctstring_eq(_first: CtString, _second: CtString) -> bool {} + +#[builtin(ctstring_hash)] +comptime fn ctstring_hash(_string: CtString) -> Field {} + +mod test { + #[test] + comptime fn as_quoted_str_example() { + // docs:start:as_quoted_str_example + let my_ctstring = "foo bar".as_ctstring(); + let my_str = my_ctstring.as_quoted_str!(); + + assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); + // docs:end:as_quoted_str_example + } +} diff --git a/noir_stdlib/src/meta/mod.nr b/noir_stdlib/src/meta/mod.nr index 1079cc6013a..44e8ddc17fc 100644 --- a/noir_stdlib/src/meta/mod.nr +++ b/noir_stdlib/src/meta/mod.nr @@ -1,3 +1,4 @@ +mod ctstring; mod expr; mod format_string; mod function_def; diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index 520c158d260..6812ebc135b 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -128,6 +128,7 @@ pub(super) fn builtin_integer_types() -> [&'static str; 8] { pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { match keyword { Keyword::Bool => Some("bool"), + Keyword::CtString => Some("CtString"), Keyword::Expr => Some("Expr"), Keyword::Field => Some("Field"), Keyword::FunctionDefinition => Some("FunctionDefinition"), @@ -212,6 +213,7 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option Date: Thu, 12 Sep 2024 15:59:49 -0500 Subject: [PATCH 2/6] Add test --- noir_stdlib/src/meta/ctstring.nr | 16 +++++++++------- .../compile_success_empty/ctstring/Nargo.toml | 7 +++++++ .../compile_success_empty/ctstring/src/main.nr | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 test_programs/compile_success_empty/ctstring/Nargo.toml create mode 100644 test_programs/compile_success_empty/ctstring/src/main.nr diff --git a/noir_stdlib/src/meta/ctstring.nr b/noir_stdlib/src/meta/ctstring.nr index f046d2d7354..d96caf2ca67 100644 --- a/noir_stdlib/src/meta/ctstring.nr +++ b/noir_stdlib/src/meta/ctstring.nr @@ -84,12 +84,14 @@ comptime fn ctstring_hash(_string: CtString) -> Field {} mod test { #[test] - comptime fn as_quoted_str_example() { - // docs:start:as_quoted_str_example - let my_ctstring = "foo bar".as_ctstring(); - let my_str = my_ctstring.as_quoted_str!(); - - assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); - // docs:end:as_quoted_str_example + fn as_quoted_str_example() { + comptime { + // docs:start:as_quoted_str_example + let my_ctstring = "foo bar".as_ctstring(); + let my_str = my_ctstring.as_quoted_str!(); + + assert_eq(crate::meta::type_of(my_str), quote { str<7> }.as_type()); + // docs:end:as_quoted_str_example + } } } diff --git a/test_programs/compile_success_empty/ctstring/Nargo.toml b/test_programs/compile_success_empty/ctstring/Nargo.toml new file mode 100644 index 00000000000..493fd8a6e4b --- /dev/null +++ b/test_programs/compile_success_empty/ctstring/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "ctstring" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/ctstring/src/main.nr b/test_programs/compile_success_empty/ctstring/src/main.nr new file mode 100644 index 00000000000..61cd848a436 --- /dev/null +++ b/test_programs/compile_success_empty/ctstring/src/main.nr @@ -0,0 +1,17 @@ +fn main() { + comptime { + let msg1 = "msg1"; + let msg2 = f" (msg2 contains {msg1}) "; + + let s1 = msg1.as_ctstring(); + let s2 = msg2.as_ctstring(); + + let s3 = &[s1, s2, s1].join("".as_ctstring()); + assert_eq(s3, "msg1 (msg2 contains msg1) msg1".as_ctstring()); + + let mut s4 = s1; + s4 = s4.append_fmtstr(msg2); + s4 = s4.append_str(msg1); + assert_eq(s3, s4); + } +} From 0bf5dfe6aec956e1b68f79538e011ada08df45bf Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 12 Sep 2024 16:00:20 -0500 Subject: [PATCH 3/6] Format --- aztec_macros/src/utils/parse_utils.rs | 12 +++++++++- compiler/noirc_frontend/src/ast/traits.rs | 18 ++++++++++++-- compiler/noirc_frontend/src/ast/visitor.rs | 12 +++++++++- .../noirc_frontend/src/elaborator/traits.rs | 8 +++++-- .../src/hir/comptime/interpreter/builtin.rs | 2 +- .../noirc_frontend/src/hir/comptime/value.rs | 8 +++++-- .../src/parser/parser/traits.rs | 24 ++++++++++++------- 7 files changed, 67 insertions(+), 17 deletions(-) diff --git a/aztec_macros/src/utils/parse_utils.rs b/aztec_macros/src/utils/parse_utils.rs index 3503c61702e..cdad842dd05 100644 --- a/aztec_macros/src/utils/parse_utils.rs +++ b/aztec_macros/src/utils/parse_utils.rs @@ -108,7 +108,17 @@ fn empty_noir_function(noir_function: &mut NoirFunction) { fn empty_trait_item(trait_item: &mut TraitItem) { match trait_item { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body, is_unconstrained: _, visibility: _, is_comptime: _ } => { + TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause, + body, + is_unconstrained: _, + visibility: _, + is_comptime: _, + } => { empty_ident(name); empty_unresolved_generics(generics); for (name, typ) in parameters.iter_mut() { diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index b84312e2ecc..9d3bd0dfa77 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -149,7 +149,17 @@ impl Display for NoirTrait { impl Display for TraitItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body, is_unconstrained, visibility, is_comptime } => { + TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause, + body, + is_unconstrained, + visibility, + is_comptime, + } => { let generics = vecmap(generics, |generic| generic.to_string()); let parameters = vecmap(parameters, |(name, typ)| format!("{name}: {typ}")); let where_clause = vecmap(where_clause, ToString::to_string); @@ -159,7 +169,11 @@ impl Display for TraitItem { let where_clause = where_clause.join(", "); let unconstrained = if *is_unconstrained { "unconstrained " } else { "" }; - let visibility = if *visibility == ItemVisibility::Private { "".to_string() } else { visibility.to_string() }; + let visibility = if *visibility == ItemVisibility::Private { + "".to_string() + } else { + visibility.to_string() + }; let is_comptime = if *is_comptime { "comptime " } else { "" }; write!( diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 82057d551af..755b3bc670e 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -655,7 +655,17 @@ impl TraitItem { pub fn accept_children(&self, visitor: &mut impl Visitor) { match self { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body, is_unconstrained: _, visibility: _, is_comptime: _ } => { + TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause, + body, + is_unconstrained: _, + visibility: _, + is_comptime: _, + } => { if visitor.visit_trait_item_function( name, generics, diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index b6367a8431f..66ed9c8acce 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -137,8 +137,12 @@ impl<'context> Elaborator<'context> { }; let no_environment = Box::new(Type::Unit); - let function_type = - Type::Function(arguments, Box::new(return_type), no_environment, *is_unconstrained); + let function_type = Type::Function( + arguments, + Box::new(return_type), + no_environment, + *is_unconstrained, + ); functions.push(TraitFunction { name: name.clone(), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 0f90e259814..aec89738439 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -40,7 +40,7 @@ use crate::{ Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; -use self::builtin_helpers::{eq_item, get_array, get_str, get_u8, hash_item, get_ctstring}; +use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item}; use super::Interpreter; pub(crate) mod builtin_helpers; diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 8636ede28af..d517b6fab38 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -204,7 +204,9 @@ impl Value { Value::U64(value) => { ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) } - Value::String(value) | Value::CtString(value) => ExpressionKind::Literal(Literal::Str(unwrap_rc(value))), + Value::String(value) | Value::CtString(value) => { + ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) + } // Format strings are lowered as normal strings since they are already interpolated. Value::FormatString(value, _) => { ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) @@ -351,7 +353,9 @@ impl Value { Value::U64(value) => { HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) } - Value::String(value) | Value::CtString(value) => HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))), + Value::String(value) | Value::CtString(value) => { + HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) + } // Format strings are lowered as normal strings since they are already interpolated. Value::FormatString(value, _) => { HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index cb5bc5dac5b..51d2d858319 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -2,7 +2,7 @@ use chumsky::prelude::*; use super::attributes::{attributes, validate_secondary_attributes}; use super::doc_comments::outer_doc_comments; -use super::function::{function_return_type, function_modifiers}; +use super::function::{function_modifiers, function_return_type}; use super::path::path_no_turbofish; use super::{ block, expression, fresh_statement, function, function_declaration_parameters, let_statement, @@ -109,13 +109,21 @@ fn trait_function_declaration() -> impl NoirParser { .then(function_return_type().map(|(_, typ)| typ)) .then(where_clause()) .then(trait_function_body_or_semicolon_or_error) - .map(|((((((modifiers, name), generics), parameters), return_type), where_clause), body)| { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body, - is_unconstrained: modifiers.0, - visibility: modifiers.1, - is_comptime: modifiers.2, - } - }) + .map( + |((((((modifiers, name), generics), parameters), return_type), where_clause), body)| { + TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause, + body, + is_unconstrained: modifiers.0, + visibility: modifiers.1, + is_comptime: modifiers.2, + } + }, + ) } /// trait_type_declaration: 'type' ident generics From 89bd3af486861272413982de5901bbbcce0852c2 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 12 Sep 2024 16:00:38 -0500 Subject: [PATCH 4/6] Format test --- test_programs/compile_success_empty/ctstring/src/main.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_programs/compile_success_empty/ctstring/src/main.nr b/test_programs/compile_success_empty/ctstring/src/main.nr index 61cd848a436..b2329cb1997 100644 --- a/test_programs/compile_success_empty/ctstring/src/main.nr +++ b/test_programs/compile_success_empty/ctstring/src/main.nr @@ -1,5 +1,6 @@ fn main() { - comptime { + comptime + { let msg1 = "msg1"; let msg2 = f" (msg2 contains {msg1}) "; From 6681da9afc7170a7f71c87f022db80f1baea629a Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 12 Sep 2024 16:00:53 -0500 Subject: [PATCH 5/6] Format stdlib --- noir_stdlib/src/meta/ctstring.nr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/noir_stdlib/src/meta/ctstring.nr b/noir_stdlib/src/meta/ctstring.nr index d96caf2ca67..0ef8ac67cfb 100644 --- a/noir_stdlib/src/meta/ctstring.nr +++ b/noir_stdlib/src/meta/ctstring.nr @@ -85,7 +85,8 @@ comptime fn ctstring_hash(_string: CtString) -> Field {} mod test { #[test] fn as_quoted_str_example() { - comptime { + comptime + { // docs:start:as_quoted_str_example let my_ctstring = "foo bar".as_ctstring(); let my_str = my_ctstring.as_quoted_str!(); From 29d051b4a9f8c07e706142301cb3afd14f8a9631 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 12 Sep 2024 16:04:03 -0500 Subject: [PATCH 6/6] Add ctstring to cspell --- cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/cspell.json b/cspell.json index 4d83c535e7d..3ace51689fb 100644 --- a/cspell.json +++ b/cspell.json @@ -56,6 +56,7 @@ "cranelift", "critesjosh", "csat", + "ctstring", "curvegroup", "databus", "debouncer",