Skip to content

Commit

Permalink
feat: turbofish operator on path segments (#5603)
Browse files Browse the repository at this point in the history
# Description

## Problem

Part of #5584

## Summary

This PR adds support for parsing turbofishes in any path segments.
Turbofish is only still checked when elaborating variables, but only for
the last turbofish (like before). For now any other placement of a
turbofish will give a type error, but at least they can be parsed now.
In later PRs we can make them work in the middle of variables, in
constructors, etc.

## Additional Context

Right now turbofishes aren't parsed in named typed (for example in a
struct pattern). The reason is that if I use `path()` instead of
`path_no_turbofish()` then the parser gives a stack overflow and I'm not
sure why. Maybe it's because `parse_type()` is used recursively, I'm not
sure... but these stack overflow errors are kind of hard to diagnose. In
any case we can parse those later on once we decide to support turbofish
in struct patterns.

Also, because a `Path`'s segments changed from `Vec<Ident>` to
`Vec<PathSegment>` some functions, like `Path::last_segment()` made more
sense if they returned `PathSegment` instead of `Ident`. Then I
introduced a bunch of helper functions to reduce some call chains, but
also introduced helpers like `last_name()` which is nice because it
returns a `&str` where in some cases an unnecessary clone was made (so
some refactors/cleanups are included in this PR as part of this
feature).

