Skip to content

Commit c66873d

Browse files
authored
Merge 3986a3f into 102ebe3
2 parents 102ebe3 + 3986a3f commit c66873d

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),
@@ -607,9 +609,10 @@ fn quoted_as_module(
607609

608610
let path = parse(argument, parser::path_no_turbofish(), "a path").ok();
609611
let option_value = path.and_then(|path| {
610-
let module = interpreter.elaborate_item(interpreter.current_function, |elaborator| {
611-
elaborator.resolve_module_by_path(path)
612-
});
612+
let module = interpreter
613+
.elaborate_in_function(interpreter.current_function, |elaborator| {
614+
elaborator.resolve_module_by_path(path)
615+
});
613616
module.map(Value::ModuleDefinition)
614617
});
615618

@@ -625,7 +628,7 @@ fn quoted_as_trait_constraint(
625628
let argument = check_one_argument(arguments, location)?;
626629
let trait_bound = parse(argument, parser::trait_bound(), "a trait constraint")?;
627630
let bound = interpreter
628-
.elaborate_item(interpreter.current_function, |elaborator| {
631+
.elaborate_in_function(interpreter.current_function, |elaborator| {
629632
elaborator.resolve_trait_bound(&trait_bound, Type::Unit)
630633
})
631634
.ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?;
@@ -641,8 +644,8 @@ fn quoted_as_type(
641644
) -> IResult<Value> {
642645
let argument = check_one_argument(arguments, location)?;
643646
let typ = parse(argument, parser::parse_type(), "a type")?;
644-
let typ =
645-
interpreter.elaborate_item(interpreter.current_function, |elab| elab.resolve_type(typ));
647+
let typ = interpreter
648+
.elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ));
646649
Ok(Value::Type(typ))
647650
}
648651

@@ -1712,23 +1715,25 @@ fn expr_resolve(
17121715
interpreter.current_function
17131716
};
17141717

1715-
let value = interpreter.elaborate_item(function_to_resolve_in, |elaborator| match expr_value {
1716-
ExprValue::Expression(expression_kind) => {
1717-
let expr = Expression { kind: expression_kind, span: self_argument_location.span };
1718-
let (expr_id, _) = elaborator.elaborate_expression(expr);
1719-
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1720-
}
1721-
ExprValue::Statement(statement_kind) => {
1722-
let statement = Statement { kind: statement_kind, span: self_argument_location.span };
1723-
let (stmt_id, _) = elaborator.elaborate_statement(statement);
1724-
Value::TypedExpr(TypedExpr::StmtId(stmt_id))
1725-
}
1726-
ExprValue::LValue(lvalue) => {
1727-
let expr = lvalue.as_expression();
1728-
let (expr_id, _) = elaborator.elaborate_expression(expr);
1729-
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1730-
}
1731-
});
1718+
let value =
1719+
interpreter.elaborate_in_function(function_to_resolve_in, |elaborator| match expr_value {
1720+
ExprValue::Expression(expression_kind) => {
1721+
let expr = Expression { kind: expression_kind, span: self_argument_location.span };
1722+
let (expr_id, _) = elaborator.elaborate_expression(expr);
1723+
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1724+
}
1725+
ExprValue::Statement(statement_kind) => {
1726+
let statement =
1727+
Statement { kind: statement_kind, span: self_argument_location.span };
1728+
let (stmt_id, _) = elaborator.elaborate_statement(statement);
1729+
Value::TypedExpr(TypedExpr::StmtId(stmt_id))
1730+
}
1731+
ExprValue::LValue(lvalue) => {
1732+
let expr = lvalue.as_expression();
1733+
let (expr_id, _) = elaborator.elaborate_expression(expr);
1734+
Value::TypedExpr(TypedExpr::ExprId(expr_id))
1735+
}
1736+
});
17321737

17331738
Ok(value)
17341739
}
@@ -1996,7 +2001,7 @@ fn function_def_set_parameters(
19962001
"a pattern",
19972002
)?;
19982003

1999-
let hir_pattern = interpreter.elaborate_item(Some(func_id), |elaborator| {
2004+
let hir_pattern = interpreter.elaborate_in_function(Some(func_id), |elaborator| {
20002005
elaborator.elaborate_pattern_and_store_ids(
20012006
parameter_pattern,
20022007
parameter_type.clone(),
@@ -2063,6 +2068,34 @@ fn function_def_set_return_public(
20632068
Ok(Value::Unit)
20642069
}
20652070

2071+
// fn add_item(self, item: Quoted)
2072+
fn module_add_item(
2073+
interpreter: &mut Interpreter,
2074+
arguments: Vec<(Value, Location)>,
2075+
location: Location,
2076+
) -> IResult<Value> {
2077+
let (self_argument, item) = check_two_arguments(arguments, location)?;
2078+
let module_id = get_module(self_argument)?;
2079+
let module_data = interpreter.elaborator.get_module(module_id);
2080+
2081+
let parser = parser::top_level_items();
2082+
let top_level_statements = parse(item, parser, "a top-level item")?;
2083+
2084+
interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| {
2085+
let mut generated_items = CollectedItems::default();
2086+
2087+
for top_level_statement in top_level_statements {
2088+
elaborator.add_item(top_level_statement, &mut generated_items, location);
2089+
}
2090+
2091+
if !generated_items.is_empty() {
2092+
elaborator.elaborate_items(generated_items);
2093+
}
2094+
});
2095+
2096+
Ok(Value::Unit)
2097+
}
2098+
20662099
// fn functions(self) -> [FunctionDefinition]
20672100
fn module_functions(
20682101
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)