Skip to content
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
28 changes: 21 additions & 7 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use acvm::FieldElement;
use iter_extended::vecmap;
use noirc_errors::{Located, Location, Span};

use super::{AsTraitPath, TypePath, UnsafeExpression};
use super::{AsTraitPath, TraitBound, TypePath, UnsafeExpression};

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ExpressionKind {
Expand Down Expand Up @@ -66,7 +66,7 @@ pub type UnresolvedGenerics = Vec<UnresolvedGeneric>;

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum UnresolvedGeneric {
Variable(Ident),
Variable(Ident, Vec<TraitBound>),
Numeric {
ident: Ident,
typ: UnresolvedType,
Expand All @@ -89,7 +89,7 @@ pub struct UnsupportedNumericGenericType {
impl UnresolvedGeneric {
pub fn location(&self) -> Location {
match self {
UnresolvedGeneric::Variable(ident) => ident.location(),
UnresolvedGeneric::Variable(ident, _) => ident.location(),
UnresolvedGeneric::Numeric { ident, typ } => ident.location().merge(typ.location),
UnresolvedGeneric::Resolved(_, location) => *location,
}
Expand All @@ -101,7 +101,7 @@ impl UnresolvedGeneric {

pub fn kind(&self) -> Result<Kind, UnsupportedNumericGenericType> {
match self {
UnresolvedGeneric::Variable(_) => Ok(Kind::Normal),
UnresolvedGeneric::Variable(_, _) => Ok(Kind::Normal),
UnresolvedGeneric::Numeric { typ, .. } => {
let typ = self.resolve_numeric_kind_type(typ)?;
Ok(Kind::numeric(typ))
Expand Down Expand Up @@ -131,7 +131,9 @@ impl UnresolvedGeneric {

pub(crate) fn ident(&self) -> &Ident {
match self {
UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident,
UnresolvedGeneric::Variable(ident, _) | UnresolvedGeneric::Numeric { ident, .. } => {
ident
}
UnresolvedGeneric::Resolved(..) => panic!("UnresolvedGeneric::Resolved no ident"),
}
}
Expand All @@ -140,7 +142,19 @@ impl UnresolvedGeneric {
impl Display for UnresolvedGeneric {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnresolvedGeneric::Variable(ident) => write!(f, "{ident}"),
UnresolvedGeneric::Variable(ident, trait_bounds) => {
write!(f, "{ident}")?;
if !trait_bounds.is_empty() {
write!(f, ": ")?;
for (index, trait_bound) in trait_bounds.iter().enumerate() {
if index > 0 {
write!(f, " + ")?;
}
write!(f, "{trait_bound}")?;
}
}
Ok(())
}
UnresolvedGeneric::Numeric { ident, typ } => write!(f, "let {ident}: {typ}"),
UnresolvedGeneric::Resolved(..) => write!(f, "(resolved)"),
}
Expand All @@ -149,7 +163,7 @@ impl Display for UnresolvedGeneric {

impl From<Ident> for UnresolvedGeneric {
fn from(value: Ident) -> Self {
UnresolvedGeneric::Variable(value)
UnresolvedGeneric::Variable(value, Vec::new())
}
}

Expand Down
39 changes: 37 additions & 2 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::node_interner::TraitId;
use crate::token::SecondaryAttribute;

use super::{Documented, GenericTypeArgs, ItemVisibility};
use super::{Documented, GenericTypeArgs, ItemVisibility, UnresolvedGeneric, UnresolvedTypeData};

/// AST node for trait definitions:
/// `trait name<generics> { ... items ... }`
Expand Down Expand Up @@ -117,7 +117,8 @@ pub enum TraitImplItemKind {
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(", ") };
let generics =
if generics.is_empty() { "".into() } else { format!("<{}>", generics.join(", ")) };

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

Expand Down Expand Up @@ -279,3 +280,37 @@ impl Display for TraitImplItemKind {
}
}
}

/// Moves trait bounds from generics into where clauses. For example:
///
/// ```noir
/// fn foo<T: Trait>(x: T) -> T {}
/// ```
///
/// becomes:
///
/// ```noir
/// fn foo<T>(x: T) -> T where T: Trait {}
/// ```
pub(crate) fn desugar_generic_trait_bounds(
generics: &mut Vec<UnresolvedGeneric>,
where_clause: &mut Vec<UnresolvedTraitConstraint>,
) {
for generic in generics {
let UnresolvedGeneric::Variable(ident, trait_bounds) = generic else {
continue;
};

if trait_bounds.is_empty() {
continue;
}

for trait_bound in std::mem::take(trait_bounds) {
let path = Path::from_ident(ident.clone());
let typ = UnresolvedTypeData::Named(path, GenericTypeArgs::default(), true);
let typ = UnresolvedType { typ, location: ident.location() };
let trait_constraint = UnresolvedTraitConstraint { typ, trait_bound };
where_clause.push(trait_constraint);
}
}
}
42 changes: 40 additions & 2 deletions compiler/noirc_frontend/src/ast/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
use super::{
ForBounds, FunctionReturnType, GenericTypeArgs, IntegerBitSize, ItemVisibility,
MatchExpression, NoirEnumeration, Pattern, Signedness, TraitBound, TraitImplItemKind, TypePath,
UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData,
UnresolvedTypeExpression, UnsafeExpression,
UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType,
UnresolvedTypeData, UnresolvedTypeExpression, UnsafeExpression,
};

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -458,6 +458,10 @@
true
}

fn visit_unresolved_generic(&mut self, _: &UnresolvedGeneric) -> bool {
true
}

fn visit_function_return_type(&mut self, _: &FunctionReturnType) -> bool {
true
}
Expand Down Expand Up @@ -612,6 +616,8 @@
attribute.accept(AttributeTarget::Function, visitor);
}

visit_unresolved_generics(&self.def.generics, visitor);

for param in &self.def.parameters {
param.typ.accept(visitor);
}
Expand All @@ -634,6 +640,8 @@
}

pub fn accept_children(&self, visitor: &mut impl Visitor) {
visit_unresolved_generics(&self.impl_generics, visitor);

self.r#trait.accept(visitor);
self.object_type.accept(visitor);

Expand Down Expand Up @@ -712,6 +720,8 @@
attribute.accept(AttributeTarget::Trait, visitor);
}

visit_unresolved_generics(&self.generics, visitor);

for bound in &self.bounds {
bound.accept(visitor);
}
Expand Down Expand Up @@ -1455,8 +1465,8 @@
UnresolvedTypeData::FieldElement => {
visitor.visit_field_element_type(self.location.span);
}
UnresolvedTypeData::Integer(signdness, size) => {

Check warning on line 1468 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.location.span);

Check warning on line 1469 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.location.span),
UnresolvedTypeData::Unit => visitor.visit_unit_type(self.location.span),
Expand Down Expand Up @@ -1604,6 +1614,28 @@
}
}

impl UnresolvedGeneric {
pub fn accept(&self, visitor: &mut impl Visitor) {
if visitor.visit_unresolved_generic(self) {
self.accept_children(visitor);
}
}

pub fn accept_children(&self, visitor: &mut impl Visitor) {
match self {
UnresolvedGeneric::Variable(_ident, trait_bounds) => {
for trait_bound in trait_bounds {
trait_bound.accept(visitor);
}
}
UnresolvedGeneric::Numeric { ident: _, typ } => {
typ.accept(visitor);
}
UnresolvedGeneric::Resolved(_quoted_type_id, _location) => (),
}
}
}

impl SecondaryAttribute {
pub fn accept(&self, target: AttributeTarget, visitor: &mut impl Visitor) {
if visitor.visit_secondary_attribute(self, target) {
Expand Down Expand Up @@ -1642,3 +1674,9 @@
unresolved_type.accept(visitor);
}
}

fn visit_unresolved_generics(generics: &[UnresolvedGeneric], visitor: &mut impl Visitor) {
for generic in generics {
generic.accept(visitor);
}
}
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ impl<'context> Elaborator<'context> {
) -> Result<(TypeVariable, Rc<String>), ResolverError> {
// Map the generic to a fresh type variable
match generic {
UnresolvedGeneric::Variable(_) | UnresolvedGeneric::Numeric { .. } => {
UnresolvedGeneric::Variable(..) | UnresolvedGeneric::Numeric { .. } => {
let id = self.interner.next_type_variable_id();
let kind = self.resolve_generic_kind(generic);
let typevar = TypeVariable::unbound(id, kind);
Expand Down Expand Up @@ -2220,7 +2220,7 @@ impl<'context> Elaborator<'context> {
let mut idents = HashSet::new();
for generic in generics {
match generic {
UnresolvedGeneric::Variable(ident) => {
UnresolvedGeneric::Variable(ident, _) => {
idents.insert(ident.clone());
}
UnresolvedGeneric::Numeric { ident, typ: _ } => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl Elaborator<'_> {
let name_location = the_trait.name.location();

this.add_existing_generic(
&UnresolvedGeneric::Variable(Ident::from("Self")),
&UnresolvedGeneric::Variable(Ident::from("Self"), Vec::new()),
name_location,
&ResolvedGeneric {
name: Rc::new("Self".to_owned()),
Expand Down
32 changes: 28 additions & 4 deletions compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Documented, Expression, FunctionDefinition, Ident, ItemVisibility, LetStatement,
ModuleDeclaration, NoirEnumeration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl,
NoirTypeAlias, Pattern, TraitImplItemKind, TraitItem, TypeImpl, UnresolvedType,
UnresolvedTypeData,
UnresolvedTypeData, desugar_generic_trait_bounds,
};
use crate::hir::resolution::errors::ResolverError;
use crate::node_interner::{ModuleAttributes, NodeInterner, ReferenceId, TypeId};
Expand Down Expand Up @@ -175,7 +175,9 @@
let mut errors = Vec::new();
let module_id = ModuleId { krate, local_id: self.module_id };

for r#impl in impls {
for mut r#impl in impls {
desugar_generic_trait_bounds(&mut r#impl.generics, &mut r#impl.where_clause);

collect_impl(
&mut context.def_interner,
&mut self.def_collector.items,
Expand All @@ -198,6 +200,11 @@
let mut errors = Vec::new();

for mut trait_impl in impls {
desugar_generic_trait_bounds(
&mut trait_impl.impl_generics,
&mut trait_impl.where_clause,
);

let (mut unresolved_functions, associated_types, associated_constants) =
collect_trait_impl_items(
&mut context.def_interner,
Expand Down Expand Up @@ -288,7 +295,13 @@
// and replace it
// With this method we iterate each function in the Crate and not each module
// This may not be great because we have to pull the module_data for each function
unresolved_functions.push_fn(self.module_id, func_id, function.item);
let mut noir_function = function.item;
desugar_generic_trait_bounds(
&mut noir_function.def.generics,
&mut noir_function.def.where_clause,
);

unresolved_functions.push_fn(self.module_id, func_id, noir_function);
}

self.def_collector.items.functions.push(unresolved_functions);
Expand Down Expand Up @@ -434,7 +447,7 @@
let mut errors: Vec<CompilationError> = vec![];
for trait_definition in traits {
let doc_comments = trait_definition.doc_comments;
let trait_definition = trait_definition.item;
let mut trait_definition = trait_definition.item;
let name = trait_definition.name.clone();
let location = trait_definition.location;

Expand Down Expand Up @@ -495,6 +508,12 @@
let mut method_ids = HashMap::default();
let mut associated_types = Generics::new();

for item in &mut trait_definition.items {
if let TraitItem::Function { generics, where_clause, .. } = &mut item.item {
desugar_generic_trait_bounds(generics, where_clause);
}
}

for trait_item in &trait_definition.items {
match &trait_item.item {
TraitItem::Function {
Expand Down Expand Up @@ -903,7 +922,7 @@
// if it's an inline module, or the first char of a the file if it's an external module.
// - `location` will always point to the token "foo" in `mod foo` regardless of whether
// it's inline or external.
// Eventually the location put in `ModuleData` is used for codelenses about `contract`s,

Check warning on line 925 in compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (codelenses)
// so we keep using `location` so that it continues to work as usual.
let location = Location::new(mod_name.span(), mod_location.file);
let new_module = ModuleData::new(
Expand Down Expand Up @@ -1237,6 +1256,7 @@

let func_id = interner.push_empty_fn();
method.def.where_clause.extend(r#impl.where_clause.clone());
desugar_generic_trait_bounds(&mut method.def.generics, &mut method.def.where_clause);
let location = Location::new(method.span(), file_id);
interner.push_function(func_id, &method.def, module_id, location);
unresolved_functions.push_fn(module_id.local_id, func_id, method);
Expand Down Expand Up @@ -1361,6 +1381,10 @@
let location = Location::new(impl_method.span(), file_id);
interner.push_function(func_id, &impl_method.def, module, location);
interner.set_doc_comments(ReferenceId::Function(func_id), item.doc_comments);
desugar_generic_trait_bounds(
&mut impl_method.def.generics,
&mut impl_method.def.where_clause,
);
unresolved_functions.push_fn(local_id, func_id, impl_method);
}
TraitImplItemKind::Constant(name, typ, expr) => {
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 @@ -125,6 +125,8 @@ pub enum ParserErrorReason {
ExpectedValueFoundBuiltInType { typ: UnresolvedType },
#[error("Logical and used instead of bitwise and")]
LogicalAnd,
#[error("Trait bounds are not allowed here")]
TraitBoundsNotAllowedHere,
}

/// Represents a parsing error, or a parsing error in the making.
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_frontend/src/parser/parser/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl Parser<'_> {
);
};

let generics = self.parse_generics();
let generics = self.parse_generics_disallowing_trait_bounds();

if !self.eat_left_brace() {
self.expected_token(Token::LeftBrace);
Expand Down Expand Up @@ -158,10 +158,11 @@ mod tests {
assert_eq!(noir_enum.generics.len(), 2);

let generic = noir_enum.generics.remove(0);
let UnresolvedGeneric::Variable(ident) = generic else {
let UnresolvedGeneric::Variable(ident, trait_bounds) = generic else {
panic!("Expected generic variable");
};
assert_eq!("A", ident.to_string());
assert!(trait_bounds.is_empty());

let generic = noir_enum.generics.remove(0);
let UnresolvedGeneric::Numeric { ident, typ } = generic else {
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 @@ -92,7 +92,7 @@ impl Parser<'_> {
return empty_function(self.previous_token_location);
};

let generics = self.parse_generics();
let generics = self.parse_generics_allowing_trait_bounds();
let parameters = self.parse_function_parameters(allow_self);

let parameters = match parameters {
Expand Down
Loading
Loading