diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index 9d3bd0dfa77..3df9939dc70 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -22,6 +22,7 @@ pub struct NoirTrait { pub span: Span, pub items: Vec>, pub attributes: Vec, + pub visibility: ItemVisibility, } /// Any declaration inside the body of a trait that a user is required to diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 39291274269..508765f943c 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -392,8 +392,20 @@ impl<'a> ModCollector<'a> { context.def_interner.set_doc_comments(ReferenceId::Trait(trait_id), doc_comments); // Add the trait to scope so its path can be looked up later - let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_trait(name.clone(), trait_id); + let visibility = trait_definition.visibility; + let result = self.def_collector.def_map.modules[self.module_id.0].declare_trait( + name.clone(), + visibility, + trait_id, + ); + + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.usage_tracker.add_unused_item( + parent_module_id, + name.clone(), + UnusedItem::Trait(trait_id), + visibility, + ); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { diff --git a/compiler/noirc_frontend/src/hir/def_map/module_data.rs b/compiler/noirc_frontend/src/hir/def_map/module_data.rs index 0acb9beeefe..645d8650c7e 100644 --- a/compiler/noirc_frontend/src/hir/def_map/module_data.rs +++ b/compiler/noirc_frontend/src/hir/def_map/module_data.rs @@ -117,8 +117,13 @@ impl ModuleData { self.declare(name, ItemVisibility::Public, id.into(), None) } - pub fn declare_trait(&mut self, name: Ident, id: TraitId) -> Result<(), (Ident, Ident)> { - self.declare(name, ItemVisibility::Public, ModuleDefId::TraitId(id), None) + pub fn declare_trait( + &mut self, + name: Ident, + visibility: ItemVisibility, + id: TraitId, + ) -> Result<(), (Ident, Ident)> { + self.declare(name, visibility, ModuleDefId::TraitId(id), None) } pub fn declare_child_module( diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 51d2d858319..77c6e87a842 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -4,6 +4,7 @@ use super::attributes::{attributes, validate_secondary_attributes}; use super::doc_comments::outer_doc_comments; use super::function::{function_modifiers, function_return_type}; use super::path::path_no_turbofish; +use super::visibility::item_visibility; use super::{ block, expression, fresh_statement, function, function_declaration_parameters, let_statement, }; @@ -42,22 +43,26 @@ pub(super) fn trait_definition() -> impl NoirParser { }); attributes() + .then(item_visibility()) .then_ignore(keyword(Keyword::Trait)) .then(ident()) .then(function::generics()) .then(where_clause()) .then(trait_body_or_error) - .validate(|((((attributes, name), generics), where_clause), items), span, emit| { - let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatementKind::Trait(NoirTrait { - name, - generics, - where_clause, - span, - items, - attributes, - }) - }) + .validate( + |(((((attributes, visibility), name), generics), where_clause), items), span, emit| { + let attributes = validate_secondary_attributes(attributes, span, emit); + TopLevelStatementKind::Trait(NoirTrait { + name, + generics, + where_clause, + span, + items, + attributes, + visibility, + }) + }, + ) } fn trait_body() -> impl NoirParser>> { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 89e93157feb..fb7aaeb847b 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -514,7 +514,7 @@ fn check_trait_wrong_parameter2() { #[test] fn check_trait_wrong_parameter_type() { let src = " - trait Default { + pub trait Default { fn default(x: Field, y: NotAType) -> Field; } @@ -2996,11 +2996,11 @@ fn uses_self_type_inside_trait() { #[test] fn uses_self_type_in_trait_where_clause() { let src = r#" - trait Trait { + pub trait Trait { fn trait_func() -> bool; } - trait Foo where Self: Trait { + pub trait Foo where Self: Trait { fn foo(self) -> bool { self.trait_func() } @@ -3222,7 +3222,7 @@ fn errors_on_unused_private_import() { pub fn bar() {} pub fn baz() {} - trait Foo { + pub trait Foo { } } @@ -3258,7 +3258,7 @@ fn errors_on_unused_pub_crate_import() { pub fn bar() {} pub fn baz() {} - trait Foo { + pub trait Foo { } } @@ -3447,6 +3447,34 @@ fn errors_on_unused_struct() { assert_eq!(*item_type, "struct"); } +#[test] +fn errors_on_unused_trait() { + let src = r#" + trait Foo {} + trait Bar {} + + pub struct Baz { + } + + impl Bar for Baz {} + + fn main() { + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "trait"); +} + #[test] fn constrained_reference_to_unconstrained() { let src = r#" diff --git a/compiler/noirc_frontend/src/usage_tracker.rs b/compiler/noirc_frontend/src/usage_tracker.rs index c8a355c583b..b6f41dc72f2 100644 --- a/compiler/noirc_frontend/src/usage_tracker.rs +++ b/compiler/noirc_frontend/src/usage_tracker.rs @@ -4,7 +4,7 @@ use crate::{ ast::{Ident, ItemVisibility}, hir::def_map::ModuleId, macros_api::StructId, - node_interner::FuncId, + node_interner::{FuncId, TraitId}, }; #[derive(Debug)] @@ -12,6 +12,7 @@ pub enum UnusedItem { Import, Function(FuncId), Struct(StructId), + Trait(TraitId), } impl UnusedItem { @@ -20,6 +21,7 @@ impl UnusedItem { UnusedItem::Import => "import", UnusedItem::Function(_) => "function", UnusedItem::Struct(_) => "struct", + UnusedItem::Trait(_) => "trait", } } } diff --git a/docs/docs/noir/concepts/traits.md b/docs/docs/noir/concepts/traits.md index 597c62c737c..b07d2cf3a08 100644 --- a/docs/docs/noir/concepts/traits.md +++ b/docs/docs/noir/concepts/traits.md @@ -463,3 +463,13 @@ impl Default for Wrapper { Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and unwrapping of values when converting to and from the `Wrapper` and `Foo` types. + +### Visibility + +By default, like functions, traits are private to the module the exist in. You can use `pub` +to make the trait public or `pub(crate)` to make it public to just its crate: + +```rust +// This trait is now public +pub trait Trait {} +``` \ No newline at end of file diff --git a/noir_stdlib/src/append.nr b/noir_stdlib/src/append.nr index 22baa9205a0..5f41dae8046 100644 --- a/noir_stdlib/src/append.nr +++ b/noir_stdlib/src/append.nr @@ -7,7 +7,7 @@ // - `T::empty().append(x) == x` // - `x.append(T::empty()) == x` // docs:start:append-trait -trait Append { +pub trait Append { fn empty() -> Self; fn append(self, other: Self) -> Self; } diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index 2a8a581f104..0015f480794 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -43,7 +43,7 @@ impl BigInt { } } -trait BigField { +pub trait BigField { fn from_le_bytes(bytes: [u8]) -> Self; fn from_le_bytes_32(bytes: [u8; 32]) -> Self; fn to_le_bytes(self) -> [u8]; diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index bc1fc66361d..ac7e3df66ad 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -2,7 +2,7 @@ use crate::meta::derive_via; #[derive_via(derive_eq)] // docs:start:eq-trait -trait Eq { +pub trait Eq { fn eq(self, other: Self) -> bool; } // docs:end:eq-trait @@ -173,7 +173,7 @@ impl Ordering { #[derive_via(derive_ord)] // docs:start:ord-trait -trait Ord { +pub trait Ord { fn cmp(self, other: Self) -> Ordering; } // docs:end:ord-trait diff --git a/noir_stdlib/src/convert.nr b/noir_stdlib/src/convert.nr index 0b2bd4f2eb7..a38a54ce365 100644 --- a/noir_stdlib/src/convert.nr +++ b/noir_stdlib/src/convert.nr @@ -1,5 +1,5 @@ // docs:start:from-trait -trait From { +pub trait From { fn from(input: T) -> Self; } // docs:end:from-trait @@ -11,7 +11,7 @@ impl From for T { } // docs:start:into-trait -trait Into { +pub trait Into { fn into(self) -> T; } diff --git a/noir_stdlib/src/default.nr b/noir_stdlib/src/default.nr index 3dcc04c7d0c..00bb278fd48 100644 --- a/noir_stdlib/src/default.nr +++ b/noir_stdlib/src/default.nr @@ -2,7 +2,7 @@ use crate::meta::derive_via; #[derive_via(derive_default)] // docs:start:default-trait -trait Default { +pub trait Default { fn default() -> Self; } // docs:end:default-trait diff --git a/noir_stdlib/src/hash/mod.nr b/noir_stdlib/src/hash/mod.nr index 08f3e8d7699..9aa7d220593 100644 --- a/noir_stdlib/src/hash/mod.nr +++ b/noir_stdlib/src/hash/mod.nr @@ -140,7 +140,7 @@ pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) // Hash trait shall be implemented per type. #[derive_via(derive_hash)] -trait Hash { +pub trait Hash { fn hash(self, state: &mut H) where H: Hasher; } @@ -155,14 +155,14 @@ comptime fn derive_hash(s: StructDefinition) -> Quoted { // Hasher trait shall be implemented by algorithms to provide hash-agnostic means. // TODO: consider making the types generic here ([u8], [Field], etc.) -trait Hasher{ +pub trait Hasher { fn finish(self) -> Field; fn write(&mut self, input: Field); } // BuildHasher is a factory trait, responsible for production of specific Hasher. -trait BuildHasher where H: Hasher{ +pub trait BuildHasher where H: Hasher { fn build_hasher(self) -> H; } diff --git a/noir_stdlib/src/meta/ctstring.nr b/noir_stdlib/src/meta/ctstring.nr index 85f386ab69e..ec205fa6f88 100644 --- a/noir_stdlib/src/meta/ctstring.nr +++ b/noir_stdlib/src/meta/ctstring.nr @@ -41,7 +41,7 @@ impl Append for CtString { } // docs:start:as-ctstring -trait AsCtString { +pub trait AsCtString { comptime fn as_ctstring(self) -> CtString; } // docs:end:as-ctstring diff --git a/noir_stdlib/src/ops/arith.nr b/noir_stdlib/src/ops/arith.nr index 653dfd59978..195ae777580 100644 --- a/noir_stdlib/src/ops/arith.nr +++ b/noir_stdlib/src/ops/arith.nr @@ -1,5 +1,5 @@ // docs:start:add-trait -trait Add { +pub trait Add { fn add(self, other: Self) -> Self; } // docs:end:add-trait @@ -58,7 +58,7 @@ impl Add for i64 { } // docs:start:sub-trait -trait Sub { +pub trait Sub { fn sub(self, other: Self) -> Self; } // docs:end:sub-trait @@ -117,7 +117,7 @@ impl Sub for i64 { } // docs:start:mul-trait -trait Mul { +pub trait Mul { fn mul(self, other: Self) -> Self; } // docs:end:mul-trait @@ -176,7 +176,7 @@ impl Mul for i64 { } // docs:start:div-trait -trait Div { +pub trait Div { fn div(self, other: Self) -> Self; } // docs:end:div-trait @@ -235,7 +235,7 @@ impl Div for i64 { } // docs:start:rem-trait -trait Rem{ +pub trait Rem { fn rem(self, other: Self) -> Self; } // docs:end:rem-trait @@ -288,7 +288,7 @@ impl Rem for i64 { } // docs:start:neg-trait -trait Neg { +pub trait Neg { fn neg(self) -> Self; } // docs:end:neg-trait diff --git a/noir_stdlib/src/ops/bit.nr b/noir_stdlib/src/ops/bit.nr index 0c0329efe4c..70c52ac38b0 100644 --- a/noir_stdlib/src/ops/bit.nr +++ b/noir_stdlib/src/ops/bit.nr @@ -1,5 +1,5 @@ // docs:start:not-trait -trait Not { +pub trait Not { fn not(self: Self) -> Self; } // docs:end:not-trait @@ -60,7 +60,7 @@ impl Not for i64 { // docs:end:not-trait-impls // docs:start:bitor-trait -trait BitOr { +pub trait BitOr { fn bitor(self, other: Self) -> Self; } // docs:end:bitor-trait @@ -119,7 +119,7 @@ impl BitOr for i64 { } // docs:start:bitand-trait -trait BitAnd { +pub trait BitAnd { fn bitand(self, other: Self) -> Self; } // docs:end:bitand-trait @@ -178,7 +178,7 @@ impl BitAnd for i64 { } // docs:start:bitxor-trait -trait BitXor { +pub trait BitXor { fn bitxor(self, other: Self) -> Self; } // docs:end:bitxor-trait @@ -237,7 +237,7 @@ impl BitXor for i64 { } // docs:start:shl-trait -trait Shl { +pub trait Shl { fn shl(self, other: u8) -> Self; } // docs:end:shl-trait @@ -290,7 +290,7 @@ impl Shl for i64 { } // docs:start:shr-trait -trait Shr { +pub trait Shr { fn shr(self, other: u8) -> Self; } // docs:end:shr-trait