## Documentation\*

Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [x] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Jul 25, 2024
1 parent 2adc6ac commit 0bb8372
Show file tree
Hide file tree
Showing 28 changed files with 362 additions and 201 deletions.
7 changes: 4 additions & 3 deletions aztec_macros/src/transforms/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use crate::{
ast_utils::{
assignment, assignment_with_type, call, cast, expression, ident, ident_path,
index_array, make_eq, make_statement, make_type, method_call, mutable_assignment,
mutable_reference, path, return_type, variable, variable_ident, variable_path,
mutable_reference, path, path_segment, return_type, variable, variable_ident,
variable_path,
},
errors::AztecMacroError,
},
Expand Down Expand Up @@ -722,8 +723,8 @@ fn add_struct_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement {
fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) {
// let identifier_as_bytes = identifier.as_bytes();
let var = variable_ident(identifier.clone());
let contents = if let ExpressionKind::Variable(p, _) = &var.kind {
p.segments.first().cloned().unwrap_or_else(|| panic!("No segments")).0.contents
let contents = if let ExpressionKind::Variable(p) = &var.kind {
p.first_name()
} else {
panic!("Unexpected identifier type")
};
Expand Down
15 changes: 6 additions & 9 deletions aztec_macros/src/transforms/note_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::{
utils::{
ast_utils::{
check_trait_method_implemented, ident, ident_path, is_custom_attribute, make_type,
path_segment,
},
errors::AztecMacroError,
hir_utils::{fetch_notes, get_contract_module_data, inject_global},
Expand All @@ -45,8 +46,8 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt
.iter_mut()
.find(|trait_impl| {
if let UnresolvedTypeData::Named(struct_path, _, _) = &trait_impl.object_type.typ {
struct_path.last_segment() == note_struct.name
&& trait_impl.trait_name.last_segment().0.contents == "NoteInterface"
struct_path.last_ident() == note_struct.name
&& trait_impl.trait_name.last_name() == "NoteInterface"
} else {
false
}
Expand All @@ -61,7 +62,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt
let note_interface_impl_span: Option<Span> = trait_impl.object_type.span;
// Look for the note struct implementation, generate a default one if it doesn't exist (in order to append methods to it)
let existing_impl = module.impls.iter_mut().find(|r#impl| match &r#impl.object_type.typ {
UnresolvedTypeData::Named(path, _, _) => path.last_segment().eq(&note_struct.name),
UnresolvedTypeData::Named(path, _, _) => path.last_ident().eq(&note_struct.name),
_ => false,
});
let note_impl = if let Some(note_impl) = existing_impl {
Expand All @@ -85,9 +86,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt
.trait_generics
.iter()
.map(|gen| match gen.typ.clone() {
UnresolvedTypeData::Named(path, _, _) => {
Ok(path.last_segment().0.contents.to_string())
}
UnresolvedTypeData::Named(path, _, _) => Ok(path.last_name().to_string()),
UnresolvedTypeData::Expression(UnresolvedTypeExpression::Constant(val, _)) => {
Ok(val.to_string())
}
Expand All @@ -106,9 +105,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt
// Automatically inject the header field if it's not present
let (header_field_name, _) = if let Some(existing_header) =
note_struct.fields.iter().find(|(_, field_type)| match &field_type.typ {
UnresolvedTypeData::Named(path, _, _) => {
path.last_segment().0.contents == "NoteHeader"
}
UnresolvedTypeData::Named(path, _, _) => path.last_name() == "NoteHeader",
_ => false,
}) {
existing_header.clone()
Expand Down
12 changes: 5 additions & 7 deletions aztec_macros/src/transforms/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
utils::{
ast_utils::{
call, expression, ident, ident_path, is_custom_attribute, lambda, make_statement,
make_type, pattern, return_type, variable, variable_path,
make_type, path_segment, pattern, return_type, variable, variable_path,
},
errors::AztecMacroError,
hir_utils::{
Expand Down Expand Up @@ -59,7 +59,7 @@ fn inject_context_in_storage_field(field: &mut UnresolvedType) -> Result<(), Azt
vec![],
false,
)));
match path.segments.last().unwrap().0.contents.as_str() {
match path.last_name() {
"Map" => inject_context_in_storage_field(&mut generics[1]),
_ => Ok(()),
}
Expand Down Expand Up @@ -106,9 +106,7 @@ pub fn check_for_storage_implementation(
storage_struct_name: &String,
) -> bool {
module.impls.iter().any(|r#impl| match &r#impl.object_type.typ {
UnresolvedTypeData::Named(path, _, _) => {
path.segments.last().is_some_and(|segment| segment.0.contents == *storage_struct_name)
}
UnresolvedTypeData::Named(path, _, _) => path.last_name() == *storage_struct_name,
_ => false,
})
}
Expand All @@ -123,8 +121,8 @@ pub fn generate_storage_field_constructor(
match typ {
UnresolvedTypeData::Named(path, generics, _) => {
let mut new_path = path.clone().to_owned();
new_path.segments.push(ident("new"));
match path.segments.last().unwrap().0.contents.as_str() {
new_path.segments.push(path_segment("new"));
match path.last_name() {
"Map" => Ok(call(
variable_path(new_path),
vec![
Expand Down
18 changes: 11 additions & 7 deletions aztec_macros/src/utils/ast_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use noirc_errors::{Span, Spanned};
use noirc_frontend::ast::{
BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType,
Ident, IndexExpression, InfixExpression, Lambda, LetStatement, MemberAccessExpression,
MethodCallExpression, NoirTraitImpl, Path, Pattern, PrefixExpression, Statement, StatementKind,
TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData,
MethodCallExpression, NoirTraitImpl, Path, PathSegment, Pattern, PrefixExpression, Statement,
StatementKind, TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData,
};
use noirc_frontend::token::SecondaryAttribute;

Expand All @@ -18,6 +18,10 @@ pub fn ident_path(name: &str) -> Path {
Path::from_ident(ident(name))
}

pub fn path_segment(name: &str) -> PathSegment {
PathSegment::from(ident(name))
}

pub fn path(ident: Ident) -> Path {
Path::from_ident(ident)
}
Expand All @@ -27,15 +31,15 @@ pub fn expression(kind: ExpressionKind) -> Expression {
}

pub fn variable(name: &str) -> Expression {
expression(ExpressionKind::Variable(ident_path(name), None))
expression(ExpressionKind::Variable(ident_path(name)))
}

pub fn variable_ident(identifier: Ident) -> Expression {
expression(ExpressionKind::Variable(path(identifier), None))
expression(ExpressionKind::Variable(path(identifier)))
}

pub fn variable_path(path: Path) -> Expression {
expression(ExpressionKind::Variable(path, None))
expression(ExpressionKind::Variable(path))
}

pub fn method_call(
Expand Down Expand Up @@ -149,7 +153,7 @@ macro_rules! chained_path {
{
let mut base_path = ident_path($base);
$(
base_path.segments.push(ident($tail));
base_path.segments.push(path_segment($tail));
)*
base_path
}
Expand All @@ -163,7 +167,7 @@ macro_rules! chained_dep {
let mut base_path = ident_path($base);
base_path.kind = PathKind::Plain;
$(
base_path.segments.push(ident($tail));
base_path.segments.push(path_segment($tail));
)*
base_path
}
Expand Down
15 changes: 3 additions & 12 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ pub enum ExpressionKind {
Cast(Box<CastExpression>),
Infix(Box<InfixExpression>),
If(Box<IfExpression>),
// The optional vec here is the optional list of generics
// provided by the turbofish operator, if used
Variable(Path, Option<Vec<UnresolvedType>>),
Variable(Path),
Tuple(Vec<Expression>),
Lambda(Box<Lambda>),
Parenthesized(Box<Expression>),
Expand Down Expand Up @@ -118,7 +116,7 @@ impl From<Ident> for UnresolvedGeneric {
impl ExpressionKind {
pub fn into_path(self) -> Option<Path> {
match self {
ExpressionKind::Variable(path, _) => Some(path),
ExpressionKind::Variable(path) => Some(path),
_ => None,
}
}
Expand Down Expand Up @@ -583,14 +581,7 @@ impl Display for ExpressionKind {
Cast(cast) => cast.fmt(f),
Infix(infix) => infix.fmt(f),
If(if_expr) => if_expr.fmt(f),
Variable(path, generics) => {
if let Some(generics) = generics {
let generics = vecmap(generics, ToString::to_string);
write!(f, "{path}::<{}>", generics.join(", "))
} else {
path.fmt(f)
}
}
Variable(path) => path.fmt(f),
Constructor(constructor) => constructor.fmt(f),
MemberAccess(access) => access.fmt(f),
Tuple(elements) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ impl UnresolvedTypeExpression {
Some(int) => Ok(UnresolvedTypeExpression::Constant(int, expr.span)),
None => Err(expr),
},
ExpressionKind::Variable(path, _) => Ok(UnresolvedTypeExpression::Variable(path)),
ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)),
ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => {
let lhs = Box::new(UnresolvedTypeExpression::Constant(0, expr.span));
let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?);
Expand Down
Loading

0 comments on commit 0bb8372

Please sign in to comment.