Skip to content

Commit af50a7b

Browse files
authored
feat: Module::add_item (#5947)
# Description ## Problem Resolves #5875 ## Summary ## Additional Context ## Documentation Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent 6004067 commit af50a7b

File tree

9 files changed

+145
-43
lines changed

9 files changed

+145
-43
lines changed

compiler/noirc_frontend/src/elaborator/comptime.rs

+34-10
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,41 @@ impl<'context> Elaborator<'context> {
5252
/// Elaborate an expression from the middle of a comptime scope.
5353
/// When this happens we require additional information to know
5454
/// what variables should be in scope.
55-
pub fn elaborate_item_from_comptime<'a, T>(
55+
pub fn elaborate_item_from_comptime_in_function<'a, T>(
5656
&'a mut self,
5757
current_function: Option<FuncId>,
5858
f: impl FnOnce(&mut Elaborator<'a>) -> T,
59+
) -> T {
60+
self.elaborate_item_from_comptime(f, |elaborator| {
61+
if let Some(function) = current_function {
62+
let meta = elaborator.interner.function_meta(&function);
63+
elaborator.current_item = Some(DependencyId::Function(function));
64+
elaborator.crate_id = meta.source_crate;
65+
elaborator.local_module = meta.source_module;
66+
elaborator.file = meta.source_file;
67+
elaborator.introduce_generics_into_scope(meta.all_generics.clone());
68+
}
69+
})
70+
}
71+
72+
pub fn elaborate_item_from_comptime_in_module<'a, T>(
73+
&'a mut self,
74+
module: ModuleId,
75+
file: FileId,
76+
f: impl FnOnce(&mut Elaborator<'a>) -> T,
77+
) -> T {
78+
self.elaborate_item_from_comptime(f, |elaborator| {
79+
elaborator.current_item = None;
80+
elaborator.crate_id = module.krate;
81+
elaborator.local_module = module.local_id;
82+
elaborator.file = file;
83+
})
84+
}
85+
86+
fn elaborate_item_from_comptime<'a, T>(
87+
&'a mut self,
88+
f: impl FnOnce(&mut Elaborator<'a>) -> T,
89+
setup: impl FnOnce(&mut Elaborator<'a>),
5990
) -> T {
6091
// Create a fresh elaborator to ensure no state is changed from
6192
// this elaborator
@@ -70,14 +101,7 @@ impl<'context> Elaborator<'context> {
70101
elaborator.function_context.push(FunctionContext::default());
71102
elaborator.scopes.start_function();
72103

73-
if let Some(function) = current_function {
74-
let meta = elaborator.interner.function_meta(&function);
75-
elaborator.current_item = Some(DependencyId::Function(function));
76-
elaborator.crate_id = meta.source_crate;
77-
elaborator.local_module = meta.source_module;
78-
elaborator.file = meta.source_file;
79-
elaborator.introduce_generics_into_scope(meta.all_generics.clone());
80-
}
104+
setup(&mut elaborator);
81105

82106
elaborator.populate_scope_from_comptime_scopes();
83107

@@ -351,7 +375,7 @@ impl<'context> Elaborator<'context> {
351375
}
352376
}
353377

354-
fn add_item(
378+
pub(crate) fn add_item(
355379
&mut self,
356380
item: TopLevelStatement,
357381
generated_items: &mut CollectedItems,

compiler/noirc_frontend/src/elaborator/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ impl<'context> Elaborator<'context> {
253253
this
254254
}
255255

256-
fn elaborate_items(&mut self, mut items: CollectedItems) {
256+
pub(crate) fn elaborate_items(&mut self, mut items: CollectedItems) {
257257
// We must first resolve and intern the globals before we can resolve any stmts inside each function.
258258
// Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope
259259
//

compiler/noirc_frontend/src/hir/comptime/interpreter.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::collections::VecDeque;
22
use std::{collections::hash_map::Entry, rc::Rc};
33

44
use acvm::{acir::AcirField, FieldElement};
5+
use fm::FileId;
56
use im::Vector;
67
use iter_extended::try_vecmap;
78
use noirc_errors::Location;
@@ -10,6 +11,7 @@ use rustc_hash::FxHashMap as HashMap;
1011
use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness};
1112
use crate::elaborator::Elaborator;
1213
use crate::graph::CrateId;
14+
use crate::hir::def_map::ModuleId;
1315
use crate::hir_def::expr::ImplKind;
1416
use crate::hir_def::function::FunctionBody;
1517
use crate::macros_api::UnaryOp;
@@ -170,7 +172,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> {
170172
Some(body) => Ok(body),
171173
None => {
172174
if matches!(&meta.function_body, FunctionBody::Unresolved(..)) {
173-
self.elaborate_item(None, |elaborator| {
175+
self.elaborate_in_function(None, |elaborator| {
174176
elaborator.elaborate_function(function);
175177
});
176178

@@ -183,13 +185,25 @@ impl<'local, 'interner> Interpreter<'local, 'interner> {
183185
}
184186
}
185187

186-
fn elaborate_item<T>(
188+
fn elaborate_in_function<T>(
187189
&mut self,
188190
function: Option<FuncId>,
189191
f: impl FnOnce(&mut Elaborator) -> T,
190192
) -> T {
191193
self.unbind_generics_from_previous_function();
192-
let result = self.elaborator.elaborate_item_from_comptime(function, f);
194+
let result = self.elaborator.elaborate_item_from_comptime_in_function(function, f);
195+
self.rebind_generics_from_previous_function();
196+
result
197+
}
198+
199+
fn elaborate_in_module<T>(
200+
&mut self,
201+
module: ModuleId,
202+
file: FileId,
203+
f: impl FnOnce(&mut Elaborator) -> T,
204+
) -> T {
205+
self.unbind_generics_from_previous_function();
206+
let result = self.elaborator.elaborate_item_from_comptime_in_module(module, file, f);
193207
self.rebind_generics_from_previous_function();
194208
result
195209
}
@@ -1244,7 +1258,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> {
12441258
let mut result = self.call_function(function_id, arguments, bindings, location)?;
12451259
if call.is_macro_call {
12461260
let expr = result.into_expression(self.elaborator.interner, location)?;
1247-
let expr = self.elaborate_item(self.current_function, |elaborator| {
1261+
let expr = self.elaborate_in_function(self.current_function, |elaborator| {
12481262
elaborator.elaborate_expression(expr).0
12491263
});
12501264
result = self.evaluate(expr)?;

compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs

+57-24
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::{
2525
FunctionReturnType, IntegerBitSize, LValue, Literal, Statement, StatementKind, UnaryOp,
2626
UnresolvedType, UnresolvedTypeData, Visibility,
2727
},
28+
hir::def_collector::dc_crate::CollectedItems,
2829
hir::{
2930
comptime::{
3031
errors::IResult,
@@ -117,6 +118,7 @@ impl<'local, 'context> Interpreter<'local, 'context> {
117118
"function_def_set_return_public" => {
118119
function_def_set_return_public(self, arguments, location)
119120
}
121+
"module_add_item" => module_add_item(self, arguments, location),
120122
"module_functions" => module_functions(self, arguments, location),
121123
"module_has_named_attribute" => module_has_named_attribute(self, arguments, location),
122124
"module_is_contract" => module_is_contract(self, arguments, location),
@@ -663,9 +665,10 @@ fn quoted_as_module(
663665

664666
let path = parse(argument, parser::path_no_turbofish(), "a path").ok();
665667
let option_value = path.and_then(|path| {
666-
let module = interpreter.elaborate_item(interpreter.current_function, |elaborator| {
667-
elaborator.resolve_module_by_path(path)
668-
});
668+
let module = interpreter
669+
.elaborate_in_function(interpreter.current_function, |elaborator| {
670+
elaborator.resolve_module_by_path(path)
671+
});
669672
module.map(Value::ModuleDefinition)
670673
});
671674

@@ -681,7 +684,7 @@ fn quoted_as_trait_constraint(
681684
let argument = check_one_argument(arguments, location)?;
682685
let trait_bound = parse(argument, parser::trait_bound(), "a trait constraint")?;
683686
let bound = interpreter
684-
.elaborate_item(interpreter.current_function, |elaborator| {
687+
.elaborate_in_function(interpreter.current_function, |elaborator| {
685688
elaborator.resolve_trait_bound(&trait_bound, Type::Unit)
686689
})
687690
.ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?;
@@ -697,8 +700,8 @@ fn quoted_as_type(
697700
) -> IResult<Value> {
698701
let argument = check_one_argument(arguments, location)?;
699702
let typ = parse(argument, parser::parse_type(), "a type")?;
700-
let typ =
701-
interpreter.elaborate_item(interpreter.current_function, |elab| elab.resolve_type(typ));
703+
let typ = interpreter
704+
.elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ));
702705
Ok(Value::Type(typ))
703706
}
704707

@@ -1768,23 +1771,25 @@ fn expr_resolve(
17681771
interpreter.current_function
17691772
};
17701773

1771-
let value = interpreter.elaborate_item(function_to_resolve_in, |elaborator| match expr_value {
1772-
ExprValue::Expression(expression_kind) => {
1773-
let expr = Expression { kind: expression_kind, span: self_argument_location.span };
1774-
let (expr_id, _) = elaborator.elaborate_expression(expr);
1775-
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1776-
}
1777-
ExprValue::Statement(statement_kind) => {
1778-
let statement = Statement { kind: statement_kind, span: self_argument_location.span };
1779-
let (stmt_id, _) = elaborator.elaborate_statement(statement);
1780-
Value::TypedExpr(TypedExpr::StmtId(stmt_id))
1781-
}
1782-
ExprValue::LValue(lvalue) => {
1783-
let expr = lvalue.as_expression();
1784-
let (expr_id, _) = elaborator.elaborate_expression(expr);
1785-
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1786-
}
1787-
});
1774+
let value =
1775+
interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value {
1776+
ExprValue::Expression(expression_kind) => {
1777+
let expr = Expression { kind: expression_kind, span: self_argument_location.span };
1778+
let (expr_id, _) = elaborator.elaborate_expression(expr);
1779+
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1780+
}
1781+
ExprValue::Statement(statement_kind) => {
1782+
let statement =
1783+
Statement { kind: statement_kind, span: self_argument_location.span };
1784+
let (stmt_id, _) = elaborator.elaborate_statement(statement);
1785+
Value::TypedExpr(TypedExpr::StmtId(stmt_id))
1786+
}
1787+
ExprValue::LValue(lvalue) => {
1788+
let expr = lvalue.as_expression();
1789+
let (expr_id, _) = elaborator.elaborate_expression(expr);
1790+
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1791+
}
1792+
});
17881793

17891794
Ok(value)
17901795
}
@@ -2052,7 +2057,7 @@ fn function_def_set_parameters(
20522057
"a pattern",
20532058
)?;
20542059

2055-
let hir_pattern = interpreter.elaborate_item(Some(func_id), |elaborator| {
2060+
let hir_pattern = interpreter.elaborate_in_function(Some(func_id), |elaborator| {
20562061
elaborator.elaborate_pattern_and_store_ids(
20572062
parameter_pattern,
20582063
parameter_type.clone(),
@@ -2119,6 +2124,34 @@ fn function_def_set_return_public(
21192124
Ok(Value::Unit)
21202125
}
21212126

2127+
// fn add_item(self, item: Quoted)
2128+
fn module_add_item(
2129+
interpreter: &mut Interpreter,
2130+
arguments: Vec<(Value, Location)>,
2131+
location: Location,
2132+
) -> IResult<Value> {
2133+
let (self_argument, item) = check_two_arguments(arguments, location)?;
2134+
let module_id = get_module(self_argument)?;
2135+
let module_data = interpreter.elaborator.get_module(module_id);
2136+
2137+
let parser = parser::top_level_items();
2138+
let top_level_statements = parse(item, parser, "a top-level item")?;
2139+
2140+
interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| {
2141+
let mut generated_items = CollectedItems::default();
2142+
2143+
for top_level_statement in top_level_statements {
2144+
elaborator.add_item(top_level_statement, &mut generated_items, location);
2145+
}
2146+
2147+
if !generated_items.is_empty() {
2148+
elaborator.elaborate_items(generated_items);
2149+
}
2150+
});
2151+
2152+
Ok(Value::Unit)
2153+
}
2154+
21222155
// fn functions(self) -> [FunctionDefinition]
21232156
fn module_functions(
21242157
interpreter: &Interpreter,

compiler/noirc_frontend/src/parser/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use noirc_errors::Span;
2626
pub use parser::path::path_no_turbofish;
2727
pub use parser::traits::trait_bound;
2828
pub use parser::{
29-
block, expression, fresh_statement, lvalue, parse_program, parse_type, pattern,
30-
top_level_items, visibility,
29+
block, expression, fresh_statement, lvalue, module, parse_program, parse_type, pattern,
30+
top_level_items, top_level_statement, visibility,
3131
};
3232

3333
#[derive(Debug, Clone)]

compiler/noirc_frontend/src/parser/parser.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ fn program() -> impl NoirParser<ParsedModule> {
175175

176176
/// module: top_level_statement module
177177
/// | %empty
178-
fn module() -> impl NoirParser<ParsedModule> {
178+
pub fn module() -> impl NoirParser<ParsedModule> {
179179
recursive(|module_parser| {
180180
empty()
181181
.to(ParsedModule::default())
@@ -202,7 +202,7 @@ pub fn top_level_items() -> impl NoirParser<Vec<TopLevelStatement>> {
202202
/// | module_declaration
203203
/// | use_statement
204204
/// | global_declaration
205-
fn top_level_statement<'a>(
205+
pub fn top_level_statement<'a>(
206206
module_parser: impl NoirParser<ParsedModule> + 'a,
207207
) -> impl NoirParser<TopLevelStatement> + 'a {
208208
choice((

docs/docs/noir/standard_library/meta/module.md

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ declarations in the source program.
88

99
## Methods
1010

11+
### add_item
12+
13+
#include_code add_item noir_stdlib/src/meta/module.nr rust
14+
15+
Adds a top-level item (a function, a struct, a global, etc.) to the module.
16+
Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it.
17+
Note that the items are type-checked as if they are inside the module they are being added to.
18+
1119
### name
1220

1321
#include_code name noir_stdlib/src/meta/module.nr rust

noir_stdlib/src/meta/module.nr

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
impl Module {
2+
#[builtin(module_add_item)]
3+
// docs:start:add_item
4+
fn add_item(self, item: Quoted) {}
5+
// docs:end:add_item
6+
27
#[builtin(module_has_named_attribute)]
38
// docs:start:has_named_attribute
49
fn has_named_attribute(self, name: Quoted) -> bool {}

test_programs/compile_success_empty/comptime_module/src/main.nr

+18
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ fn outer_attribute_separate_module(m: Module) {
4242
increment_counter();
4343
}
4444

45+
struct Foo {}
46+
47+
#[add_function]
48+
mod add_to_me {
49+
fn add_to_me_function() {}
50+
}
51+
52+
fn add_function(m: Module) {
53+
m.add_item(
54+
quote { pub fn added_function() -> super::Foo {
55+
add_to_me_function();
56+
super::Foo {}
57+
} }
58+
);
59+
}
60+
4561
fn main() {
4662
comptime
4763
{
@@ -73,6 +89,8 @@ fn main() {
7389

7490
yet_another_module::generated_outer_function();
7591
yet_another_module::generated_inner_function();
92+
93+
let _ = add_to_me::added_function();
7694
}
7795

7896
// docs:start:as_module_example

0 commit comments

Comments
 (0)