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: Implement parsing of traits #1886

Merged
merged 5 commits into from
Jul 12, 2023
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
3 changes: 2 additions & 1 deletion crates/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fmt::Display;

use crate::token::{Attribute, Token};
use crate::{Ident, Path, Pattern, Recoverable, Statement, UnresolvedType};
use crate::{Ident, Path, Pattern, Recoverable, Statement, TraitConstraint, UnresolvedType};
use acvm::FieldElement;
use iter_extended::vecmap;
use noirc_errors::{Span, Spanned};
Expand Down Expand Up @@ -337,6 +337,7 @@ pub struct FunctionDefinition {
pub parameters: Vec<(Pattern, UnresolvedType, noirc_abi::AbiVisibility)>,
pub body: BlockExpression,
pub span: Span,
pub where_clause: Vec<TraitConstraint>,
pub return_type: UnresolvedType,
pub return_visibility: noirc_abi::AbiVisibility,
pub return_distinctness: noirc_abi::AbiDistinctness,
Expand Down
2 changes: 2 additions & 0 deletions crates/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ mod expression;
mod function;
mod statement;
mod structure;
mod traits;

pub use expression::*;
pub use function::*;

use noirc_errors::Span;
pub use statement::*;
pub use structure::*;
pub use traits::*;

use crate::{
parser::{ParserError, ParserErrorReason},
Expand Down
8 changes: 8 additions & 0 deletions crates/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,14 @@ impl Pattern {
_ => panic!("only the identifier pattern can return a name"),
}
}

pub(crate) fn into_ident(self) -> Ident {
match self {
Pattern::Identifier(ident) => ident,
Pattern::Mutable(pattern, _) => pattern.into_ident(),
other => panic!("Pattern::into_ident called on {other} pattern with no identifier"),
}
}
}

impl Recoverable for Pattern {
Expand Down
29 changes: 1 addition & 28 deletions crates/noirc_frontend/src/ast/structure.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Display;

use crate::{Ident, NoirFunction, UnresolvedGenerics, UnresolvedType};
use crate::{Ident, UnresolvedGenerics, UnresolvedType};
use iter_extended::vecmap;
use noirc_errors::Span;

Expand All @@ -24,15 +24,6 @@ impl NoirStruct {
}
}

/// Ast node for an impl
#[derive(Clone, Debug)]
pub struct NoirImpl {
pub object_type: UnresolvedType,
pub type_span: Span,
pub generics: UnresolvedGenerics,
pub methods: Vec<NoirFunction>,
}

impl Display for NoirStruct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.generics, |generic| generic.to_string());
Expand All @@ -47,21 +38,3 @@ impl Display for NoirStruct {
write!(f, "}}")
}
}

impl Display for NoirImpl {
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(", ") };

writeln!(f, "impl{} {} {{", generics, self.object_type)?;

for method in self.methods.iter() {
let method = method.to_string();
for line in method.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}
165 changes: 165 additions & 0 deletions crates/noirc_frontend/src/ast/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use std::fmt::Display;

use iter_extended::vecmap;
use noirc_errors::Span;

use crate::{Ident, NoirFunction, UnresolvedGenerics, UnresolvedType};

/// AST node for trait definitions:
/// `trait name<generics> { ... items ... }`
#[derive(Clone, Debug)]
pub struct NoirTrait {
pub name: Ident,
pub generics: Vec<Ident>,
pub items: Vec<TraitItem>,
}

/// Any declaration inside the body of a trait that a user is required to
/// specify when implementing the trait.
#[derive(Clone, Debug)]
pub enum TraitItem {
Function {
name: Ident,
generics: Vec<Ident>,
parameters: Vec<(Ident, UnresolvedType)>,
return_type: UnresolvedType,
where_clause: Vec<TraitConstraint>,
},
Type {
name: Ident,
},
}

/// Ast node for an impl of a concrete type
/// `impl object_type<generics> { ... methods ... }`
#[derive(Clone, Debug)]
pub struct TypeImpl {
pub object_type: UnresolvedType,
pub type_span: Span,
pub generics: UnresolvedGenerics,
pub methods: Vec<NoirFunction>,
}

/// Ast node for an implementation of a trait for a particular type
/// `impl trait_name<trait_generics> for object_type where where_clauses { ... items ... }`
#[derive(Clone, Debug)]
pub struct TraitImpl {
pub impl_generics: UnresolvedGenerics,

pub trait_name: Ident,
pub trait_generics: Vec<UnresolvedType>,

pub object_type: UnresolvedType,
pub object_type_span: Span,

pub where_clause: Vec<TraitConstraint>,

pub items: Vec<TraitImplItem>,
}

/// Represents a trait constraint such as `where Foo: Display`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TraitConstraint {
pub typ: UnresolvedType,
pub trait_name: Ident,
pub trait_generics: Vec<UnresolvedType>,
}

#[derive(Clone, Debug)]
pub enum TraitImplItem {
Function(NoirFunction),
Type { name: Ident, alias: UnresolvedType },
}

impl Display for TypeImpl {
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(", ") };

writeln!(f, "impl{} {} {{", generics, self.object_type)?;

