Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add a comptime string type for string handling at compile-time #6026

Merged
merged 6 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion aztec_macros/src/utils/parse_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 } => {
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() {
Expand Down
27 changes: 24 additions & 3 deletions compiler/noirc_frontend/src/ast/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<generics> { ... items ... }`
Expand All @@ -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)>,
Expand Down Expand Up @@ -146,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 } => {
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);
Expand All @@ -155,9 +168,17 @@ 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 {
Expand Down
12 changes: 11 additions & 1 deletion compiler/noirc_frontend/src/ast/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
InternedUnresolvedTypeData, QuotedTypeId,
},
parser::{Item, ItemKind, ParsedSubModule},
token::{CustomAtrribute, SecondaryAttribute, Tokens},

Check warning on line 19 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
ParsedModule, QuotedType,
};

Expand Down Expand Up @@ -461,7 +461,7 @@
true
}

fn visit_custom_attribute(&mut self, _: &CustomAtrribute, _target: AttributeTarget) {}

Check warning on line 464 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
}

impl ParsedModule {
Expand Down Expand Up @@ -655,7 +655,17 @@

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,
Expand Down Expand Up @@ -1266,8 +1276,8 @@
UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.span),
UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.span),
UnresolvedTypeData::FieldElement => visitor.visit_field_element_type(self.span),
UnresolvedTypeData::Integer(signdness, size) => {

Check warning on line 1279 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (signdness)
visitor.visit_integer_type(*signdness, *size, self.span);

Check warning on line 1280 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (signdness)
}
UnresolvedTypeData::Bool => visitor.visit_bool_type(self.span),
UnresolvedTypeData::Unit => visitor.visit_unit_type(self.span),
Expand Down Expand Up @@ -1367,7 +1377,7 @@
}
}

impl CustomAtrribute {

Check warning on line 1380 in compiler/noirc_frontend/src/ast/visitor.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Atrribute)
pub fn accept(&self, target: AttributeTarget, visitor: &mut impl Visitor) {
visitor.visit_custom_attribute(self, target);
}
Expand Down
12 changes: 9 additions & 3 deletions compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ impl<'context> Elaborator<'context> {
return_type,
where_clause,
body: _,
is_unconstrained,
visibility: _,
is_comptime: _,
} = &item.item
{
self.recover_generics(|this| {
Expand Down Expand Up @@ -134,9 +137,12 @@ 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);
let function_type = Type::Function(
arguments,
Box::new(return_type),
no_environment,
*is_unconstrained,
);

functions.push(TraitFunction {
name: name.clone(),
Expand Down
36 changes: 35 additions & 1 deletion compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_ctstring, get_str, get_u8, hash_item};
use super::Interpreter;

pub(crate) mod builtin_helpers;
Expand All @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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<Value> {
let self_argument = check_one_argument(arguments, location)?;
let string = get_str(interner, self_argument)?;
Ok(Value::CtString(string))
}

// fn add_attribute<let N: u32>(self, attribute: str<N>)
fn struct_def_add_attribute(
interner: &mut NodeInterner,
Expand Down Expand Up @@ -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<Value> {
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,
Expand Down Expand Up @@ -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<Value> {
eq_item(arguments, location, get_ctstring)
}

fn ctstring_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Value> {
hash_item(arguments, location, get_ctstring)
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ pub(crate) fn get_str(
}
}

pub(crate) fn get_ctstring((value, location): (Value, Location)) -> IResult<Rc<String>> {
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),
Expand Down
11 changes: 9 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub enum Value {
U64(u64),
String(Rc<String>),
FormatString(Rc<String>, Type),
CtString(Rc<String>),
Function(FuncId, Type, Rc<TypeBindings>),
Closure(HirLambda, Vec<Value>, Type),
Tuple(Vec<Value>),
Expand Down Expand Up @@ -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),
})
}

Expand Down Expand Up @@ -202,7 +204,9 @@ 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)))
Expand Down Expand Up @@ -349,7 +353,9 @@ 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)))
Expand Down Expand Up @@ -589,6 +595,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)"),
Expand Down
7 changes: 5 additions & 2 deletions compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
};

Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
UnresolvedType,
FunctionDefinition,
Module,
CtString,
}

/// A list of TypeVariableIds to bind to a type. Storing the
Expand Down Expand Up @@ -759,6 +760,7 @@
QuotedType::UnresolvedType => write!(f, "UnresolvedType"),
QuotedType::FunctionDefinition => write!(f, "FunctionDefinition"),
QuotedType::Module => write!(f, "Module"),
QuotedType::CtString => write!(f, "CtString"),
}
}
}
Expand Down Expand Up @@ -1985,7 +1987,7 @@
}

let recur_on_binding = |id, replacement: &Type| {
// Prevent recuring forever if there's a `T := T` binding

Check warning on line 1990 in compiler/noirc_frontend/src/hir_def/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (recuring)
if replacement.type_variable_id() == Some(id) {
replacement.clone()
} else {
Expand Down Expand Up @@ -2056,7 +2058,7 @@
Type::Tuple(fields)
}
Type::Forall(typevars, typ) => {
// Trying to substitute_helper a variable de, substitute_bound_typevarsfined within a nested Forall

Check warning on line 2061 in compiler/noirc_frontend/src/hir_def/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (typevarsfined)
// is usually impossible and indicative of an error in the type checker somewhere.
for var in typevars {
assert!(!type_bindings.contains_key(&var.id()));
Expand Down Expand Up @@ -2223,7 +2225,7 @@

/// Replace any `Type::NamedGeneric` in this type with a `Type::TypeVariable`
/// using to the same inner `TypeVariable`. This is used during monomorphization
/// to bind to named generics since they are unbindable during type checking.

Check warning on line 2228 in compiler/noirc_frontend/src/hir_def/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (unbindable)
pub fn replace_named_generics_with_type_variables(&mut self) {
match self {
Type::FieldElement
Expand Down Expand Up @@ -2602,7 +2604,7 @@
len.hash(state);
env.hash(state);
}
Type::Tuple(elems) => elems.hash(state),

Check warning on line 2607 in compiler/noirc_frontend/src/hir_def/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elems)

Check warning on line 2607 in compiler/noirc_frontend/src/hir_def/types.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (elems)
Type::Struct(def, args) => {
def.hash(state);
args.hash(state);
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,7 @@ pub enum Keyword {
Continue,
Contract,
Crate,
CtString,
Dep,
Else,
Expr,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/parser/parser/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser<NoirFunct
/// function_modifiers: 'unconstrained'? (visibility)?
///
/// returns (is_unconstrained, visibility) for whether each keyword was present
fn function_modifiers() -> 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),
)
Expand Down
25 changes: 19 additions & 6 deletions compiler/noirc_frontend/src/parser/parser/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_modifiers, function_return_type};
use super::path::path_no_turbofish;
use super::{
block, expression, fresh_statement, function, function_declaration_parameters, let_statement,
Expand Down Expand Up @@ -101,16 +101,29 @@ fn trait_function_declaration() -> impl NoirParser<TraitItem> {
}
});

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,
}
},
)
}

/// trait_type_declaration: 'type' ident generics
Expand Down
7 changes: 7 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub(super) fn comptime_type() -> impl NoirParser<UnresolvedType> {
top_level_item_type(),
quoted_type(),
typed_expr_type(),
comptime_string_type(),
))
}

Expand Down Expand Up @@ -166,6 +167,12 @@ fn typed_expr_type() -> impl NoirParser<UnresolvedType> {
.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<UnresolvedType> {
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.
Expand Down
Loading
Loading