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: visibility for globals #6161

Merged
merged 6 commits into from
Sep 26, 2024
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
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@
noir_trait_impl.accept(self.span, visitor);
}
ItemKind::Impl(type_impl) => type_impl.accept(self.span, visitor),
ItemKind::Global(let_statement) => {
ItemKind::Global(let_statement, _visibility) => {
if visitor.visit_global(let_statement, self.span) {
let_statement.accept(visitor);
}
Expand Down Expand Up @@ -1296,8 +1296,8 @@
UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.span),
UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.span),
UnresolvedTypeData::FieldElement => visitor.visit_field_element_type(self.span),
UnresolvedTypeData::Integer(signdness, size) => {

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

Check warning on line 1300 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.span),
UnresolvedTypeData::Unit => visitor.visit_unit_type(self.span),
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_frontend/src/elaborator/comptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,11 +439,12 @@ impl<'context> Elaborator<'context> {
resolved_trait_generics: Vec::new(),
});
}
TopLevelStatementKind::Global(global) => {
TopLevelStatementKind::Global(global, visibility) => {
let (global, error) = dc_mod::collect_global(
self.interner,
self.def_maps.get_mut(&self.crate_id).unwrap(),
Documented::new(global, item.doc_comments),
visibility,
self.file,
self.local_module,
self.crate_id,
Expand Down
18 changes: 14 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 @@ -139,15 +139,16 @@
fn collect_globals(
&mut self,
context: &mut Context,
globals: Vec<Documented<LetStatement>>,
globals: Vec<(Documented<LetStatement>, ItemVisibility)>,
crate_id: CrateId,
) -> Vec<(CompilationError, fm::FileId)> {
let mut errors = vec![];
for global in globals {
for (global, visibility) in globals {
let (global, error) = collect_global(
&mut context.def_interner,
&mut self.def_collector.def_map,
global,
visibility,
self.file_id,
self.module_id,
crate_id,
Expand Down Expand Up @@ -515,7 +516,7 @@

if let Err((first_def, second_def)) = self.def_collector.def_map.modules
[trait_id.0.local_id.0]
.declare_global(name.clone(), global_id)
.declare_global(name.clone(), ItemVisibility::Public, global_id)
{
let error = DefCollectorErrorKind::Duplicate {
typ: DuplicateType::TraitAssociatedConst,
Expand Down Expand Up @@ -816,7 +817,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 820 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 =
Expand Down Expand Up @@ -1160,6 +1161,7 @@
interner: &mut NodeInterner,
def_map: &mut CrateDefMap,
global: Documented<LetStatement>,
visibility: ItemVisibility,
file_id: FileId,
module_id: LocalModuleId,
crate_id: CrateId,
Expand All @@ -1180,7 +1182,15 @@
);

// Add the statement to the scope so its path can be looked up later
let result = def_map.modules[module_id.0].declare_global(name, global_id);
let result = def_map.modules[module_id.0].declare_global(name.clone(), visibility, global_id);

let parent_module_id = ModuleId { krate: crate_id, local_id: module_id };
interner.usage_tracker.add_unused_item(
parent_module_id,
name,
UnusedItem::Global(global_id),
visibility,
);

let error = result.err().map(|(first_def, second_def)| {
let err =
Expand Down
9 changes: 7 additions & 2 deletions compiler/noirc_frontend/src/hir/def_map/module_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,13 @@ impl ModuleData {
self.definitions.remove_definition(name);
}

pub fn declare_global(&mut self, name: Ident, id: GlobalId) -> Result<(), (Ident, Ident)> {
self.declare(name, ItemVisibility::Public, id.into(), None)
pub fn declare_global(
&mut self,
name: Ident,
visibility: ItemVisibility,
id: GlobalId,
) -> Result<(), (Ident, Ident)> {
self.declare(name, visibility, id.into(), None)
}

pub fn declare_struct(
Expand Down
37 changes: 24 additions & 13 deletions compiler/noirc_frontend/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub enum TopLevelStatementKind {
Impl(TypeImpl),
TypeAlias(NoirTypeAlias),
SubModule(ParsedSubModule),
Global(LetStatement),
Global(LetStatement, ItemVisibility),
InnerAttribute(SecondaryAttribute),
Error,
}
Expand All @@ -64,7 +64,7 @@ impl TopLevelStatementKind {
TopLevelStatementKind::Impl(i) => Some(ItemKind::Impl(i)),
TopLevelStatementKind::TypeAlias(t) => Some(ItemKind::TypeAlias(t)),
TopLevelStatementKind::SubModule(s) => Some(ItemKind::Submodules(s)),
TopLevelStatementKind::Global(c) => Some(ItemKind::Global(c)),
TopLevelStatementKind::Global(c, visibility) => Some(ItemKind::Global(c, visibility)),
TopLevelStatementKind::InnerAttribute(a) => Some(ItemKind::InnerAttribute(a)),
TopLevelStatementKind::Error => None,
}
Expand Down Expand Up @@ -249,7 +249,7 @@ pub struct SortedModule {
pub trait_impls: Vec<NoirTraitImpl>,
pub impls: Vec<TypeImpl>,
pub type_aliases: Vec<Documented<NoirTypeAlias>>,
pub globals: Vec<Documented<LetStatement>>,
pub globals: Vec<(Documented<LetStatement>, ItemVisibility)>,

/// Module declarations like `mod foo;`
pub module_decls: Vec<Documented<ModuleDeclaration>>,
Expand All @@ -271,7 +271,7 @@ impl std::fmt::Display for SortedModule {
write!(f, "{import}")?;
}

for global_const in &self.globals {
for (global_const, _visibility) in &self.globals {
write!(f, "{global_const}")?;
}

Expand Down Expand Up @@ -321,7 +321,9 @@ impl ParsedModule {
ItemKind::TypeAlias(type_alias) => {
module.push_type_alias(type_alias, item.doc_comments);
}
ItemKind::Global(global) => module.push_global(global, item.doc_comments),
ItemKind::Global(global, visibility) => {
module.push_global(global, visibility, item.doc_comments);
}
ItemKind::ModuleDecl(mod_name) => {
module.push_module_decl(mod_name, item.doc_comments);
}
Expand Down Expand Up @@ -354,7 +356,7 @@ pub enum ItemKind {
TraitImpl(NoirTraitImpl),
Impl(TypeImpl),
TypeAlias(NoirTypeAlias),
Global(LetStatement),
Global(LetStatement, ItemVisibility),
ModuleDecl(ModuleDeclaration),
Submodules(ParsedSubModule),
InnerAttribute(SecondaryAttribute),
Expand Down Expand Up @@ -438,8 +440,13 @@ impl SortedModule {
self.submodules.push(Documented::new(submodule, doc_comments));
}

fn push_global(&mut self, global: LetStatement, doc_comments: Vec<String>) {
self.globals.push(Documented::new(global, doc_comments));
fn push_global(
&mut self,
global: LetStatement,
visibility: ItemVisibility,
doc_comments: Vec<String>,
) {
self.globals.push((Documented::new(global, doc_comments), visibility));
}
}

Expand Down Expand Up @@ -526,19 +533,23 @@ impl std::fmt::Display for TopLevelStatementKind {
TopLevelStatementKind::Function(fun) => fun.fmt(f),
TopLevelStatementKind::Module(m) => m.fmt(f),
TopLevelStatementKind::Import(tree, visibility) => {
if visibility == &ItemVisibility::Private {
write!(f, "use {tree}")
} else {
write!(f, "{visibility} use {tree}")
if visibility != &ItemVisibility::Private {
write!(f, "{visibility} ")?;
}
write!(f, "use {tree}")
}
TopLevelStatementKind::Trait(t) => t.fmt(f),
TopLevelStatementKind::TraitImpl(i) => i.fmt(f),
TopLevelStatementKind::Struct(s) => s.fmt(f),
TopLevelStatementKind::Impl(i) => i.fmt(f),
TopLevelStatementKind::TypeAlias(t) => t.fmt(f),
TopLevelStatementKind::SubModule(s) => s.fmt(f),
TopLevelStatementKind::Global(c) => c.fmt(f),
TopLevelStatementKind::Global(c, visibility) => {
if visibility != &ItemVisibility::Private {
write!(f, "{visibility} ")?;
}
c.fmt(f)
}
TopLevelStatementKind::InnerAttribute(a) => write!(f, "#![{}]", a),
TopLevelStatementKind::Error => write!(f, "error"),
}
Expand Down
18 changes: 15 additions & 3 deletions compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ fn implementation() -> impl NoirParser<TopLevelStatementKind> {
/// global_declaration: 'global' ident global_type_annotation '=' literal
fn global_declaration() -> impl NoirParser<TopLevelStatementKind> {
let p = attributes::attributes()
.then(item_visibility())
.then(maybe_comp_time())
.then(spanned(keyword(Keyword::Mut)).or_not())
.then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global))
Expand All @@ -229,7 +230,9 @@ fn global_declaration() -> impl NoirParser<TopLevelStatementKind> {
let p = then_commit_ignore(p, just(Token::Assign));
let p = then_commit(p, expression());
p.validate(
|(((((attributes, comptime), mutable), mut pattern), r#type), expression), span, emit| {
|((((((attributes, visibility), comptime), mutable), mut pattern), r#type), expression),
span,
emit| {
let global_attributes =
attributes::validate_secondary_attributes(attributes, span, emit);

Expand All @@ -239,10 +242,19 @@ fn global_declaration() -> impl NoirParser<TopLevelStatementKind> {
let span = mut_span.merge(pattern.span());
pattern = Pattern::Mutable(Box::new(pattern), span, false);
}
LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes }
(
LetStatement {
pattern,
r#type,
comptime,
expression,
attributes: global_attributes,
},
visibility,
)
},
)
.map(TopLevelStatementKind::Global)
.map(|(let_statement, visibility)| TopLevelStatementKind::Global(let_statement, visibility))
}

/// submodule: 'mod' ident '{' module '}'
Expand Down
4 changes: 3 additions & 1 deletion compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1376,7 +1376,9 @@
// Mutable globals are only allowed in a comptime context
let src = r#"
mut global FOO: Field = 0;
fn main() {}
fn main() {
let _ = FOO; // silence FOO never used warning
}
"#;
assert_eq!(get_program_errors(src).len(), 1);
}
Expand Down Expand Up @@ -2996,7 +2998,7 @@
}

#[test]
fn arithmetic_generics_canonicalization_deduplication_regression() {

Check warning on line 3001 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (canonicalization)
let source = r#"
struct ArrData<let N: u32> {
a: [Field; N],
Expand Down
23 changes: 23 additions & 0 deletions compiler/noirc_frontend/src/tests/unused_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,26 @@ fn errors_if_type_alias_aliases_more_private_type_in_generic() {
assert_eq!(typ, "Foo");
assert_eq!(item, "Bar");
}

#[test]
fn warns_on_unused_global() {
let src = r#"
global foo = 1;
global bar = 1;

fn main() {
let _ = bar;
}
"#;

let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0].0
else {
panic!("Expected an unused item warning");
};

assert_eq!(ident.to_string(), "foo");
assert_eq!(item.item_type(), "global");
}
4 changes: 3 additions & 1 deletion compiler/noirc_frontend/src/usage_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
ast::{Ident, ItemVisibility},
hir::def_map::ModuleId,
macros_api::StructId,
node_interner::{FuncId, TraitId, TypeAliasId},
node_interner::{FuncId, GlobalId, TraitId, TypeAliasId},
};

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand All @@ -14,6 +14,7 @@ pub enum UnusedItem {
Struct(StructId),
Trait(TraitId),
TypeAlias(TypeAliasId),
Global(GlobalId),
}

impl UnusedItem {
Expand All @@ -24,6 +25,7 @@ impl UnusedItem {
UnusedItem::Struct(_) => "struct",
UnusedItem::Trait(_) => "trait",
UnusedItem::TypeAlias(_) => "type alias",
UnusedItem::Global(_) => "global",
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions docs/docs/noir/concepts/globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,13 @@ fn foo() -> [Field; 100] { ... }
This is usually fine since Noir will generally optimize any function call that does not
refer to a program input into a constant. It should be kept in mind however, if the called
function performs side-effects like `println`, as these will still occur on each use.

### Visibility

By default, like functions, globals are private to the module the exist in. You can use `pub`
to make the global public or `pub(crate)` to make it public to just its crate:

```rust
// This global is now public
pub global N = 5;
```
2 changes: 1 addition & 1 deletion noir_stdlib/src/field/bn254.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::runtime::is_unconstrained;
global PLO: Field = 53438638232309528389504892708671455233;
global PHI: Field = 64323764613183177041862057485226039389;

global TWO_POW_128: Field = 0x100000000000000000000000000000000;
pub(crate) global TWO_POW_128: Field = 0x100000000000000000000000000000000;

// Decomposes a single field into two 16 byte fields.
fn compute_decomposition(x: Field) -> (Field, Field) {
Expand Down
2 changes: 1 addition & 1 deletion tooling/nargo_fmt/src/visitor/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ impl super::FmtVisitor<'_> {
ItemKind::Struct(_)
| ItemKind::Trait(_)
| ItemKind::TypeAlias(_)
| ItemKind::Global(_)
| ItemKind::Global(..)
| ItemKind::ModuleDecl(_)
| ItemKind::InnerAttribute(_) => {
self.push_rewrite(self.slice(span).to_string(), span);
Expand Down
Loading