Merge pull request #163 from AleoHQ/feature/unary-minus

Implement unary minus operator to negate a value, variable, or expression
This commit is contained in:
Howard Wu 2020-08-07 15:27:05 -07:00 committed by GitHub
commit 9e5b986b6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 457 additions and 92 deletions

View File

@ -6,11 +6,11 @@ use crate::{
ArrayInlineExpression,
CircuitInlineExpression,
Expression,
NotExpression,
PostfixExpression,
TernaryExpression,
UnaryExpression,
},
operations::{BinaryOperation, NotOperation},
operations::{BinaryOperation, UnaryOperation},
values::Value,
};
@ -76,17 +76,18 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
Rule::expression_conditional => {
Expression::Ternary(TernaryExpression::from_pest(&mut pair.into_inner()).unwrap())
}
Rule::expression_not => {
Rule::expression_unary => {
// The following is necessary to match with the unary operator and its unary expression
let span = next.as_span();
let mut inner = next.into_inner();
let operation = match inner.next().unwrap().as_rule() {
Rule::operation_not => {
NotOperation::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
Rule::operation_unary => {
UnaryOperation::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap()
}
rule => unreachable!("`expression_not` should yield `operation_pre_not`, found {:#?}", rule),
rule => unreachable!("`expression_unary` should yield `operation_unary`, found {:#?}", rule),
};
let expression = parse_term(inner.next().unwrap());
Expression::Not(NotExpression {
Expression::Unary(UnaryExpression {
operation,
expression,
span,
@ -95,21 +96,8 @@ fn parse_term(pair: Pair<Rule>) -> Box<Expression> {
Rule::expression_postfix => {
Expression::Postfix(PostfixExpression::from_pest(&mut pair.into_inner()).unwrap())
}
Rule::expression_primitive => {
let next = next.into_inner().next().unwrap();
match next.as_rule() {
Rule::value => Expression::Value(
Value::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
),
Rule::identifier => Expression::Identifier(
Identifier::from_pest(&mut pair.into_inner().next().unwrap().into_inner()).unwrap(),
),
rule => unreachable!(
"`expression_primitive` should contain one of [`value`, `identifier`], found {:#?}",
rule
),
}
}
Rule::value => Expression::Value(Value::from_pest(&mut pair.into_inner()).unwrap()),
Rule::identifier => Expression::Identifier(Identifier::from_pest(&mut pair.into_inner()).unwrap()),
rule => unreachable!(
"`term` should contain one of ['value', 'identifier', 'expression', 'expression_not', 'expression_increment', 'expression_decrement'], found {:#?}",
rule

View File

@ -8,7 +8,7 @@ use std::fmt;
pub enum Expression<'ast> {
Value(Value<'ast>),
Identifier(Identifier<'ast>),
Not(NotExpression<'ast>),
Unary(UnaryExpression<'ast>),
Binary(BinaryExpression<'ast>),
Ternary(TernaryExpression<'ast>),
ArrayInline(ArrayInlineExpression<'ast>),
@ -50,7 +50,7 @@ impl<'ast> Expression<'ast> {
match self {
Expression::Value(expression) => &expression.span(),
Expression::Identifier(expression) => &expression.span,
Expression::Not(expression) => &expression.span,
Expression::Unary(expression) => &expression.span,
Expression::Binary(expression) => &expression.span,
Expression::Ternary(expression) => &expression.span,
Expression::ArrayInline(expression) => &expression.span,
@ -66,7 +66,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
match *self {
Expression::Value(ref expression) => write!(f, "{}", expression),
Expression::Identifier(ref expression) => write!(f, "{}", expression),
Expression::Not(ref expression) => write!(f, "!{}", expression.expression),
Expression::Unary(ref expression) => write!(f, "{}", expression),
Expression::Binary(ref expression) => write!(f, "{} == {}", expression.left, expression.right),
Expression::Ternary(ref expression) => write!(
f,

View File

@ -13,8 +13,8 @@ pub use circuit_inline_expression::*;
pub mod expression;
pub use expression::*;
pub mod not_expression;
pub use not_expression::*;
pub mod unary_expression;
pub use unary_expression::*;
pub mod postfix_expression;
pub use postfix_expression::*;

View File

@ -1,15 +0,0 @@
use crate::{ast::Rule, expressions::Expression, operations::NotOperation, SpanDef};
use pest::Span;
use pest_ast::FromPest;
use serde::Serialize;
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
#[pest_ast(rule(Rule::expression_not))]
pub struct NotExpression<'ast> {
pub operation: NotOperation<'ast>,
pub expression: Box<Expression<'ast>>,
#[pest_ast(outer())]
#[serde(with = "SpanDef")]
pub span: Span<'ast>,
}

View File

@ -0,0 +1,22 @@
use crate::{ast::Rule, expressions::Expression, operations::UnaryOperation, SpanDef};
use pest::Span;
use pest_ast::FromPest;
use serde::Serialize;
use std::fmt;
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
#[pest_ast(rule(Rule::expression_unary))]
pub struct UnaryExpression<'ast> {
pub operation: UnaryOperation,
pub expression: Box<Expression<'ast>>,
#[pest_ast(outer())]
#[serde(with = "SpanDef")]
pub span: Span<'ast>,
}
impl<'ast> fmt::Display for UnaryExpression<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.operation, self.expression)
}
}

View File

@ -68,8 +68,13 @@ let_ = { "let " }
/// Operations
// Declared in operations/not_operation.rs
// Declared in operations/unary_operation.rs
operation_unary = {
operation_not
| operation_negate
}
operation_not = { "!" }
operation_negate = { "-" }
// Declared in operations/binary_operation.rs
operation_and = { "&&" }
@ -263,11 +268,12 @@ expression_term = {
| expression_array_inline
| expression_circuit_inline
| expression_conditional
| expression_not
| value
| expression_unary // must be defined below value to avoid conflicts with negative values
| expression_postfix
| expression_primitive
| identifier
}
expression_primitive = { value | identifier }
expression_tuple = _{ (expression ~ ("," ~ expression)*)? }
// Declared in expressions/expression.rs
@ -284,8 +290,8 @@ inline_array_inner = _{(spread_or_expression ~ ("," ~ NEWLINE* ~ spread_or_expre
expression_circuit_inline = { identifier ~ "{" ~ NEWLINE* ~ circuit_field_list ~ NEWLINE* ~ "}" }
circuit_field_list = _{ (circuit_field ~ ("," ~ NEWLINE* ~ circuit_field)*)? ~ ","? }
// Declared in expressions/not_expression.rs
expression_not = { operation_not ~ expression_term }
// Declared in expressions/unary_expression.rs
expression_unary = { operation_unary ~ expression_term }
// Declared in expressions/postfix_expression.rs
expression_postfix = { identifier ~ access+ }

View File

@ -4,5 +4,5 @@ pub use assign_operation::*;
pub mod binary_operation;
pub use binary_operation::*;
pub mod not_operation;
pub use not_operation::*;
pub mod unary_operation;
pub use unary_operation::*;

View File

@ -1,13 +0,0 @@
use crate::{ast::Rule, SpanDef};
use pest::Span;
use pest_ast::FromPest;
use serde::Serialize;
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
#[pest_ast(rule(Rule::operation_not))]
pub struct NotOperation<'ast> {
#[pest_ast(outer())]
#[serde(with = "SpanDef")]
pub span: Span<'ast>,
}

View File

@ -0,0 +1,29 @@
use crate::ast::Rule;
use pest_ast::FromPest;
use serde::Serialize;
use std::fmt;
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
#[pest_ast(rule(Rule::operation_unary))]
pub enum UnaryOperation {
Negate(Negate),
Not(Not),
}
impl fmt::Display for UnaryOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UnaryOperation::Negate(_) => write!(f, "-"),
UnaryOperation::Not(_) => write!(f, "!"),
}
}
}
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
#[pest_ast(rule(Rule::operation_not))]
pub struct Not {}
#[derive(Clone, Debug, FromPest, PartialEq, Serialize)]
#[pest_ast(rule(Rule::operation_negate))]
pub struct Negate {}

View File

@ -20,7 +20,13 @@ impl FieldError {
FieldError::Error(FormattedError::new_from_span(message, span))
}
pub fn cannot_enforce(operation: String, error: SynthesisError, span: Span) -> Self {
pub fn negate_operation(error: SynthesisError, span: Span) -> Self {
let message = format!("field negation failed due to synthesis error `{}`", error,);
Self::new_from_span(message, span)
}
pub fn binary_operation(operation: String, error: SynthesisError, span: Span) -> Self {
let message = format!(
"the field binary operation `{}` failed due to synthesis error `{}`",
operation, error,

View File

@ -20,7 +20,13 @@ impl GroupError {
GroupError::Error(FormattedError::new_from_span(message, span))
}
pub fn cannot_enforce(operation: String, error: SynthesisError, span: Span) -> Self {
pub fn negate_operation(error: SynthesisError, span: Span) -> Self {
let message = format!("group negation failed due to the synthesis error `{}`", error,);
Self::new_from_span(message, span)
}
pub fn binary_operation(operation: String, error: SynthesisError, span: Span) -> Self {
let message = format!(
"the group binary operation `{}` failed due to the synthesis error `{}`",
operation, error,

View File

@ -51,7 +51,13 @@ impl IntegerError {
Self::new_from_span(message, span)
}
pub fn cannot_evaluate(operation: String, span: Span) -> Self {
pub fn negate_operation(span: Span) -> Self {
let message = format!("integer negation can only be enforced on signed integers");
Self::new_from_span(message, span)
}
pub fn binary_operation(operation: String, span: Span) -> Self {
let message = format!(
"the integer binary operation `{}` can only be enforced on integers of the same type",
operation

View File

@ -6,6 +6,9 @@ pub use self::add::*;
pub mod sub;
pub use self::sub::*;
pub mod negate;
pub use self::negate::*;
pub mod mul;
pub use self::mul::*;

View File

@ -0,0 +1,22 @@
//! Enforces a unary negate `-` operator in a resolved Leo program.
use crate::{errors::ExpressionError, value::ConstrainedValue, GroupType};
use leo_typed::Span;
use snarkos_models::{
curves::{Field, PrimeField},
gadgets::r1cs::ConstraintSystem,
};
pub fn enforce_negate<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>>(
cs: &mut CS,
value: ConstrainedValue<F, G>,
span: Span,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match value {
ConstrainedValue::Integer(integer) => Ok(ConstrainedValue::Integer(integer.negate(cs, span)?)),
ConstrainedValue::Field(field) => Ok(ConstrainedValue::Field(field.negate(cs, span)?)),
ConstrainedValue::Group(group) => Ok(ConstrainedValue::Group(group.negate(cs, span)?)),
value => Err(ExpressionError::incompatible_types(format!("-{}", value), span)),
}
}

View File

@ -45,6 +45,12 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
}
// Binary operations
Expression::Negate(expression, span) => {
let resolved_value =
self.enforce_expression(cs, file_scope, function_scope, expected_types, *expression)?;
enforce_negate(cs, resolved_value, span)
}
Expression::Add(left, right, span) => {
let (resolved_left, resolved_right) = self.enforce_binary_expression(
cs,

View File

@ -43,6 +43,17 @@ impl<F: Field + PrimeField> FieldType<F> {
Ok(FieldType::Constant(value))
}
pub fn negate<CS: ConstraintSystem<F>>(&self, cs: CS, span: Span) -> Result<Self, FieldError> {
match self {
FieldType::Constant(field) => Ok(FieldType::Constant(field.neg())),
FieldType::Allocated(field) => {
let result = field.negate(cs).map_err(|e| FieldError::negate_operation(e, span))?;
Ok(FieldType::Allocated(result))
}
}
}
pub fn add<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, FieldError> {
match (self, other) {
(FieldType::Constant(self_value), FieldType::Constant(other_value)) => {
@ -52,7 +63,7 @@ impl<F: Field + PrimeField> FieldType<F> {
(FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => {
let result = self_value
.add(cs, other_value)
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span))?;
.map_err(|e| FieldError::binary_operation(format!("+"), e, span))?;
Ok(FieldType::Allocated(result))
}
@ -61,7 +72,7 @@ impl<F: Field + PrimeField> FieldType<F> {
| (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok(FieldType::Allocated(
allocated_value
.add_constant(cs, constant_value)
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span))?,
.map_err(|e| FieldError::binary_operation(format!("+"), e, span))?,
)),
}
}
@ -75,7 +86,7 @@ impl<F: Field + PrimeField> FieldType<F> {
(FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => {
let result = self_value
.sub(cs, other_value)
.map_err(|e| FieldError::cannot_enforce(format!("-"), e, span))?;
.map_err(|e| FieldError::binary_operation(format!("-"), e, span))?;
Ok(FieldType::Allocated(result))
}
@ -84,7 +95,7 @@ impl<F: Field + PrimeField> FieldType<F> {
| (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok(FieldType::Allocated(
allocated_value
.sub_constant(cs, constant_value)
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span))?,
.map_err(|e| FieldError::binary_operation(format!("+"), e, span))?,
)),
}
}
@ -98,7 +109,7 @@ impl<F: Field + PrimeField> FieldType<F> {
(FieldType::Allocated(self_value), FieldType::Allocated(other_value)) => {
let result = self_value
.mul(cs, other_value)
.map_err(|e| FieldError::cannot_enforce(format!("*"), e, span))?;
.map_err(|e| FieldError::binary_operation(format!("*"), e, span))?;
Ok(FieldType::Allocated(result))
}
@ -107,7 +118,7 @@ impl<F: Field + PrimeField> FieldType<F> {
| (FieldType::Allocated(allocated_value), FieldType::Constant(constant_value)) => Ok(FieldType::Allocated(
allocated_value
.mul_by_constant(cs, constant_value)
.map_err(|e| FieldError::cannot_enforce(format!("*"), e, span))?,
.map_err(|e| FieldError::binary_operation(format!("*"), e, span))?,
)),
}
}
@ -124,7 +135,7 @@ impl<F: Field + PrimeField> FieldType<F> {
FieldType::Allocated(allocated) => {
let allocated_inverse = allocated
.inverse(&mut cs)
.map_err(|e| FieldError::cannot_enforce(format!("+"), e, span.clone()))?;
.map_err(|e| FieldError::binary_operation(format!("+"), e, span.clone()))?;
FieldType::Allocated(allocated_inverse)
}

View File

@ -36,6 +36,8 @@ pub trait GroupType<F: Field>:
fn to_allocated<CS: ConstraintSystem<F>>(&self, cs: CS, span: Span) -> Result<Self, GroupError>;
fn negate<CS: ConstraintSystem<F>>(&self, cs: CS, span: Span) -> Result<Self, GroupError>;
fn add<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, GroupError>;
fn sub<CS: ConstraintSystem<F>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, GroupError>;

View File

@ -23,7 +23,11 @@ use snarkos_models::{
},
},
};
use std::{borrow::Borrow, ops::Sub, str::FromStr};
use std::{
borrow::Borrow,
ops::{Neg, Sub},
str::FromStr,
};
#[derive(Clone, Debug)]
pub enum EdwardsGroupType {
@ -50,6 +54,18 @@ impl GroupType<Fq> for EdwardsGroupType {
.map_err(|error| GroupError::synthesis_error(error, span))
}
fn negate<CS: ConstraintSystem<Fq>>(&self, cs: CS, span: Span) -> Result<Self, GroupError> {
match self {
EdwardsGroupType::Constant(group) => Ok(EdwardsGroupType::Constant(group.neg())),
EdwardsGroupType::Allocated(group) => {
let result = <EdwardsBlsGadget as GroupGadget<GroupAffine<EdwardsParameters>, Fq>>::negate(group, cs)
.map_err(|e| GroupError::negate_operation(e, span))?;
Ok(EdwardsGroupType::Allocated(result))
}
}
}
fn add<CS: ConstraintSystem<Fq>>(&self, cs: CS, other: &Self, span: Span) -> Result<Self, GroupError> {
match (self, other) {
(EdwardsGroupType::Constant(self_value), EdwardsGroupType::Constant(other_value)) => {
@ -62,7 +78,7 @@ impl GroupType<Fq> for EdwardsGroupType {
cs,
other_value,
)
.map_err(|e| GroupError::cannot_enforce(format!("+"), e, span))?;
.map_err(|e| GroupError::binary_operation(format!("+"), e, span))?;
Ok(EdwardsGroupType::Allocated(result))
}
@ -72,7 +88,7 @@ impl GroupType<Fq> for EdwardsGroupType {
Ok(EdwardsGroupType::Allocated(
allocated_value
.add_constant(cs, constant_value)
.map_err(|e| GroupError::cannot_enforce(format!("+"), e, span))?,
.map_err(|e| GroupError::binary_operation(format!("+"), e, span))?,
))
}
}
@ -90,7 +106,7 @@ impl GroupType<Fq> for EdwardsGroupType {
cs,
other_value,
)
.map_err(|e| GroupError::cannot_enforce(format!("-"), e, span))?;
.map_err(|e| GroupError::binary_operation(format!("-"), e, span))?;
Ok(EdwardsGroupType::Allocated(result))
}
@ -100,7 +116,7 @@ impl GroupType<Fq> for EdwardsGroupType {
Ok(EdwardsGroupType::Allocated(
allocated_value
.sub_constant(cs, constant_value)
.map_err(|e| GroupError::cannot_enforce(format!("-"), e, span))?,
.map_err(|e| GroupError::binary_operation(format!("-"), e, span))?,
))
}
}

View File

@ -347,6 +347,21 @@ impl Integer {
Self::allocate_type(cs, integer_type, name, option, span)
}
pub fn negate<F: Field + PrimeField, CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
span: Span,
) -> Result<Self, IntegerError> {
let unique_namespace = format!("enforce -{} {}:{}", self, span.line, span.start);
let a = self;
let s = span.clone();
let result = match_signed_integer!(a, s => a.neg(cs.ns(|| unique_namespace)));
result.ok_or(IntegerError::negate_operation(span))
}
pub fn add<F: Field + PrimeField, CS: ConstraintSystem<F>>(
self,
cs: &mut CS,
@ -361,7 +376,7 @@ impl Integer {
let result = match_integers_span!((a, b), s => a.add(cs.ns(|| unique_namespace), &b));
result.ok_or(IntegerError::cannot_evaluate(format!("+"), span))
result.ok_or(IntegerError::binary_operation(format!("+"), span))
}
pub fn sub<F: Field + PrimeField, CS: ConstraintSystem<F>>(
@ -378,7 +393,7 @@ impl Integer {
let result = match_integers_span!((a, b), s => a.sub(cs.ns(|| unique_namespace), &b));
result.ok_or(IntegerError::cannot_evaluate(format!("-"), span))
result.ok_or(IntegerError::binary_operation(format!("-"), span))
}
pub fn mul<F: Field + PrimeField, CS: ConstraintSystem<F>>(
@ -395,7 +410,7 @@ impl Integer {
let result = match_integers_span!((a, b), s => a.mul(cs.ns(|| unique_namespace), &b));
result.ok_or(IntegerError::cannot_evaluate(format!("*"), span))
result.ok_or(IntegerError::binary_operation(format!("*"), span))
}
pub fn div<F: Field + PrimeField, CS: ConstraintSystem<F>>(
@ -412,7 +427,7 @@ impl Integer {
let result = match_integers_span!((a, b), s => a.div(cs.ns(|| unique_namespace), &b));
result.ok_or(IntegerError::cannot_evaluate(format!("÷"), span))
result.ok_or(IntegerError::binary_operation(format!("÷"), span))
}
pub fn pow<F: Field + PrimeField, CS: ConstraintSystem<F>>(
@ -429,7 +444,7 @@ impl Integer {
let result = match_integers_span!((a, b), s => a.pow(cs.ns(|| unique_namespace), &b));
result.ok_or(IntegerError::cannot_evaluate(format!("**"), span))
result.ok_or(IntegerError::binary_operation(format!("**"), span))
}
}

View File

@ -49,6 +49,21 @@ macro_rules! match_integer {
};
}
#[macro_export]
macro_rules! match_signed_integer {
($integer: ident, $span: ident => $expression: expr) => {
match $integer {
Integer::I8($integer) => Some(Integer::I8($expression.map_err(|e| IntegerError::signed(e, $span))?)),
Integer::I16($integer) => Some(Integer::I16($expression.map_err(|e| IntegerError::signed(e, $span))?)),
Integer::I32($integer) => Some(Integer::I32($expression.map_err(|e| IntegerError::signed(e, $span))?)),
Integer::I64($integer) => Some(Integer::I64($expression.map_err(|e| IntegerError::signed(e, $span))?)),
Integer::I128($integer) => Some(Integer::I128($expression.map_err(|e| IntegerError::signed(e, $span))?)),
_ => None,
}
};
}
#[macro_export]
macro_rules! match_integers {
(($a: ident, $b: ident) => $expression:expr) => {

View File

@ -23,6 +23,33 @@ pub fn field_to_decimal_string(f: Fq) -> String {
f_bigint.to_str_radix(10)
}
#[test]
fn test_negate() {
use std::ops::Neg;
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
for _ in 0..10 {
let a: Fq = rng.gen();
let b = a.neg();
let a_string = field_to_decimal_string(a);
let b_string = field_to_decimal_string(b);
let bytes = include_bytes!("negate.leo");
let mut program = parse_program(bytes).unwrap();
let main_input = generate_main_input(vec![
("a", Some(InputValue::Field(a_string))),
("b", Some(InputValue::Field(b_string))),
]);
program.set_main_input(main_input);
assert_satisfied(program)
}
}
#[test]
fn test_add() {
use std::ops::Add;

View File

@ -0,0 +1,3 @@
function main(a: field, b: field) {
assert_eq!(-a, b);
}

View File

@ -59,6 +59,32 @@ fn test_input() {
expect_synthesis_error(program);
}
#[test]
fn test_negate() {
use std::ops::Neg;
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
for _ in 0..10 {
let a: EdwardsAffine = rng.gen();
let b = a.neg();
let a_string = group_to_decimal_string(a);
let b_string = group_to_decimal_string(b);
let bytes = include_bytes!("negate.leo");
let mut program = parse_program(bytes).unwrap();
let main_input = generate_main_input(vec![
("a", Some(InputValue::Group(a_string))),
("b", Some(InputValue::Group(b_string))),
]);
program.set_main_input(main_input);
assert_satisfied(program)
}
}
#[test]
fn test_add() {
use std::ops::Add;

View File

@ -0,0 +1,3 @@
function main(a: group, b: group) {
assert_eq!(-a, b);
}

View File

@ -30,6 +30,21 @@ fn test_i128_max_fail() {
Testi128::test_max_fail();
}
#[test]
fn test_i128_neg() {
Testi128::test_negate();
}
#[test]
fn test_i128_neg_max_fail() {
Testi128::test_negate_min_fail();
}
#[test]
fn test_i128_neg_zero() {
Testi128::test_negate_zero();
}
#[test]
fn test_i128_add() {
Testi128::test_add();

View File

@ -0,0 +1,3 @@
function main(a: i128, b: i128) {
assert_eq!(-a, b);
}

View File

@ -0,0 +1,4 @@
function main() {
let a: i128 = -170141183460469231731687303715884105728;
let b = -a;
}

View File

@ -0,0 +1,5 @@
function main() {
let a = 0i128;
assert_eq!(-a, 0i128);
}

View File

@ -30,6 +30,21 @@ fn test_i16_max_fail() {
Testi16::test_max_fail();
}
#[test]
fn test_i16_neg() {
Testi16::test_negate();
}
#[test]
fn test_i16_neg_max_fail() {
Testi16::test_negate_min_fail();
}
#[test]
fn test_i16_neg_zero() {
Testi16::test_negate_zero();
}
#[test]
fn test_i16_add() {
Testi16::test_add();

View File

@ -0,0 +1,3 @@
function main(a: i16, b: i16) {
assert_eq!(-a, b);
}

View File

@ -0,0 +1,4 @@
function main() {
let a = -32768i16;
let b = -a;
}

View File

@ -0,0 +1,5 @@
function main() {
let a = 0i16;
assert_eq!(-a, 0i16);
}

View File

@ -30,6 +30,21 @@ fn test_i32_max_fail() {
Testi32::test_max_fail();
}
#[test]
fn test_i32_neg() {
Testi32::test_negate();
}
#[test]
fn test_i32_neg_max_fail() {
Testi32::test_negate_min_fail();
}
#[test]
fn test_i32_neg_zero() {
Testi32::test_negate_zero();
}
#[test]
fn test_i32_add() {
Testi32::test_add();

View File

@ -0,0 +1,3 @@
function main(a: i32, b: i32) {
assert_eq!(-a, b);
}

View File

@ -0,0 +1,4 @@
function main() {
let a = -2147483648i32;
let b = -a;
}

View File

@ -0,0 +1,5 @@
function main() {
let a = 0i32;
assert_eq!(-a, 0i32);
}

View File

@ -30,6 +30,21 @@ fn test_i64_max_fail() {
Testi64::test_max_fail();
}
#[test]
fn test_i64_neg() {
Testi64::test_negate();
}
#[test]
fn test_i64_neg_max_fail() {
Testi64::test_negate_min_fail();
}
#[test]
fn test_i64_neg_zero() {
Testi64::test_negate_zero();
}
#[test]
fn test_i64_add() {
Testi64::test_add();

View File

@ -0,0 +1,3 @@
function main(a: i64, b: i64) {
assert_eq!(-a, b);
}

View File

@ -0,0 +1,4 @@
function main() {
let a: i64 = -9223372036854775808;
let b = -a;
}

View File

@ -0,0 +1,5 @@
function main() {
let a = 0i64;
assert_eq!(-a, 0i64);
}

View File

@ -30,6 +30,21 @@ fn test_i8_max_fail() {
Testi8::test_max_fail();
}
#[test]
fn test_i8_neg() {
Testi8::test_negate();
}
#[test]
fn test_i8_neg_max_fail() {
Testi8::test_negate_min_fail();
}
#[test]
fn test_i8_neg_zero() {
Testi8::test_negate_zero();
}
#[test]
fn test_i8_add() {
Testi8::test_add();

View File

@ -0,0 +1,3 @@
function main(a: i8, b: i8) {
assert_eq!(-a, b);
}

View File

@ -0,0 +1,4 @@
function main() {
let a = -128i8;
let b = -a;
}

View File

@ -0,0 +1,5 @@
function main() {
let a = 0i8;
assert_eq!(-a, 0i8);
}

View File

@ -2,6 +2,44 @@ macro_rules! test_int {
($name: ident, $type_: ty, $integer_type: expr, $gadget: ty) => {
pub struct $name {}
impl $name {
fn test_negate() {
for _ in 0..10 {
let a: $type_ = rand::random();
let b = match a.checked_neg() {
Some(valid) => valid,
None => continue,
};
let bytes = include_bytes!("negate.leo");
let mut program = parse_program(bytes).unwrap();
let main_input = generate_main_input(vec![
("a", Some(InputValue::Integer($integer_type, a.to_string()))),
("b", Some(InputValue::Integer($integer_type, b.to_string()))),
]);
program.set_main_input(main_input);
assert_satisfied(program);
}
}
fn test_negate_min_fail() {
let bytes = include_bytes!("negate_min.leo");
let program = parse_program(bytes).unwrap();
expect_computation_error(program);
}
fn test_negate_zero() {
let bytes = include_bytes!("negate_zero.leo");
let program = parse_program(bytes).unwrap();
assert_satisfied(program);
}
}
impl IntegerTester for $name {
fn test_min() {
let bytes = include_bytes!("min.leo");

View File

@ -8,11 +8,11 @@ use leo_ast::{
BinaryExpression,
CircuitInlineExpression,
Expression as AstExpression,
NotExpression,
PostfixExpression,
TernaryExpression,
UnaryExpression,
},
operations::BinaryOperation,
operations::{BinaryOperation, UnaryOperation},
values::{
AddressValue,
BooleanValue,
@ -52,6 +52,7 @@ pub enum Expression {
// Boolean operations
Not(Box<Expression>, Span),
Negate(Box<Expression>, Span),
Or(Box<Expression>, Box<Expression>, Span),
And(Box<Expression>, Box<Expression>, Span),
Eq(Box<Expression>, Box<Expression>, Span),
@ -139,6 +140,7 @@ impl<'ast> fmt::Display for Expression {
Expression::Integer(ref type_, ref integer, ref _span) => write!(f, "{}{}", integer, type_),
// Number operations
Expression::Negate(ref expression, ref _span) => write!(f, "-{}", expression),
Expression::Add(ref left, ref right, ref _span) => write!(f, "{} + {}", left, right),
Expression::Sub(ref left, ref right, ref _span) => write!(f, "{} - {}", left, right),
Expression::Mul(ref left, ref right, ref _span) => write!(f, "{} * {}", left, right),
@ -272,7 +274,7 @@ impl<'ast> From<AstExpression<'ast>> for Expression {
match expression {
AstExpression::Value(value) => Expression::from(value),
AstExpression::Identifier(variable) => Expression::from(variable),
AstExpression::Not(expression) => Expression::from(expression),
AstExpression::Unary(expression) => Expression::from(expression),
AstExpression::Binary(expression) => Expression::from(expression),
AstExpression::Ternary(expression) => Expression::from(expression),
AstExpression::ArrayInline(expression) => Expression::from(expression),
@ -426,12 +428,18 @@ impl<'ast> From<Value<'ast>> for Expression {
}
}
impl<'ast> From<NotExpression<'ast>> for Expression {
fn from(expression: NotExpression<'ast>) -> Self {
Expression::Not(
impl<'ast> From<UnaryExpression<'ast>> for Expression {
fn from(expression: UnaryExpression<'ast>) -> Self {
match expression.operation {
UnaryOperation::Not(_) => Expression::Not(
Box::new(Expression::from(*expression.expression)),
Span::from(expression.span),
)
),
UnaryOperation::Negate(_) => Expression::Negate(
Box::new(Expression::from(*expression.expression)),
Span::from(expression.span),
),
}
}
}