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

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
72 changes: 41 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,15 +88,19 @@ 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() })
}

/// This method returns one 'ItemKind' in the majority of cases.
/// The current exception is when parsing a trait alias,
/// which returns both the trait and the impl.
///
/// ItemKind
/// = InnerAttribute
/// | Attributes Modifiers
Expand All @@ -106,9 +113,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 +129,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.
fn parse_many_to_many_return_trailing_separator_if_any<T, F>(
&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