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: trait aliases #6431

Merged
merged 17 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6886b5a
wip adding parsing support for trait aliases, adding tests, debugging…
michaeljklein Nov 1, 2024
a271d8b
finished debugging parsing tests, added method stub to generate the i…
michaeljklein Nov 1, 2024
54845ae
debugging failing tests, cleanup, modify parse_many to accept a many-…
michaeljklein Nov 5, 2024
1025b47
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 6, 2024
224a822
cargo fmt
michaeljklein Nov 6, 2024
ced2f68
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 6, 2024
e32f51a
test 'pub' for trait aliases, add tests with where clause, reference …
michaeljklein Nov 6, 2024
d034c86
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 6, 2024
10ac395
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 7, 2024
e1836eb
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 13, 2024
3d72a82
added formatter test and support for trait aliases in formatter, add …
michaeljklein Nov 13, 2024
a6f9899
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 13, 2024
8b2f196
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 15, 2024
0ab07d8
Update compiler/noirc_frontend/src/parser/parser/parse_many.rs
michaeljklein Nov 15, 2024
63718b8
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 20, 2024
47db54f
add note r.e. parse_item_kind returning a Vec
michaeljklein Nov 20, 2024
b039e31
Merge branch 'master' into michaeljklein/trait-aliases
michaeljklein Nov 20, 2024
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
16 changes: 16 additions & 0 deletions compiler/noirc_frontend/src/ast/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct NoirTrait {
pub items: Vec<Documented<TraitItem>>,
pub attributes: Vec<SecondaryAttribute>,
pub visibility: ItemVisibility,
pub is_alias: bool,
}

/// Any declaration inside the body of a trait that a user is required to
Expand Down Expand Up @@ -77,6 +78,9 @@ pub struct NoirTraitImpl {
pub where_clause: Vec<UnresolvedTraitConstraint>,

pub items: Vec<Documented<TraitImplItem>>,

/// true if generated at compile-time, e.g. from a trait alias
pub is_synthetic: bool,
}

/// Represents a simple trait constraint such as `where Foo: TraitY<U, V>`
Expand Down Expand Up @@ -130,12 +134,19 @@ impl Display for TypeImpl {
}
}

// TODO: display where clauses (follow-up issue)
impl Display for NoirTrait {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.generics, |generic| generic.to_string());
let generics = if generics.is_empty() { "".into() } else { generics.join(", ") };

write!(f, "trait {}{}", self.name, generics)?;

if self.is_alias {
let bounds = vecmap(&self.bounds, |bound| bound.to_string()).join(" + ");
return write!(f, " = {};", bounds);
}

