Skip to content

Commit

Permalink
feat: Add is_none
Browse files Browse the repository at this point in the history
There was no way to assert that an option value is None, so I add
is_none which returns true if the value is None. Rust seems to have
decided against unwrap_none, so I will not go down that path.

rust-lang/rust#62633

assert!(is_none(x)) might produce slightly larger Simplicity expressions
than a hypothetical unwrap_none(x), but I will not try to prematurely
optimize any Simplicity code here. A future version of the Simfony
compiler might detect assert! + is_none and produce an optimized
Simplicity expression accordingly.
  • Loading branch information
uncomputable committed Jul 27, 2024
1 parent 7a17bab commit 9042a14
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ pub enum CallName {
UnwrapLeft(ResolvedType),
/// [`Either::unwrap_right`].
UnwrapRight(ResolvedType),
/// [`Option::is_none`].
IsNone(ResolvedType),
/// [`Option::unwrap`].
Unwrap,
/// [`assert`].
Expand Down Expand Up @@ -900,6 +902,22 @@ impl AbstractSyntaxTree for Call {
scope,
)?])
}
CallName::IsNone(some_ty) => {
if from.args.len() != 1 {
return Err(Error::InvalidNumberOfArguments(1, from.args.len()))
.with_span(from);
}
let out_ty = ResolvedType::boolean();
if ty != &out_ty {
return Err(Error::ExpressionTypeMismatch(ty.clone(), out_ty)).with_span(from);
}
let arg_ty = ResolvedType::option(some_ty);
Arc::from([Expression::analyze(
from.args.first().unwrap(),
&arg_ty,
scope,
)?])
}
CallName::Unwrap => {
let args_ty = ResolvedType::option(ty.clone());
if from.args.len() != 1 {
Expand Down Expand Up @@ -998,6 +1016,9 @@ impl AbstractSyntaxTree for CallName {
.resolve(left_ty)
.map(Self::UnwrapRight)
.with_span(from),
parse::CallName::IsNone(some_ty) => {
scope.resolve(some_ty).map(Self::IsNone).with_span(from)
}
parse::CallName::Unwrap => Ok(Self::Unwrap),
parse::CallName::Assert => Ok(Self::Assert),
parse::CallName::TypeCast(target) => {
Expand Down
5 changes: 5 additions & 0 deletions src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ impl Call {
let get_inner = ProgNode::assertr_take(fail_cmr, &ProgNode::iden());
ProgNode::comp(&right_and_unit, &get_inner).with_span(self)
}
CallName::IsNone(..) => {
let sum_and_unit = ProgNode::pair_unit(&args);
let is_right = ProgNode::case_true_false();
ProgNode::comp(&sum_and_unit, &is_right).with_span(self)
}
CallName::Assert => {
let jet = ProgNode::jet(Elements::Verify);
ProgNode::comp(&args, &jet).with_span(self)
Expand Down
3 changes: 2 additions & 1 deletion src/minimal.pest
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ false_expr = @{ "false" }
true_expr = @{ "true" }
unwrap_left = { "unwrap_left::<" ~ ty ~ ">" }
unwrap_right = { "unwrap_right::<" ~ ty ~ ">" }
is_none = { "is_none::<" ~ ty ~ ">" }
unwrap = @{ "unwrap" }
assert = @{ "assert!" }
type_cast = { "<" ~ ty ~ ">::into" }
call_name = { jet | unwrap_left | unwrap_right | unwrap | assert | type_cast | function_name }
call_name = { jet | unwrap_left | unwrap_right | is_none | unwrap | assert | type_cast | function_name }
call_args = { "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }
call_expr = { call_name ~ call_args }
unsigned_decimal = @{ (ASCII_DIGIT | "_")+ }
Expand Down
10 changes: 10 additions & 0 deletions src/named.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ pub trait CoreExt: CoreConstructible + Sized {
fn assertr_drop(cmr: Cmr, right: &Self) -> Self {
Self::assertr(cmr, &Self::drop_(right)).unwrap()
}

/// `case false true` always type-checks.
fn case_false_true() -> Self {
Self::case(&Self::bit_false(), &Self::bit_true()).unwrap()
}

/// `case true false` always type-checks.
fn case_true_false() -> Self {
Self::case(&Self::bit_true(), &Self::bit_false()).unwrap()
}
}

impl<N: CoreConstructible> CoreExt for N {}
Expand Down
6 changes: 6 additions & 0 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ pub enum CallName {
UnwrapRight(AliasedType),
/// [`Option::unwrap`].
Unwrap,
/// [`Option::is_none`].
IsNone(AliasedType),
/// [`assert`].
Assert,
/// Cast from the given source type.
Expand Down Expand Up @@ -816,6 +818,10 @@ impl PestParse for CallName {
let inner = pair.into_inner().next().unwrap();
AliasedType::parse(inner).map(Self::UnwrapRight)
}
Rule::is_none => {
let inner = pair.into_inner().next().unwrap();
AliasedType::parse(inner).map(Self::IsNone)
}
Rule::unwrap => Ok(Self::Unwrap),
Rule::assert => Ok(Self::Assert),
Rule::type_cast => {
Expand Down

0 comments on commit 9042a14

Please sign in to comment.