for method in self.methods.iter() {
let method = method.to_string();
for line in method.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}

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(", ") };

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

for item in self.items.iter() {
let item = item.to_string();
for line in item.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}

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 } => {
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);

let generics = generics.join(", ");
let parameters = parameters.join(", ");
let where_clause = where_clause.join(", ");

write!(
f,
"fn {name}<{}>({}) -> {} where {};",
generics, parameters, return_type, where_clause
)
}
TraitItem::Type { name } => write!(f, "type {name};"),
}
}
}

impl Display for TraitConstraint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.trait_generics, |generic| generic.to_string());
write!(f, "{}: {}<{}>", self.typ, self.trait_name, generics.join(", "))
}
}

impl Display for TraitImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let generics = vecmap(&self.trait_generics, |generic| generic.to_string());
let generics = generics.join(", ");

writeln!(f, "impl {}<{}> for {} {{", self.trait_name, generics, self.object_type)?;

for item in self.items.iter() {
let item = item.to_string();
for line in item.lines() {
writeln!(f, " {line}")?;
}
}

write!(f, "}}")
}
}

impl Display for TraitImplItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TraitImplItem::Function(function) => function.fmt(f),
TraitImplItem::Type { name, alias } => write!(f, "type {name} = {alias}"),
}
}
}
4 changes: 2 additions & 2 deletions crates/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use noirc_errors::FileDiagnostic;

use crate::{
graph::CrateId, hir::def_collector::dc_crate::UnresolvedStruct, node_interner::StructId,
parser::SubModule, Ident, LetStatement, NoirFunction, NoirImpl, NoirStruct, ParsedModule,
parser::SubModule, Ident, LetStatement, NoirFunction, NoirStruct, ParsedModule, TypeImpl,
};

use super::{
Expand Down Expand Up @@ -92,7 +92,7 @@ impl<'a> ModCollector<'a> {
}
}

fn collect_impls(&mut self, context: &mut Context, impls: Vec<NoirImpl>) {
fn collect_impls(&mut self, context: &mut Context, impls: Vec<TypeImpl>) {
for r#impl in impls {
let mut unresolved_functions =
UnresolvedFunctions { file_id: self.file_id, functions: Vec::new() };
Expand Down
21 changes: 15 additions & 6 deletions crates/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,20 +428,23 @@ pub enum Keyword {
Fn,
For,
Global,
Impl,
If,
Impl,
In,
Internal,
Let,
Mod,
Mut,
Open,
Pub,
String,
Return,
String,
Struct,
Trait,
Type,
Unconstrained,
Use,
Where,
While,
}

Expand All @@ -463,20 +466,23 @@ impl fmt::Display for Keyword {
Keyword::Fn => write!(f, "fn"),
Keyword::For => write!(f, "for"),
Keyword::Global => write!(f, "global"),
Keyword::Impl => write!(f, "impl"),
Keyword::If => write!(f, "if"),
Keyword::Impl => write!(f, "impl"),
Keyword::In => write!(f, "in"),
Keyword::Internal => write!(f, "internal"),
Keyword::Let => write!(f, "let"),
Keyword::Mod => write!(f, "mod"),
Keyword::Mut => write!(f, "mut"),
Keyword::Open => write!(f, "open"),
Keyword::Pub => write!(f, "pub"),
Keyword::String => write!(f, "str"),
Keyword::Return => write!(f, "return"),
Keyword::String => write!(f, "str"),
Keyword::Struct => write!(f, "struct"),
Keyword::Trait => write!(f, "trait"),
Keyword::Type => write!(f, "type"),
Keyword::Unconstrained => write!(f, "unconstrained"),
Keyword::Use => write!(f, "use"),
Keyword::Where => write!(f, "where"),
Keyword::While => write!(f, "while"),
}
}
Expand All @@ -501,20 +507,23 @@ impl Keyword {
"fn" => Keyword::Fn,
"for" => Keyword::For,
"global" => Keyword::Global,
"impl" => Keyword::Impl,
"if" => Keyword::If,
"impl" => Keyword::Impl,
"in" => Keyword::In,
"internal" => Keyword::Internal,
"let" => Keyword::Let,
"mod" => Keyword::Mod,
"mut" => Keyword::Mut,
"open" => Keyword::Open,
"pub" => Keyword::Pub,
"str" => Keyword::String,
"return" => Keyword::Return,
"str" => Keyword::String,
"struct" => Keyword::Struct,
"trait" => Keyword::Trait,
"type" => Keyword::Type,
"unconstrained" => Keyword::Unconstrained,
"use" => Keyword::Use,
"where" => Keyword::Where,
"while" => Keyword::While,

"true" => return Some(Token::Bool(true)),
Expand Down
4 changes: 4 additions & 0 deletions crates/noirc_frontend/src/parser/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub enum ParserErrorReason {
InvalidArrayLengthExpression(Expression),
#[error("Early 'return' is unsupported")]
EarlyReturn,
#[error("Patterns aren't allowed in a trait's function declarations")]
PatternInTraitFunctionParameter,
#[error("Traits are an experimental feature that are not yet in a working state")]
TraitsAreExperimental,
}

/// Represents a parsing error, or a parsing error in the making.
Expand Down
Loading