if !self.bounds.is_empty() {
let bounds = vecmap(&self.bounds, |bound| bound.to_string()).join(" + ");
write!(f, ": {}", bounds)?;
Expand Down Expand Up @@ -222,6 +233,11 @@ impl Display for TraitBound {

impl Display for NoirTraitImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Synthetic NoirTraitImpl's don't get printed
if self.is_synthetic {
return Ok(());
}

write!(f, "impl")?;
if !self.impl_generics.is_empty() {
write!(
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ pub enum TypeCheckError {

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NoMatchingImplFoundError {
constraints: Vec<(Type, String)>,
pub(crate) constraints: Vec<(Type, String)>,
pub span: Span,
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/parser/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub enum ParserErrorReason {
AssociatedTypesNotAllowedInPaths,
#[error("Associated types are not allowed on a method call")]
AssociatedTypesNotAllowedInMethodCalls,
#[error("Empty trait alias")]
EmptyTraitAlias,
#[error(
"Wrong number of arguments for attribute `{}`. Expected {}, found {}",
name,
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl<'a> Parser<'a> {
let object_type = self.parse_type_or_error();
let where_clause = self.parse_where_clause();
let items = self.parse_trait_impl_body();
let is_synthetic = false;

NoirTraitImpl {
impl_generics,
Expand All @@ -121,6 +122,7 @@ impl<'a> Parser<'a> {
object_type,
where_clause,
items,
is_synthetic,
}
}

Expand Down
68 changes: 37 additions & 31 deletions compiler/noirc_frontend/src/parser/parser/item.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use iter_extended::vecmap;

use crate::{
parser::{labels::ParsingRuleLabel, Item, ItemKind},
token::{Keyword, Token},
Expand All @@ -13,31 +15,32 @@ impl<'a> Parser<'a> {
}

pub(crate) fn parse_module_items(&mut self, nested: bool) -> Vec<Item> {
self.parse_many("items", without_separator(), |parser| {
self.parse_many_to_many("items", without_separator(), |parser| {
parser.parse_module_item_in_list(nested)
})
}

fn parse_module_item_in_list(&mut self, nested: bool) -> Option<Item> {
fn parse_module_item_in_list(&mut self, nested: bool) -> Vec<Item> {
loop {
// We only break out of the loop on `}` if we are inside a `mod { ..`
if nested && self.at(Token::RightBrace) {
return None;
return vec![];
}

// We always break on EOF (we don't error because if we are inside `mod { ..`
// the outer parsing logic will error instead)
if self.at_eof() {
return None;
return vec![];
}

let Some(item) = self.parse_item() else {
let parsed_items = self.parse_item();
if parsed_items.is_empty() {
// If we couldn't parse an item we check which token we got
match self.token.token() {
Token::RightBrace if nested => {
return None;
return vec![];
}
Token::EOF => return None,
Token::EOF => return vec![],
_ => (),
}

Expand All @@ -47,7 +50,7 @@ impl<'a> Parser<'a> {
continue;
};

return Some(item);
return parsed_items;
}
}

Expand Down Expand Up @@ -85,13 +88,13 @@ impl<'a> Parser<'a> {
}

/// Item = OuterDocComments ItemKind
fn parse_item(&mut self) -> Option<Item> {
fn parse_item(&mut self) -> Vec<Item> {
let start_span = self.current_token_span;
let doc_comments = self.parse_outer_doc_comments();
let kind = self.parse_item_kind()?;
let kinds = self.parse_item_kind();
let span = self.span_since(start_span);

Some(Item { kind, span, doc_comments })
vecmap(kinds, |kind| Item { kind, span, doc_comments: doc_comments.clone() })
}

/// ItemKind
Expand All @@ -106,9 +109,9 @@ impl<'a> Parser<'a> {
/// | TypeAlias
/// | Function
/// )
fn parse_item_kind(&mut self) -> Option<ItemKind> {
fn parse_item_kind(&mut self) -> Vec<ItemKind> {
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
if let Some(kind) = self.parse_inner_attribute() {
return Some(ItemKind::InnerAttribute(kind));
return vec![ItemKind::InnerAttribute(kind)];
}

let start_span = self.current_token_span;
Expand All @@ -122,78 +125,81 @@ impl<'a> Parser<'a> {
self.comptime_mutable_and_unconstrained_not_applicable(modifiers);

let use_tree = self.parse_use_tree();
return Some(ItemKind::Import(use_tree, modifiers.visibility));
return vec![ItemKind::Import(use_tree, modifiers.visibility)];
}

if let Some(is_contract) = self.eat_mod_or_contract() {
self.comptime_mutable_and_unconstrained_not_applicable(modifiers);

return Some(self.parse_mod_or_contract(attributes, is_contract, modifiers.visibility));
return vec![self.parse_mod_or_contract(attributes, is_contract, modifiers.visibility)];
}

if self.eat_keyword(Keyword::Struct) {
self.comptime_mutable_and_unconstrained_not_applicable(modifiers);

return Some(ItemKind::Struct(self.parse_struct(
return vec![ItemKind::Struct(self.parse_struct(
attributes,
modifiers.visibility,
start_span,
)));
))];
}

if self.eat_keyword(Keyword::Impl) {
self.comptime_mutable_and_unconstrained_not_applicable(modifiers);

return Some(match self.parse_impl() {
return vec![match self.parse_impl() {
Impl::Impl(type_impl) => ItemKind::Impl(type_impl),
Impl::TraitImpl(noir_trait_impl) => ItemKind::TraitImpl(noir_trait_impl),
});
}];
}

if self.eat_keyword(Keyword::Trait) {
self.comptime_mutable_and_unconstrained_not_applicable(modifiers);

return Some(ItemKind::Trait(self.parse_trait(
attributes,
modifiers.visibility,
start_span,
)));
let (noir_trait, noir_impl) =
self.parse_trait(attributes, modifiers.visibility, start_span);
let mut output = vec![ItemKind::Trait(noir_trait)];
if let Some(noir_impl) = noir_impl {
output.push(ItemKind::TraitImpl(noir_impl));
}

return output;
}

if self.eat_keyword(Keyword::Global) {
self.unconstrained_not_applicable(modifiers);

return Some(ItemKind::Global(
return vec![ItemKind::Global(
self.parse_global(
attributes,
modifiers.comptime.is_some(),
modifiers.mutable.is_some(),
),
modifiers.visibility,
));
)];
}

if self.eat_keyword(Keyword::Type) {
self.comptime_mutable_and_unconstrained_not_applicable(modifiers);

return Some(ItemKind::TypeAlias(
return vec![ItemKind::TypeAlias(
self.parse_type_alias(modifiers.visibility, start_span),
));
)];
}

if self.eat_keyword(Keyword::Fn) {
self.mutable_not_applicable(modifiers);

return Some(ItemKind::Function(self.parse_function(
return vec![ItemKind::Function(self.parse_function(
attributes,
modifiers.visibility,
modifiers.comptime.is_some(),
modifiers.unconstrained.is_some(),
false, // allow_self
)));
))];
}

None
vec![]
}

fn eat_mod_or_contract(&mut self) -> Option<bool> {
Expand Down
40 changes: 37 additions & 3 deletions compiler/noirc_frontend/src/parser/parser/parse_many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ impl<'a> Parser<'a> {
self.parse_many_return_trailing_separator_if_any(items, separated_by, f).0
}

/// parse_many, where the given function `f` may return multiple results
pub(super) fn parse_many_to_many<T, F>(
&mut self,
items: &'static str,
separated_by: SeparatedBy,
f: F,
) -> Vec<T>
where
F: FnMut(&mut Parser<'a>) -> Vec<T>,
{
self.parse_many_to_many_return_trailing_separator_if_any(items, separated_by, f).0
}

/// Same as parse_many, but returns a bool indicating whether a trailing separator was found.
pub(super) fn parse_many_return_trailing_separator_if_any<T, F>(
&mut self,
Expand All @@ -27,6 +40,26 @@ impl<'a> Parser<'a> {
) -> (Vec<T>, bool)
where
F: FnMut(&mut Parser<'a>) -> Option<T>,
{
let f = |x: &mut Parser<'a>| {
if let Some(result) = f(x) {
vec![result]
} else {
vec![]
}
};
self.parse_many_to_many_return_trailing_separator_if_any(items, separated_by, f)
}

/// Same as parse_many, but returns a bool indicating whether a trailing separator was found.
pub(super) fn parse_many_to_many_return_trailing_separator_if_any<T, F>(
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
items: &'static str,
separated_by: SeparatedBy,
mut f: F,
) -> (Vec<T>, bool)
where
F: FnMut(&mut Parser<'a>) -> Vec<T>,
{
let mut elements: Vec<T> = Vec::new();
let mut trailing_separator = false;
Expand All @@ -38,20 +71,21 @@ impl<'a> Parser<'a> {
}

let start_span = self.current_token_span;
let Some(element) = f(self) else {
let mut new_elements = f(self);
if new_elements.is_empty() {
if let Some(end) = &separated_by.until {
self.eat(end.clone());
}
break;
};
}

if let Some(separator) = &separated_by.token {
if !trailing_separator && !elements.is_empty() {
self.expected_token_separating_items(separator.clone(), items, start_span);
}
}

elements.push(element);
elements.append(&mut new_elements);

trailing_separator = if let Some(separator) = &separated_by.token {
self.eat(separator.clone())
Expand Down
Loading
Loading