Skip to content
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 docs/docs/noir/concepts/data_types/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ let slice : [[Field]] = &[];
## Dynamic Indexing

Using constant indices of arrays will often be more efficient at runtime in constrained code.
Indexing an array with non-constant indices (indices derived from the inputs to the program) is also
Indexing an array with non-constant indices (indices derived from the inputs to the program, or returned from unconstrained functions) is also
called "dynamic indexing" and incurs a slight runtime cost:

```rust
Expand Down
25 changes: 16 additions & 9 deletions tooling/ast_fuzzer/src/program/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,8 @@
/// Generate a function call to any function in the global context except `main`,
/// if the function returns the target type, or something we can use to produce that type.
///
/// Whether a call is dynamic depends on whether it has dynamic arguments.
/// Whether a call is dynamic depends on whether it has dynamic arguments,
/// and whether we are crossing an ACIR-to-Brillig boundary.
fn gen_call(
&mut self,
u: &mut Unstructured,
Expand All @@ -1208,7 +1209,12 @@
let opts = self
.call_targets
.iter()
.filter_map(|(id, types)| types.contains(typ).then_some(id))
.filter(|(id, types)|
// We need to be able to generate the type from what the function returns.
types.contains(typ) &&
// We might not be able to call this function, depending on context.
!(self.in_no_dynamic && !self.unconstrained() && self.callable_signature(**id).2))
.map(|(id, _)| id)
.collect::<Vec<_>>();

if opts.is_empty() {
Expand All @@ -1220,11 +1226,11 @@

let callee_id = *u.choose_iter(opts)?;
let callee_expr = self.callable_expr(callee_id);
let (param_types, return_type) = self.callable_signature(callee_id);
let (param_types, return_type, callee_unconstrained) = self.callable_signature(callee_id);

// Generate an expression for each argument.
let mut args = Vec::new();
let mut call_dyn = false;
let mut call_dyn = !self.unconstrained() && callee_unconstrained;
for typ in &param_types {
let (arg, arg_dyn) = self.gen_expr(u, typ, max_depth, Flags::CALL)?;
call_dyn |= arg_dyn;
Expand All @@ -1250,7 +1256,7 @@
callee_id: FuncId,
) -> arbitrary::Result<Expression> {
let callee_ident = self.func_ident(callee_id);
let (param_types, return_type) = self.callable_signature(CallableId::Global(callee_id));
let (param_types, return_type, _) = self.callable_signature(CallableId::Global(callee_id));

let mut args = Vec::new();
for typ in &param_types {
Expand Down Expand Up @@ -1501,20 +1507,21 @@
}

/// Get the parameter types and return type of a callable function.
fn callable_signature(&self, callee_id: CallableId) -> (Vec<Type>, Type) {
fn callable_signature(&self, callee_id: CallableId) -> (Vec<Type>, Type, bool) {
match callee_id {
CallableId::Global(id) => {
let decl = self.ctx.function_decl(id);
let return_type = decl.return_type.clone();
let param_types = decl.params.iter().map(|p| p.3.clone()).collect::<Vec<_>>();
(param_types, return_type)
(param_types, return_type, decl.unconstrained)
}
CallableId::Local(id) => {
let (_, _, typ) = self.locals.current().get_variable(&id);
let Type::Function(param_types, return_type, _, _) = types::unref(typ) else {
let Type::Function(param_types, return_type, _, unconstrained) = types::unref(typ)
else {
unreachable!("function pointers should have function type; got {typ}")
};
(param_types.clone(), return_type.as_ref().clone())
(param_types.clone(), return_type.as_ref().clone(), *unconstrained)
}
}
}
Expand All @@ -1541,9 +1548,9 @@
ctx.config.max_loop_size = 10;
ctx.config.vary_loop_size = false;
ctx.gen_main_decl(&mut u);
let mut fctx = FunctionContext::new(&mut ctx, FuncId(0));

Check warning on line 1551 in tooling/ast_fuzzer/src/program/func.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (fctx)
fctx.budget = 2;

Check warning on line 1552 in tooling/ast_fuzzer/src/program/func.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (fctx)
let loop_code = format!("{}", fctx.gen_loop(&mut u).unwrap()).replace(" ", "");

Check warning on line 1553 in tooling/ast_fuzzer/src/program/func.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (fctx)

assert!(
loop_code.starts_with(
Expand All @@ -1566,8 +1573,8 @@
ctx.config.max_loop_size = 10;
ctx.config.vary_loop_size = false;
ctx.gen_main_decl(&mut u);
let mut fctx = FunctionContext::new(&mut ctx, FuncId(0));

Check warning on line 1576 in tooling/ast_fuzzer/src/program/func.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (fctx)
fctx.budget = 2;

Check warning on line 1577 in tooling/ast_fuzzer/src/program/func.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (fctx)
let while_code = format!("{}", fctx.gen_while(&mut u).unwrap()).replace(" ", "");

assert!(
Expand Down
Loading