Skip to content

Commit

Permalink
feat: Sync from noir (AztecProtocol/aztec-packages#5416)
Browse files Browse the repository at this point in the history
Automated pull of development from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
chore(github): Improve PR template "document later" checkbox description
(#4625)
chore: Update integers.md to note support for Fields using
`from_integer` (#4536)
chore: update docs with function names to match version 0.25.0
specifications (#4466)
feat: add specific error for attempting `string[x] = ".."`
(#4611)
fix(ssa): Use accurate type during SSA AsSlice simplficiation
(#4610)
END_COMMIT_OVERRIDE

---------

Co-authored-by: sirasistant <sirasistant@gmail.com>
  • Loading branch information
AztecBot and sirasistant committed Mar 25, 2024
1 parent 13eb71b commit 05c2eaa
Show file tree
Hide file tree
Showing 17 changed files with 124 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .aztec-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
208abbb63af4c9a3f25d723fe1c49e82aa461061
13a12d5255e788be94d575c726da141e652f14e3
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Resolves <!-- Link to GitHub Issue -->
Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR.

# PR Checklist\*

Expand Down
117 changes: 58 additions & 59 deletions aztec_macros/src/transforms/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,62 +232,62 @@ fn create_assert_initializer() -> Statement {
/// ```noir
/// #[aztec(private)]
/// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field {
/// // Create the hasher object
/// let mut hasher = Hasher::new();
/// // Create the bounded vec object
/// let mut serialized_args = BoundedVec::new();
///
/// // struct inputs call serialize on them to add an array of fields
/// hasher.add_multiple(structInput.serialize());
/// serialized_args.extend_from_array(structInput.serialize());
///
/// // Array inputs are iterated over and each element is added to the hasher (as a field)
/// // Array inputs are iterated over and each element is added to the bounded vec (as a field)
/// for i in 0..arrayInput.len() {
/// hasher.add(arrayInput[i] as Field);
/// serialized_args.push(arrayInput[i] as Field);
/// }
/// // Field inputs are added to the hasher
/// hasher.add({ident});
/// // Field inputs are added to the bounded vec
/// serialized_args.push({ident});
///
/// // Create the context
/// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context
/// let mut context = PrivateContext::new(inputs, hasher.hash());
/// let mut context = PrivateContext::new(inputs, hash_args(serialized_args));
/// }
/// ```
fn create_context(ty: &str, params: &[Param]) -> Result<Vec<Statement>, AztecMacroError> {
let mut injected_expressions: Vec<Statement> = vec![];

// `let mut hasher = Hasher::new();`
let let_hasher = mutable_assignment(
"hasher", // Assigned to
// `let mut serialized_args = BoundedVec::new();`
let let_serialized_args = mutable_assignment(
"serialized_args", // Assigned to
call(
variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path
vec![], // args
variable_path(chained_dep!("std", "collections", "bounded_vec", "BoundedVec", "new")), // Path
vec![], // args
),
);

// Completes: `let mut hasher = Hasher::new();`
injected_expressions.push(let_hasher);
// Completes: `let mut serialized_args = BoundedVec::new();`
injected_expressions.push(let_serialized_args);

// Iterate over each of the function parameters, adding to them to the hasher
// Iterate over each of the function parameters, adding to them to the bounded vec
for Param { pattern, typ, span, .. } in params {
match pattern {
Pattern::Identifier(identifier) => {
// Match the type to determine the padding to do
let unresolved_type = &typ.typ;
let expression = match unresolved_type {
// `hasher.add_multiple({ident}.serialize())`
UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier),
// `serialized_args.extend_from_array({ident}.serialize())`
UnresolvedTypeData::Named(..) => add_struct_to_serialized_args(identifier),
UnresolvedTypeData::Array(_, arr_type) => {
add_array_to_hasher(identifier, arr_type)
add_array_to_serialized_args(identifier, arr_type)
}
// `hasher.add({ident})`
UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier),
// Add the integer to the hasher, casted to a field
// `hasher.add({ident} as Field)`
// `serialized_args.push({ident})`
UnresolvedTypeData::FieldElement => add_field_to_serialized_args(identifier),
// Add the integer to the serialized args, casted to a field
// `serialized_args.push({ident} as Field)`
UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => {
add_cast_to_hasher(identifier)
add_cast_to_serialized_args(identifier)
}
UnresolvedTypeData::String(..) => {
let (var_bytes, id) = str_to_bytes(identifier);
injected_expressions.push(var_bytes);
add_array_to_hasher(
add_array_to_serialized_args(
&id,
&UnresolvedType {
typ: UnresolvedTypeData::Integer(
Expand All @@ -313,11 +313,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result<Vec<Statement>, AztecMac

// Create the inputs to the context
let inputs_expression = variable("inputs");
// `hasher.hash()`
let hash_call = method_call(
variable("hasher"), // variable
"hash", // method name
vec![], // args
// `hash_args(serialized_args)`
let hash_call = call(
variable_path(chained_dep!("aztec", "hash", "hash_args")), // variable
vec![variable("serialized_args")], // args
);

let path_snippet = ty.to_case(Case::Snake); // e.g. private_context
Expand Down Expand Up @@ -598,21 +597,21 @@ fn create_context_finish() -> Statement {
}

//
// Methods to create hasher inputs
// Methods to create hash_args inputs
//

fn add_struct_to_hasher(identifier: &Ident) -> Statement {
// If this is a struct, we call serialize and add the array to the hasher
fn add_struct_to_serialized_args(identifier: &Ident) -> Statement {
// If this is a struct, we call serialize and add the array to the serialized args
let serialized_call = method_call(
variable_path(path(identifier.clone())), // variable
"serialize", // method name
vec![], // args
);

make_statement(StatementKind::Semi(method_call(
variable("hasher"), // variable
"add_multiple", // method name
vec![serialized_call], // args
variable("serialized_args"), // variable
"extend_from_array", // method name
vec![serialized_call], // args
)))
}

Expand All @@ -632,7 +631,7 @@ fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) {
}

fn create_loop_over(var: Expression, loop_body: Vec<Statement>) -> Statement {
// If this is an array of primitive types (integers / fields) we can add them each to the hasher
// If this is an array of primitive types (integers / fields) we can add them each to the serialized args
// casted to a field
let span = var.span;

Expand All @@ -644,7 +643,7 @@ fn create_loop_over(var: Expression, loop_body: Vec<Statement>) -> Statement {
);

// What will be looped over
// - `hasher.add({ident}[i] as Field)`
// - `serialized_args.push({ident}[i] as Field)`
let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body)));

// `for i in 0..{ident}.len()`
Expand All @@ -662,66 +661,66 @@ fn create_loop_over(var: Expression, loop_body: Vec<Statement>) -> Statement {
}))
}

fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement {
// If this is an array of primitive types (integers / fields) we can add them each to the hasher
fn add_array_to_serialized_args(identifier: &Ident, arr_type: &UnresolvedType) -> Statement {
// If this is an array of primitive types (integers / fields) we can add them each to the serialized_args
// casted to a field

// Wrap in the semi thing - does that mean ended with semi colon?
// `hasher.add({ident}[i] as Field)`
// `serialized_args.push({ident}[i] as Field)`

let arr_index = index_array(identifier.clone(), "i");
let (add_expression, hasher_method_name) = match arr_type.typ {
let (add_expression, vec_method_name) = match arr_type.typ {
UnresolvedTypeData::Named(..) => {
let hasher_method_name = "add_multiple".to_owned();
let vec_method_name = "extend_from_array".to_owned();
let call = method_call(
// All serialize on each element
arr_index, // variable
"serialize", // method name
vec![], // args
);
(call, hasher_method_name)
(call, vec_method_name)
}
_ => {
let hasher_method_name = "add".to_owned();
let vec_method_name = "push".to_owned();
let call = cast(
arr_index, // lhs - `ident[i]`
UnresolvedTypeData::FieldElement, // cast to - `as Field`
);
(call, hasher_method_name)
(call, vec_method_name)
}
};

let block_statement = make_statement(StatementKind::Semi(method_call(
variable("hasher"), // variable
&hasher_method_name, // method name
variable("serialized_args"), // variable
&vec_method_name, // method name
vec![add_expression],
)));

create_loop_over(variable_ident(identifier.clone()), vec![block_statement])
}

fn add_field_to_hasher(identifier: &Ident) -> Statement {
// `hasher.add({ident})`
fn add_field_to_serialized_args(identifier: &Ident) -> Statement {
// `serialized_args.push({ident})`
let ident = variable_path(path(identifier.clone()));
make_statement(StatementKind::Semi(method_call(
variable("hasher"), // variable
"add", // method name
vec![ident], // args
variable("serialized_args"), // variable
"push", // method name
vec![ident], // args
)))
}

fn add_cast_to_hasher(identifier: &Ident) -> Statement {
// `hasher.add({ident} as Field)`
fn add_cast_to_serialized_args(identifier: &Ident) -> Statement {
// `serialized_args.push({ident} as Field)`
// `{ident} as Field`
let cast_operation = cast(
variable_path(path(identifier.clone())), // lhs
UnresolvedTypeData::FieldElement, // rhs
);

// `hasher.add({ident} as Field)`
// `serialized_args.push({ident} as Field)`
make_statement(StatementKind::Semi(method_call(
variable("hasher"), // variable
"add", // method name
vec![cast_operation], // args
variable("serialized_args"), // variable
"push", // method name
vec![cast_operation], // args
)))
}
9 changes: 5 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ pub(super) fn simplify_call(
}
}
Intrinsic::AsSlice => {
let slice = dfg.get_array_constant(arguments[0]);
if let Some((slice, element_type)) = slice {
let slice_length = dfg.make_constant(slice.len().into(), Type::length_type());
let new_slice = dfg.make_array(slice, element_type);
let array = dfg.get_array_constant(arguments[0]);
if let Some((array, array_type)) = array {
let slice_length = dfg.make_constant(array.len().into(), Type::length_type());
let inner_element_types = array_type.element_types();
let new_slice = dfg.make_array(array, Type::Slice(inner_element_types));
SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice])
} else {
SimplifyResult::None
Expand Down
7 changes: 7 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ impl Type {
Type::Reference(element) => element.contains_an_array(),
}
}

pub(crate) fn element_types(self) -> Rc<Vec<Type>> {
match self {
Type::Array(element_types, _) | Type::Slice(element_types) => element_types,
other => panic!("element_types: Expected array or slice, found {other}"),
}
}
}

/// Composite Types are essentially flattened struct or tuple types.
Expand Down
5 changes: 4 additions & 1 deletion compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ pub enum TypeCheckError {
method_name: String,
span: Span,
},
#[error("Strings do not support indexed assignment")]
StringIndexAssign { span: Span },
}

impl TypeCheckError {
Expand Down Expand Up @@ -237,7 +239,8 @@ impl From<TypeCheckError> for Diagnostic {
| TypeCheckError::ConstrainedReferenceToUnconstrained { span }
| TypeCheckError::UnconstrainedReferenceToConstrained { span }
| TypeCheckError::UnconstrainedSliceReturnToConstrained { span }
| TypeCheckError::NonConstantSliceLength { span } => {
| TypeCheckError::NonConstantSliceLength { span }
| TypeCheckError::StringIndexAssign { span } => {
Diagnostic::simple_error(error.to_string(), String::new(), span)
}
TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error(
Expand Down
5 changes: 5 additions & 0 deletions compiler/noirc_frontend/src/hir/type_check/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ impl<'interner> TypeChecker<'interner> {
Type::Array(_, elem_type) => *elem_type,
Type::Slice(elem_type) => *elem_type,
Type::Error => Type::Error,
Type::String(_) => {
let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue);
self.errors.push(TypeCheckError::StringIndexAssign { span: lvalue_span });
Type::Error
}
other => {
// TODO: Need a better span here
self.errors.push(TypeCheckError::TypeMismatch {
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/how_to/how-to-oracles.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo
```js
const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc

await noir.generateFinalProof(inputs, foreignCallHandler)
await noir.generateProof(inputs, foreignCallHandler)
```

As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/noir/concepts/data_types/integers.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The built-in structure `U128` allows you to use 128-bit unsigned integers almost
- You cannot cast between a native integer and `U128`
- There is a higher performance cost when using `U128`, compared to a native type.

Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions.
Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input.

```rust
fn main() {
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/tutorials/noirjs_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul
await setup(); // let's squeeze our wasm inits here

display('logs', 'Generating proof... ⌛');
const proof = await noir.generateFinalProof(input);
const proof = await noir.generateProof(input);
display('logs', 'Generating proof... ✅');
display('results', proof.proof);
```
Expand All @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th
```js
display('logs', 'Verifying proof... ⌛');
const verification = await noir.verifyFinalProof(proof);
const verification = await noir.verifyProof(proof);
if (verification) display('logs', 'Verifying proof... ✅');
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo
```js
const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc

await noir.generateFinalProof(inputs, foreignCallHandler)
await noir.generateProof(inputs, foreignCallHandler)
```

As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo.
Expand Down
4 changes: 2 additions & 2 deletions docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul
await setup(); // let's squeeze our wasm inits here

display('logs', 'Generating proof... ⌛');
const proof = await noir.generateFinalProof(input);
const proof = await noir.generateProof(input);
display('logs', 'Generating proof... ✅');
display('results', proof.proof);
```
Expand All @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th
```js
display('logs', 'Verifying proof... ⌛');
const verification = await noir.verifyFinalProof(proof);
const verification = await noir.verifyProof(proof);
if (verification) display('logs', 'Verifying proof... ✅');
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo
```js
const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc

await noir.generateFinalProof(inputs, foreignCallHandler)
await noir.generateProof(inputs, foreignCallHandler)
```

As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo.
Expand Down
Loading

0 comments on commit 05c2eaa

Please sign in to comment.