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(std_lib): println statements #630

Merged
merged 66 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
19bd86b
preliminary strings work, type added to Hir and abi
vezenovm Jan 6, 2023
4a2d6da
nargo build working for simple str types
vezenovm Jan 6, 2023
a627b02
initial strings work. can input to abi and compare string literal. st…
vezenovm Jan 9, 2023
c6f40b5
little cleanup and test addition
vezenovm Jan 9, 2023
b4f9e5c
cargo clippy and fixed decoding of public string values in toml
vezenovm Jan 9, 2023
fdc3058
Merge branch 'master' into mv/strings
vezenovm Jan 10, 2023
a1eaba3
some pr comments, moving to use str keyword
vezenovm Jan 11, 2023
8aeab6a
clarifying comment for string toml type to input value
vezenovm Jan 11, 2023
8780311
pr comments
vezenovm Jan 11, 2023
094cbd6
starting log work
vezenovm Jan 11, 2023
9df5934
update missing type from monomorphisation ast
vezenovm Jan 11, 2023
82af973
update display for string types
vezenovm Jan 11, 2023
9c8a8bd
Merge branch 'mv/strings' into mv/logging
vezenovm Jan 11, 2023
715873e
preliminary logging work, can log strings and arrays stored in idents…
vezenovm Jan 12, 2023
211f14d
switch to using Log directive
vezenovm Jan 12, 2023
92a6b69
merge conflicts
vezenovm Jan 17, 2023
f784f4a
merge conflicts, specifically with changes from to_radix PR, working …
vezenovm Jan 17, 2023
fce1c8f
switch to using builtin function for println rather than having a new…
vezenovm Jan 17, 2023
68ceb23
some missing cleanup to remove Log operation from SSA
vezenovm Jan 17, 2023
9828d63
merge conflicts with master
vezenovm Jan 17, 2023
279d266
fix convert type for new string type
vezenovm Jan 17, 2023
bf9371b
merge conflicts after higher order functions PR
vezenovm Jan 17, 2023
e5f3927
cargo fmt
vezenovm Jan 17, 2023
dcb3a0a
fix up some minor PR nitpicks
vezenovm Jan 17, 2023
3f7a7fc
decode method for string of field into abi string
vezenovm Jan 18, 2023
3dbee51
small cleanuo
vezenovm Jan 18, 2023
a1756b9
println for blackbox func output now working. need to still do some f…
vezenovm Jan 18, 2023
49546e7
little cleanup and additional comments
vezenovm Jan 18, 2023
deeabd3
parsing toml now follow abi
vezenovm Jan 19, 2023
c11a1bc
additional parser error and some comments
vezenovm Jan 19, 2023
7168c4b
merge conflicts
vezenovm Jan 19, 2023
755417f
fix usage of to_bytes with master merge
vezenovm Jan 19, 2023
31ded35
working string inputs inside struct inputs to main
vezenovm Jan 19, 2023
2715eb0
cargo clippy cleanup
vezenovm Jan 19, 2023
09998b9
remove unused err
vezenovm Jan 19, 2023
cf2eb44
additional test case for type directed toml parsing
vezenovm Jan 19, 2023
e56ffc5
merge conflicts after migration to acvm 0.3.0, still have to move aci…
vezenovm Jan 19, 2023
735d9d0
use remote acvm repo, do not merge as need to wait for acvm to be upd…
vezenovm Jan 20, 2023
eeae5c8
merge conflicts
vezenovm Jan 20, 2023
cccba89
update cargo lock
vezenovm Jan 20, 2023
d6e2270
merge conflicts
vezenovm Jan 20, 2023
07c02e5
Merge branch 'mv/strings' into mv/logging
vezenovm Jan 20, 2023
b6dab4a
merge conflicts after mv/strings merge to master
vezenovm Jan 25, 2023
11c1c61
clippy
vezenovm Jan 25, 2023
c04ee6b
more cargo clippy fixes
vezenovm Jan 25, 2023
672ab1c
Merge branch 'master' into mv/logging
TomAFrench Feb 8, 2023
bcdc617
chore: remove unwanted dependency to old acir crate
TomAFrench Feb 8, 2023
c4734f3
merge conflicts after acir gen refactor
vezenovm Feb 10, 2023
6bd75bb
remove old comments
vezenovm Feb 10, 2023
f4848cb
merge conflicts
vezenovm Feb 10, 2023
75d9b07
add nocapture flag to test and start println output on new line
vezenovm Feb 10, 2023
55bdf57
merge conflicts
vezenovm Feb 13, 2023
5fd266b
use refactored SsaContext methods
vezenovm Feb 13, 2023
a91b501
switc to show-logs over nocapture flag for printing test output
vezenovm Feb 13, 2023
8d90110
cargo clippy changes
vezenovm Feb 13, 2023
39375e0
merge conflicts
vezenovm Feb 13, 2023
468e06a
remove comment
vezenovm Feb 13, 2023
5dde7a3
PR comments
vezenovm Feb 13, 2023
4f347b3
some more pr comments, prepend 0x to hex strings and removed prepende…
vezenovm Feb 13, 2023
31d56cd
format_field_str method so that we have even number of digits in hex str
vezenovm Feb 13, 2023
d61f422
added comment to format_field_string
vezenovm Feb 13, 2023
ee14c9e
cargo clippy
vezenovm Feb 14, 2023
82596e9
AcirMem merge conflict
vezenovm Feb 14, 2023
b78c6bf
Merge branch 'master' into mv/logging
vezenovm Feb 14, 2023
0a93b7f
bring back cached_witness() method on InternalVar
vezenovm Feb 14, 2023
e77c938
cargo fmt
vezenovm Feb 14, 2023
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
5 changes: 5 additions & 0 deletions crates/nargo/tests/test_data/strings/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.1"

