From 7fb413519227dd361ecc6aa3a8d55d1c2572cafe Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 21 Jun 2023 11:11:10 -0500 Subject: [PATCH 1/6] Add Option --- noir_stdlib/src/lib.nr | 1 + noir_stdlib/src/option.nr | 113 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 noir_stdlib/src/option.nr diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index b010eb31be3..11eabd835ec 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -12,6 +12,7 @@ mod ec; mod unsafe; mod collections; mod compat; +mod option; #[builtin(println)] fn println(_input : T) {} diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr new file mode 100644 index 00000000000..809c1c4a032 --- /dev/null +++ b/noir_stdlib/src/option.nr @@ -0,0 +1,113 @@ +struct Option { + _is_some: bool, + value: T, +} + +impl Option { + fn none() -> Self { + Self { _is_some: false, value: std::unsafe::zeroed() } + } + + fn some(value: T) -> Self { + Self { _is_some: true, value } + } + + fn is_none(self) -> bool { + !self._is_some + } + + fn is_some(self) -> bool { + self._is_some + } + + fn unwrap(self) -> T { + assert(self._is_some); + self.value + } + + fn unwrap_or(self, default: T) -> T { + if self._is_some { + self.value + } else { + default + } + } + + fn unwrap_or_else(self, default: fn() -> T) -> T { + if self._is_some { + self.value + } else { + default() + } + } + + fn map(self, f: fn(T) -> U) -> Option { + if self._is_some { + Option::some(f(self.value)) + } else { + Option::none() + } + } + + fn map_or(self, default: U, f: fn(T) -> U) -> U { + if self._is_some { + f(self.value) + } else { + default + } + } + + fn map_or_else(self, default: fn() -> U, f: fn(T) -> U) -> U { + if self._is_some { + f(self.value) + } else { + default() + } + } + + fn and(self, other: Self) -> Self { + if self.is_none() { + Option::none() + } else { + other + } + } + + fn and_then(self, f: fn(T) -> Option) -> Option { + if self._is_some { + f(self.value) + } else { + Option::none() + } + } + + fn or(self, other: Self) -> Self { + if self._is_some { + self + } else { + other + } + } + + fn or_else(self, default: fn() -> Option) -> Option { + if self._is_some { + self + } else { + default() + } + } + + fn xor(self, other: Self) -> Self { + if self._is_some { + if other._is_some { + Option::none() + } else { + self + } + } else if other._is_some { + other + } else { + Option::none() + } + } +} From c53538e35f549fb59d8b23f1839f3000d1175c46 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 21 Jun 2023 11:16:55 -0500 Subject: [PATCH 2/6] Fix path --- noir_stdlib/src/option.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index 809c1c4a032..95cf8564a21 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -5,7 +5,7 @@ struct Option { impl Option { fn none() -> Self { - Self { _is_some: false, value: std::unsafe::zeroed() } + Self { _is_some: false, value: crate::unsafe::zeroed() } } fn some(value: T) -> Self { From 3bb70fd08516077a95a3c8fd6481a4d2b714cd0a Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 21 Jun 2023 11:32:26 -0500 Subject: [PATCH 3/6] Add option test --- .../test_data_ssa_refactor/option/Nargo.toml | 5 ++ .../test_data_ssa_refactor/option/src/main.nr | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 crates/nargo_cli/tests/test_data_ssa_refactor/option/Nargo.toml create mode 100644 crates/nargo_cli/tests/test_data_ssa_refactor/option/src/main.nr diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/option/Nargo.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/option/Nargo.toml new file mode 100644 index 00000000000..5a02ffe4729 --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/option/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.7.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/option/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/option/src/main.nr new file mode 100644 index 00000000000..0a41b9a629c --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/option/src/main.nr @@ -0,0 +1,53 @@ +use dep::std::option::Option; + +fn main() { + let none = Option::none(); + let some = Option::some(3); + + assert(none.is_none()); + assert(some.is_some()); + + assert(some.unwrap() == 3); + + assert(none.unwrap_or(2) == 2); + assert(some.unwrap_or(2) == 3); + + assert(none.unwrap_or_else(|| 5) == 5); + assert(some.unwrap_or_else(|| 5) == 3); + + assert(none.map(|x| x * 2).is_none()); + assert(some.map(|x| x * 2).unwrap() == 6); + + assert(none.map_or(0, |x| x * 2) == 0); + assert(some.map_or(0, |x| x * 2) == 6); + + assert(none.map_or_else(|| 0, |x| x * 2) == 0); + assert(some.map_or_else(|| 0, |x| x * 2) == 6); + + assert(none.and(none).is_none()); + assert(none.and(some).is_none()); + assert(some.and(none).is_none()); + assert(some.and(some).is_some()); + + let add1_u64 = |value: Field| Option::some(value as u64 + 1); + + assert(none.and_then(|_value| Option::none()).is_none()); + assert(none.and_then(add1_u64).is_none()); + assert(some.and_then(|_value| Option::none()).is_none()); + assert(some.and_then(add1_u64).unwrap() == 4); + + assert(none.or(none).is_none()); + assert(none.or(some).is_some()); + assert(some.or(none).is_some()); + assert(some.or(some).is_some()); + + assert(none.or_else(|| Option::none()).is_none()); + assert(none.or_else(|| Option::some(5)).is_some()); + assert(some.or_else(|| Option::none()).is_some()); + assert(some.or_else(|| Option::some(5)).is_some()); + + assert(none.xor(none).is_none()); + assert(none.xor(some).is_some()); + assert(some.xor(none).is_some()); + assert(some.xor(some).is_none()); +} From 2be7ed28a52bd7a499e0d73a95d1608e85302472 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 31 Jul 2023 16:46:16 -0500 Subject: [PATCH 4/6] Move test --- .../tests/{test_data_ssa_refactor => test_data}/option/Nargo.toml | 0 .../{test_data_ssa_refactor => test_data}/option/src/main.nr | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename crates/nargo_cli/tests/{test_data_ssa_refactor => test_data}/option/Nargo.toml (100%) rename crates/nargo_cli/tests/{test_data_ssa_refactor => test_data}/option/src/main.nr (100%) diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/option/Nargo.toml b/crates/nargo_cli/tests/test_data/option/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/test_data_ssa_refactor/option/Nargo.toml rename to crates/nargo_cli/tests/test_data/option/Nargo.toml diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/option/src/main.nr b/crates/nargo_cli/tests/test_data/option/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/test_data_ssa_refactor/option/src/main.nr rename to crates/nargo_cli/tests/test_data/option/src/main.nr From cdee97812b68949443f2859e99ee93adbfa56ec5 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 1 Aug 2023 12:06:22 -0500 Subject: [PATCH 5/6] Add docs and filter, flatten methods --- .../tests/test_data/option/Nargo.toml | 3 +- .../src/ssa_refactor/acir_gen/mod.rs | 2 +- noir_stdlib/src/option.nr | 46 ++++++++++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/crates/nargo_cli/tests/test_data/option/Nargo.toml b/crates/nargo_cli/tests/test_data/option/Nargo.toml index 5a02ffe4729..2248e9c06dd 100644 --- a/crates/nargo_cli/tests/test_data/option/Nargo.toml +++ b/crates/nargo_cli/tests/test_data/option/Nargo.toml @@ -1,5 +1,6 @@ [package] +name = "option" authors = [""] compiler_version = "0.7.0" -[dependencies] \ No newline at end of file +[dependencies] diff --git a/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs index da8409431ce..77467c116d9 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs @@ -998,7 +998,7 @@ impl Context { } Intrinsic::ArrayLen => { let len = match self.convert_value(arguments[0], dfg) { - AcirValue::Var(_, _) => unreachable!("Non-array passed to array.len() method"), + AcirValue::Var(_, _) => unreachable!("Non-array passed to array.len() method"), AcirValue::Array(values) => (values.len() as u128).into(), AcirValue::DynamicArray(array) => (array.len as u128).into(), }; diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index 95cf8564a21..cdd115663ea 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -4,27 +4,33 @@ struct Option { } impl Option { + /// Constructs a None value fn none() -> Self { Self { _is_some: false, value: crate::unsafe::zeroed() } } + /// Constructs a Some wrapper around the given value fn some(value: T) -> Self { Self { _is_some: true, value } } + /// True if this Option is None fn is_none(self) -> bool { !self._is_some } + /// True if this Option is Some fn is_some(self) -> bool { self._is_some } + /// Asserts `self.is_some()` and returns the wrapped value. fn unwrap(self) -> T { assert(self._is_some); self.value } + /// Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. fn unwrap_or(self, default: T) -> T { if self._is_some { self.value @@ -33,6 +39,8 @@ impl Option { } } + /// Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return + /// a default value. fn unwrap_or_else(self, default: fn() -> T) -> T { if self._is_some { self.value @@ -41,6 +49,7 @@ impl Option { } } + /// If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. fn map(self, f: fn(T) -> U) -> Option { if self._is_some { Option::some(f(self.value)) @@ -49,6 +58,7 @@ impl Option { } } + /// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. fn map_or(self, default: U, f: fn(T) -> U) -> U { if self._is_some { f(self.value) @@ -57,6 +67,7 @@ impl Option { } } + /// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. fn map_or_else(self, default: fn() -> U, f: fn(T) -> U) -> U { if self._is_some { f(self.value) @@ -65,6 +76,7 @@ impl Option { } } + /// Returns None if self is None. Otherwise, this returns `other`. fn and(self, other: Self) -> Self { if self.is_none() { Option::none() @@ -73,6 +85,10 @@ impl Option { } } + /// If self is None, this returns None. Otherwise, this calls the given function + /// with the Some value contained within self, and returns the result of that call. + /// + /// In some languages this function is called `flat_map` or `bind`. fn and_then(self, f: fn(T) -> Option) -> Option { if self._is_some { f(self.value) @@ -81,6 +97,7 @@ impl Option { } } + /// If self is Some, return self. Otherwise, return `other`. fn or(self, other: Self) -> Self { if self._is_some { self @@ -89,7 +106,8 @@ impl Option { } } - fn or_else(self, default: fn() -> Option) -> Option { + /// If self is Some, return self. Otherwise, return `default()`. + fn or_else(self, default: fn() -> Self) -> Self { if self._is_some { self } else { @@ -97,6 +115,8 @@ impl Option { } } + // If only one of the two Options is Some, return that option. + // Otherwise, if both options are Some or both are None, None is returned. fn xor(self, other: Self) -> Self { if self._is_some { if other._is_some { @@ -110,4 +130,28 @@ impl Option { Option::none() } } + + /// Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. + /// Otherwise, this returns `None` + fn filter(self, predicate: fn(T) -> bool) -> Self { + if self._is_some { + if predicate(self.value) { + self + } else { + None + } + } else { + None + } + } + + /// Flattens an Option> into a Option. + /// This returns None if the outer Option is None. Otherwise, this returns the inner Option. + fn flatten(option: Option>) -> Option { + if option._is_some { + option.value + } else { + None + } + } } From 5cbfb9c4a06c8865c98ff2b594464b037d821a5c Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 1 Aug 2023 14:39:00 -0500 Subject: [PATCH 6/6] Fix stdlib --- noir_stdlib/src/option.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index cdd115663ea..5cc4dfae887 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -138,10 +138,10 @@ impl Option { if predicate(self.value) { self } else { - None + Option::none() } } else { - None + Option::none() } } @@ -151,7 +151,7 @@ impl Option { if option._is_some { option.value } else { - None + Option::none() } } }