Skip to content

Commit 04752f7

Browse files
authored
Merge 2e29b92 into e8bbce7
2 parents e8bbce7 + 2e29b92 commit 04752f7

11 files changed

Lines changed: 230 additions & 18 deletions

File tree

compiler/noirc_frontend/src/ast/type_alias.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{Ident, UnresolvedGenerics, UnresolvedType};
1+
use super::{Ident, ItemVisibility, UnresolvedGenerics, UnresolvedType};
22
use iter_extended::vecmap;
33
use noirc_errors::Span;
44
use std::fmt::Display;
@@ -9,6 +9,7 @@ pub struct NoirTypeAlias {
99
pub name: Ident,
1010
pub generics: UnresolvedGenerics,
1111
pub typ: UnresolvedType,
12+
pub visibility: ItemVisibility,
1213
pub span: Span,
1314
}
1415

@@ -17,9 +18,10 @@ impl NoirTypeAlias {
1718
name: Ident,
1819
generics: UnresolvedGenerics,
1920
typ: UnresolvedType,
21+
visibility: ItemVisibility,
2022
span: Span,
2123
) -> NoirTypeAlias {
22-
NoirTypeAlias { name, generics, typ, span }
24+
NoirTypeAlias { name, generics, typ, visibility, span }
2325
}
2426
}
2527

compiler/noirc_frontend/src/elaborator/mod.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,13 +1131,106 @@ impl<'context> Elaborator<'context> {
11311131
self.file = alias.file_id;
11321132
self.local_module = alias.module_id;
11331133

1134+
let name = &alias.type_alias_def.name;
1135+
let visibility = alias.type_alias_def.visibility;
1136+
let span = alias.type_alias_def.typ.span;
1137+
11341138
let generics = self.add_generics(&alias.type_alias_def.generics);
11351139
self.current_item = Some(DependencyId::Alias(alias_id));
11361140
let typ = self.resolve_type(alias.type_alias_def.typ);
1141+
1142+
if visibility != ItemVisibility::Private {
1143+
self.check_aliased_type_is_not_more_private(name, visibility, &typ, span);
1144+
}
1145+
11371146
self.interner.set_type_alias(alias_id, typ, generics);
11381147
self.generics.clear();
11391148
}
11401149