[dependencies]
2 changes: 2 additions & 0 deletions crates/nargo/tests/test_data/strings/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
message = "hello world"
y = 5
21 changes: 21 additions & 0 deletions crates/nargo/tests/test_data/strings/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use dep::std;

fn main(message : pub str[11], y : Field) {
let mut bad_message = "hello world";

constrain message == "hello world";
bad_message = "helld world";
let x = 2;
log(x);

// Currently broken
// let hash = std::hash::pedersen([x])[0];
// log(bad_message);
log("five");

let array = [1, 2, 3, 5, 8];
constrain y != 5;
log(array);

constrain message != bad_message;
}
5 changes: 5 additions & 0 deletions crates/noirc_abi/src/input_parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::AbiType;
pub enum InputValue {
Field(FieldElement),
Vec(Vec<FieldElement>),
String(String),
Struct(BTreeMap<String, InputValue>),
Undefined,
}
Expand All @@ -38,6 +39,10 @@ impl InputValue {
.all(|field_element| Self::Field(*field_element).matches_abi(typ))
}

(InputValue::String(string), AbiType::String { length }) => {
string.len() == *length as usize
}

(InputValue::Struct(map), AbiType::Struct { fields, .. }) => {
if map.len() != fields.len() {
return false;
Expand Down
16 changes: 11 additions & 5 deletions crates/noirc_abi/src/input_parser/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,14 @@ fn toml_map_to_field(
for (parameter, value) in toml_map {
let mapped_value = match value {
TomlTypes::String(string) => {
let new_value = parse_str(&string)?;

if let Some(field_element) = new_value {
let new_value = try_str_to_field(&string);

// We accept UTF-8 strings as input as well as hex strings representing field elements.
// We still want developers to be able to pass in hex strings for FieldElement inputs.
// Thus, we first try to parse the string into a field element, and if that fails we assume that the input is meant to be a plain string
if new_value.is_err() {
InputValue::String(string)
} else if let Ok(Some(field_element)) = new_value {
InputValue::Field(field_element)
} else {
InputValue::Undefined
Expand All @@ -84,7 +89,7 @@ fn toml_map_to_field(
TomlTypes::ArrayString(arr_str) => {
let array_elements: Vec<_> = arr_str
.into_iter()
.map(|elem_str| parse_str(&elem_str).unwrap().unwrap())
.map(|elem_str| try_str_to_field(&elem_str).unwrap().unwrap())
.collect();

InputValue::Vec(array_elements)
Expand Down Expand Up @@ -116,6 +121,7 @@ fn toml_remap(map: &BTreeMap<String, InputValue>) -> BTreeMap<String, TomlTypes>
let array = v.iter().map(|i| format!("0x{}", i.to_hex())).collect();
TomlTypes::ArrayString(array)
}
InputValue::String(s) => TomlTypes::String(s.clone()),
InputValue::Struct(map) => {
let map_with_toml_types = toml_remap(map);
TomlTypes::Table(map_with_toml_types)
Expand Down Expand Up @@ -145,7 +151,7 @@ enum TomlTypes {
Table(BTreeMap<String, TomlTypes>),
}

fn parse_str(value: &str) -> Result<Option<FieldElement>, InputParserError> {
fn try_str_to_field(value: &str) -> Result<Option<FieldElement>, InputParserError> {
if value.is_empty() {
Ok(None)
} else if value.starts_with("0x") {
Expand Down
31 changes: 30 additions & 1 deletion crates/noirc_abi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::BTreeMap, convert::TryInto};
use std::{collections::BTreeMap, convert::TryInto, str};

use acvm::FieldElement;
use errors::AbiError;
Expand Down Expand Up @@ -31,6 +31,7 @@ pub enum AbiType {
Array { length: u128, typ: Box<AbiType> },
Integer { sign: Sign, width: u32 },
Struct { fields: BTreeMap<String, AbiType> },
String { length: u128 },
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -63,6 +64,7 @@ impl AbiType {
AbiType::Field | AbiType::Integer { .. } => 1,
AbiType::Array { length, typ: _ } => *length as usize,
AbiType::Struct { fields, .. } => fields.len(),
AbiType::String { length } => *length as usize,
}
}

Expand All @@ -74,6 +76,7 @@ impl AbiType {
AbiType::Struct { fields, .. } => {
fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count())
}
AbiType::String { length } => *length as u32,
}
}
}
Expand Down Expand Up @@ -177,6 +180,13 @@ impl Abi {
match value {
InputValue::Field(elem) => encoded_value.push(elem),
InputValue::Vec(vec_elem) => encoded_value.extend(vec_elem),
InputValue::String(string) => {
let str_as_fields = string
.into_bytes()
.into_iter()
.map(|byte| FieldElement::from_be_bytes_reduce(&[byte]));
encoded_value.extend(str_as_fields)
}
InputValue::Struct(object) => {
for (field_name, value) in object {
let new_name = format!("{}.{}", param_name, field_name);
Expand Down Expand Up @@ -235,6 +245,24 @@ impl Abi {
index += *length as usize;
InputValue::Vec(field_elements.to_vec())
}
AbiType::String { length } => {
let field_elements = &encoded_inputs[index..index + (*length as usize)];

let string_as_slice = field_elements
.iter()
.map(|e| {
let mut field_as_bytes = e.to_bytes();
let char_byte = field_as_bytes.pop().unwrap(); // A character in a string is represented by a u8, thus we just want the last byte of the element
assert!(field_as_bytes.into_iter().all(|b| b == 0)); // Assert that the rest of the field element's bytes are empty
char_byte
})
.collect::<Vec<_>>();

let final_string = str::from_utf8(&string_as_slice).unwrap();

index += *length as usize;
InputValue::String(final_string.to_owned())
}
AbiType::Struct { fields, .. } => {
let mut struct_map = BTreeMap::new();

Expand Down Expand Up @@ -265,6 +293,7 @@ impl Serialize for Abi {
match param.typ {
AbiType::Field => map.serialize_entry(&param.name, "")?,
AbiType::Array { .. } => map.serialize_entry(&param.name, &vec)?,
AbiType::String { .. } => map.serialize_entry(&param.name, "")?,
AbiType::Integer { .. } => map.serialize_entry(&param.name, "")?,
AbiType::Struct { .. } => map.serialize_entry(&param.name, "")?,
};
Expand Down
11 changes: 11 additions & 0 deletions crates/noirc_evaluator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ impl Evaluator {
self.generate_struct_witnesses(&mut struct_witnesses, visibility, &new_fields)?;
igen.abi_struct(name, Some(def), fields, struct_witnesses);
}
AbiType::String { length } => {
let typ = AbiType::Integer { sign: noirc_abi::Sign::Unsigned, width: 8 };
let witnesses = self.generate_array_witnesses(visibility, length, &typ)?;
igen.abi_array(name, Some(def), &typ, *length, witnesses);
}
}
Ok(())
}
Expand Down Expand Up @@ -192,6 +197,12 @@ impl Evaluator {
}
self.generate_struct_witnesses(struct_witnesses, visibility, &new_fields)?
}
AbiType::String { length } => {
let typ = AbiType::Integer { sign: noirc_abi::Sign::Unsigned, width: 8 };
let internal_str_witnesses =
self.generate_array_witnesses(visibility, length, &typ)?;
struct_witnesses.insert(name.clone(), internal_str_witnesses);
}
}
}
Ok(())
Expand Down
82 changes: 78 additions & 4 deletions crates/noirc_evaluator/src/ssa/acir_gen.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use super::mem::{ArrayId, MemArray, Memory};
use super::node::{BinaryOp, Instruction, ObjectType, Operation};
use super::node::{BinaryOp, Instruction, LogInfo, NodeId, ObjectType, Operation};
use acvm::acir::OPCODE;
use acvm::FieldElement;

use super::node::NodeId;

use num_traits::{One, Zero};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::ops::{Mul, Neg};
use std::{collections::HashMap, str};
//use crate::acir::native_types::{Arithmetic, Witness};
use crate::ssa::context::SsaContext;
use crate::ssa::node::Node;
Expand Down Expand Up @@ -210,6 +208,82 @@ impl Acir {
todo!("dynamic arrays are not implemented yet");
}
}

Operation::Log(log_id) => {
// TODO: switch these prints to string creation and make a Directive to use when solving the ACIR
match log_id {
LogInfo::Node(node_id) => match ctx.get_as_constant(*node_id) {
Some(field) => {
println!("{}", field)
}
None => unreachable!("array objects should not be marked as single values"),
},
LogInfo::Array(array_log) => {
let mem_array = &ctx.mem[array_log.array_id];
let mut field_elements = Vec::new();
for idx in 0..mem_array.len {
let absolute_adr = mem_array.absolute_adr(idx);
if self.memory_map.contains_key(&absolute_adr) {
let array_elem_expr =
self.memory_map[&absolute_adr].expression.clone();
if array_elem_expr.is_const() {
field_elements.push(array_elem_expr.q_c)
} else {
unreachable!("array elements being logging must be const");
}
} else {
// TODO add handling for if key not found
unreachable!(
"array element being logging does not exist in memory"
);
}
}
if array_log.is_string {
// TODO: make a decode_string_value method in noirc_abi and use it here this is reused code
let string_as_slice = field_elements
.iter()
.map(|e| {
let mut field_as_bytes = e.to_bytes();
let char_byte = field_as_bytes.pop().unwrap(); // A character in a string is represented by a u8, thus we just want the last byte of the element
assert!(field_as_bytes.into_iter().all(|b| b == 0)); // Assert that the rest of the field element's bytes are empty
char_byte
})
.collect::<Vec<_>>();

let final_string = str::from_utf8(&string_as_slice).unwrap();
println!("{}", final_string);
} else {
print!("[");
let mut iter = field_elements.iter().peekable();
while let Some(elem) = iter.next() {
if iter.peek().is_none() {
print!("{}", elem);
} else {
print!("{}, ", elem)
}
}
println!("]");
}
// for idx in 0..mem_array.len {
// let absolute_adr = mem_array.absolute_adr(idx);
// if self.memory_map.contains_key(&absolute_adr) {
// let array_elem_expr = self.memory_map[&absolute_adr].expression.clone();
// if array_elem_expr.is_const() {
// let field_element = array_elem_expr.q_c;
// if array_log.is_string {

// } else {
// print!("{}, ",field_element)
// }
// }
// // dbg!(self.memory_map[&absolute_adr].expression.to_string());
// }
// }
}
}
//we do not generate constraint, so no output.
InternalVar::default()
}
};
output.id = Some(ins.id);
self.arith_cache.insert(ins.id, output);
Expand Down
56 changes: 55 additions & 1 deletion crates/noirc_evaluator/src/ssa/code_gen.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::context::SsaContext;
use super::function::FuncIndex;
use super::mem::ArrayId;
use super::node::{Binary, BinaryOp, NodeId, ObjectType, Operation, Variable};
use super::node::{ArrayLog, Binary, BinaryOp, LogInfo, NodeId, ObjectType, Operation, Variable};
use super::{block, node, ssa_form};
use std::collections::{BTreeMap, HashMap};
use std::convert::TryInto;
Expand Down Expand Up @@ -158,6 +158,9 @@ impl IRGenerator {
noirc_abi::AbiType::Struct { .. } => {
unreachable!("array of structs are not supported for now")
}
noirc_abi::AbiType::String { .. } => {
unreachable!("array of strings are not supported for now")
}
}
}

Expand Down Expand Up @@ -515,6 +518,28 @@ impl IRGenerator {
}
Ok(Value::Single(new_var))
}
Expression::Literal(Literal::Str(string)) => {
let string_as_integers = string
.bytes()
.into_iter()
.map(|byte| {
let f = FieldElement::from_be_bytes_reduce(&[byte]);
Expression::Literal(Literal::Integer(
f,
Type::Integer(noirc_frontend::Signedness::Unsigned, 8),
))
})
.collect::<Vec<_>>();

let string_arr_literal = ArrayLiteral {
contents: string_as_integers,
element_type: Type::Integer(noirc_frontend::Signedness::Unsigned, 8),
};

let new_value = self
.codegen_expression(&Expression::Literal(Literal::Array(string_arr_literal)))?;
Ok(new_value)
}
Expression::Ident(ident) => {
Ok(self.codegen_identifier(ident))
//n.b this creates a new variable if it does not exist, may be we should delegate this to explicit statements (let) - TODO
Expand Down Expand Up @@ -602,9 +627,38 @@ impl IRGenerator {
self.codegen_expression(expr.as_ref())?;
Ok(Value::dummy())
}
Expression::Log(expr) => {
// dbg!(expr.as_ref());
self.codegen_log(expr.as_ref())
}
}
}

fn codegen_log(&mut self, expression: &Expression) -> Result<Value, RuntimeError> {
let is_string = match expression {
Expression::Ident(ident) => match ident.typ {
Type::String(_) => true,
_ => false,
},
Expression::Literal(literal) => match literal {
Literal::Str(_) => true,
_ => false,
},
_ => unreachable!("logging this expression type is not supported"),
};

let node_id = self.codegen_expression(expression)?.unwrap_id();
let obj_type = self.context.get_object_type(node_id);
let operation = match obj_type {
ObjectType::Pointer(array_id) => {
Operation::Log(LogInfo::Array(ArrayLog { array_id, is_string }))
}
_ => Operation::Log(LogInfo::Node(node_id)),
};
self.context.new_instruction(operation, ObjectType::NotAnObject)?;
Ok(Value::dummy())
}

fn codegen_lowlevel(&mut self, call: &CallLowLevel) -> Result<NodeId, RuntimeError> {
match OPCODE::lookup(&call.opcode) {
Some(func) => self.call_low_level(func, call),
Expand Down
Loading