1150+
fn check_aliased_type_is_not_more_private(
1151+
&mut self,
1152+
name: &Ident,
1153+
visibility: ItemVisibility,
1154+
typ: &Type,
1155+
span: Span,
1156+
) {
1157+
match typ {
1158+
Type::Struct(struct_type, generics) => {
1159+
let struct_type = struct_type.borrow();
1160+
let struct_module_id = struct_type.id.module_id();
1161+
1162+
// We only check this in types in the same crate. If it's in a different crate
1163+
// then it's either accessible (all good) or it's not, in which case a different
1164+
// error will happen somewhere else, but no need to error again here.
1165+
if struct_module_id.krate == self.crate_id {
1166+
// Go to the struct's parent module
1167+
let module_data = self.get_module(struct_module_id);
1168+
let parent_local_id =
1169+
module_data.parent.expect("Expected struct module parent to exist");
1170+
let parent_module_id =
1171+
ModuleId { krate: struct_module_id.krate, local_id: parent_local_id };
1172+
let parent_module_data = self.get_module(parent_module_id);
1173+
1174+
// Find the struct in the parent module so we can know its visibility
1175+
let per_ns = parent_module_data.find_name(&struct_type.name);
1176+
if let Some((_, aliased_visibility, _)) = per_ns.types {
1177+
if aliased_visibility < visibility {
1178+
self.push_err(ResolverError::TypeIsMorePrivateThenItem {
1179+
typ: struct_type.name.to_string(),
1180+
item: name.to_string(),
1181+
span,
1182+
});
1183+
}
1184+
}
1185+
}
1186+
1187+
for generic in generics {
1188+
self.check_aliased_type_is_not_more_private(name, visibility, generic, span);
1189+
}
1190+
}
1191+
Type::Tuple(types) => {
1192+
for typ in types {
1193+
self.check_aliased_type_is_not_more_private(name, visibility, typ, span);
1194+
}
1195+
}
1196+
Type::Alias(alias_type, generics) => {
1197+
self.check_aliased_type_is_not_more_private(
1198+
name,
1199+
visibility,
1200+
&alias_type.borrow().get_type(generics),
1201+
span,
1202+
);
1203+
}
1204+
Type::Function(args, return_type, env, _) => {
1205+
for arg in args {
1206+
self.check_aliased_type_is_not_more_private(name, visibility, arg, span);
1207+
}
1208+
self.check_aliased_type_is_not_more_private(name, visibility, return_type, span);
1209+
self.check_aliased_type_is_not_more_private(name, visibility, env, span);
1210+
}
1211+
Type::MutableReference(typ) | Type::Array(_, typ) | Type::Slice(typ) => {
1212+
self.check_aliased_type_is_not_more_private(name, visibility, typ, span);
1213+
}
1214+
Type::InfixExpr(left, _op, right) => {
1215+
self.check_aliased_type_is_not_more_private(name, visibility, left, span);
1216+
self.check_aliased_type_is_not_more_private(name, visibility, right, span);
1217+
}
1218+
Type::FieldElement
1219+
| Type::Integer(..)
1220+
| Type::Bool
1221+
| Type::String(..)
1222+
| Type::FmtString(..)
1223+
| Type::Unit
1224+
| Type::Quoted(..)
1225+
| Type::TypeVariable(..)
1226+
| Type::Forall(..)
1227+
| Type::TraitAsType(..)
1228+
| Type::Constant(..)
1229+
| Type::NamedGeneric(..)
1230+
| Type::Error => (),
1231+
}
1232+
}
1233+
11411234
fn collect_struct_definitions(&mut self, structs: &BTreeMap<StructId, UnresolvedStruct>) {
11421235
// This is necessary to avoid cloning the entire struct map
11431236
// when adding checks after each struct field is resolved.

compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ impl<'a> ModCollector<'a> {
306306
let doc_comments = type_alias.doc_comments;
307307
let type_alias = type_alias.item;
308308
let name = type_alias.name.clone();
309+
let visibility = type_alias.visibility;
309310

310311
// And store the TypeId -> TypeAlias mapping somewhere it is reachable
311312
let unresolved = UnresolvedTypeAlias {
@@ -327,8 +328,19 @@ impl<'a> ModCollector<'a> {
327328
context.def_interner.set_doc_comments(ReferenceId::Alias(type_alias_id), doc_comments);
328329

329330
// Add the type alias to scope so its path can be looked up later
330-
let result = self.def_collector.def_map.modules[self.module_id.0]
331-
.declare_type_alias(name.clone(), type_alias_id);
331+
let result = self.def_collector.def_map.modules[self.module_id.0].declare_type_alias(
332+
name.clone(),
333+
visibility,
334+
type_alias_id,
335+
);
336+
337+
let parent_module_id = ModuleId { krate, local_id: self.module_id };
338+
context.def_interner.usage_tracker.add_unused_item(
339+
parent_module_id,
340+
name.clone(),
341+
UnusedItem::TypeAlias(type_alias_id),
342+
visibility,
343+
);
332344

333345
if let Err((first_def, second_def)) = result {
334346
let err = DefCollectorErrorKind::Duplicate {
@@ -526,7 +538,11 @@ impl<'a> ModCollector<'a> {
526538
TraitItem::Type { name } => {
527539
if let Err((first_def, second_def)) = self.def_collector.def_map.modules
528540
[trait_id.0.local_id.0]
529-
.declare_type_alias(name.clone(), TypeAliasId::dummy_id())
541+
.declare_type_alias(
542+
name.clone(),
543+
ItemVisibility::Public,
544+
TypeAliasId::dummy_id(),
545+
)
530546
{
531547
let error = DefCollectorErrorKind::Duplicate {
532548
typ: DuplicateType::TraitAssociatedType,

compiler/noirc_frontend/src/hir/def_map/module_data.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,10 @@ impl ModuleData {
112112
pub fn declare_type_alias(
113113
&mut self,
114114
name: Ident,
115+
visibility: ItemVisibility,
115116
id: TypeAliasId,
116117
) -> Result<(), (Ident, Ident)> {
117-
self.declare(name, ItemVisibility::Public, id.into(), None)
118+
self.declare(name, visibility, id.into(), None)
118119
}
119120

120121
pub fn declare_trait(

compiler/noirc_frontend/src/hir/resolution/errors.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ pub enum ResolverError {
130130
MutatingComptimeInNonComptimeContext { name: String, span: Span },
131131
#[error("Failed to parse `{statement}` as an expression")]
132132
InvalidInternedStatementInExpr { statement: String, span: Span },
133+
#[error("Type `{typ}` is more private than item `{item}`")]
134+
TypeIsMorePrivateThenItem { typ: String, item: String, span: Span },
133135
}
134136

135137
impl ResolverError {
@@ -529,6 +531,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic {
529531
*span,
530532
)
531533
},
534+
ResolverError::TypeIsMorePrivateThenItem { typ, item, span } => {
535+
Diagnostic::simple_warning(
536+
format!("Type `{typ}` is more private than item `{item}`"),
537+
String::new(),
538+
*span,
539+
)
540+
},
532541
}
533542
}
534543
}

compiler/noirc_frontend/src/parser/parser.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -290,14 +290,21 @@ fn contract(
290290
fn type_alias_definition() -> impl NoirParser<TopLevelStatementKind> {
291291
use self::Keyword::Type;
292292

293-
let p = ignore_then_commit(keyword(Type), ident());
294-
let p = then_commit(p, function::generics());
295-
let p = then_commit_ignore(p, just(Token::Assign));
296-
let p = then_commit(p, parse_type());
297-
298-
p.map_with_span(|((name, generics), typ), span| {
299-
TopLevelStatementKind::TypeAlias(NoirTypeAlias { name, generics, typ, span })
300-
})
293+
item_visibility()
294+
.then_ignore(keyword(Type))
295+
.then(ident())
296+
.then(function::generics())
297+
.then_ignore(just(Token::Assign))
298+
.then(parse_type())
299+
.map_with_span(|(((visibility, name), generics), typ), span| {
300+
TopLevelStatementKind::TypeAlias(NoirTypeAlias {
301+
name,
302+
generics,
303+
typ,
304+
visibility,
305+
span,
306+
})
307+
})
301308
}
302309

303310
fn self_parameter() -> impl NoirParser<Param> {

compiler/noirc_frontend/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,8 +2659,8 @@ fn incorrect_generic_count_on_struct_impl() {
26592659
#[test]
26602660
fn incorrect_generic_count_on_type_alias() {
26612661
let src = r#"
2662-
struct Foo {}
2663-
type Bar = Foo<i32>;
2662+
pub struct Foo {}
2663+
pub type Bar = Foo<i32>;
26642664
fn main() {}
26652665
"#;
26662666

compiler/noirc_frontend/src/tests/unused_items.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,77 @@ fn silences_unused_variable_warning() {
171171
"#;
172172
assert_no_errors(src);
173173
}
174+
175+
#[test]
176+
fn errors_on_unused_type_alias() {
177+
let src = r#"
178+
type Foo = Field;
179+
type Bar = Field;
180+
pub fn bar(_: Bar) {}
181+
fn main() {}
182+
"#;
183+
184+
let errors = get_program_errors(src);
185+
assert_eq!(errors.len(), 1);
186+
187+
let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) =
188+
&errors[0].0
189+
else {
190+
panic!("Expected an unused item error");
191+
};
192+
193+
assert_eq!(ident.to_string(), "Foo");
194+
assert_eq!(*item_type, "type alias");
195+
}
196+
197+
#[test]
198+
fn errors_if_type_alias_aliases_more_private_type() {
199+
let src = r#"
200+
struct Foo {}
201+
pub type Bar = Foo;
202+
pub fn no_unused_warnings(_b: Bar) {
203+
let _ = Foo {};
204+
}
205+
fn main() {}
206+
"#;
207+
208+
let errors = get_program_errors(src);
209+
assert_eq!(errors.len(), 1);
210+
211+
let CompilationError::ResolverError(ResolverError::TypeIsMorePrivateThenItem {
212+
typ, item, ..
213+
}) = &errors[0].0
214+
else {
215+
panic!("Expected an unused item error");
216+
};
217+
218+
assert_eq!(typ, "Foo");
219+
assert_eq!(item, "Bar");
220+
}
221+
222+
#[test]
223+
fn errors_if_type_alias_aliases_more_private_type_in_generic() {
224+
let src = r#"
225+
pub struct Generic<T> { value: T }
226+
struct Foo {}
227+
pub type Bar = Generic<Foo>;
228+
pub fn no_unused_warnings(_b: Bar) {
229+
let _ = Foo {};
230+
let _ = Generic { value: 1 };
231+
}
232+
fn main() {}
233+
"#;
234+
235+
let errors = get_program_errors(src);
236+
assert_eq!(errors.len(), 1);
237+
238+
let CompilationError::ResolverError(ResolverError::TypeIsMorePrivateThenItem {
239+
typ, item, ..
240+
}) = &errors[0].0
241+
else {
242+
panic!("Expected an unused item error");
243+
};
244+
245+
assert_eq!(typ, "Foo");
246+
assert_eq!(item, "Bar");
247+
}

compiler/noirc_frontend/src/usage_tracker.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
ast::{Ident, ItemVisibility},
55
hir::def_map::ModuleId,
66
macros_api::StructId,
7-
node_interner::{FuncId, TraitId},
7+
node_interner::{FuncId, TraitId, TypeAliasId},
88
};
99

1010
#[derive(Debug)]
@@ -13,6 +13,7 @@ pub enum UnusedItem {
1313
Function(FuncId),
1414
Struct(StructId),
1515
Trait(TraitId),
16+
TypeAlias(TypeAliasId),
1617
}
1718

1819
impl UnusedItem {
@@ -22,6 +23,7 @@ impl UnusedItem {
2223
UnusedItem::Function(_) => "function",
2324
UnusedItem::Struct(_) => "struct",
2425
UnusedItem::Trait(_) => "trait",
26+
UnusedItem::TypeAlias(_) => "type alias",
2527
}
2628
}
2729
}

docs/docs/noir/concepts/data_types/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ type Bad2 = Bad1;
105105
// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2
106106
```
107107

108+
By default, like functions, type aliases are private to the module the exist in. You can use `pub`
109+
to make the type alias public or `pub(crate)` to make it public to just its crate:
110+
111+
```rust
112+
// This type alias is now public
113+
pub type Id = u8;
114+
```
115+
108116
## Wildcard Type
109117
Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type.
110118

0 commit comments

Comments
 (0)