diff --git a/compiler/src/constraints/boolean.rs b/compiler/src/constraints/boolean.rs index f041cd1671..bdfae7f013 100644 --- a/compiler/src/constraints/boolean.rs +++ b/compiler/src/constraints/boolean.rs @@ -5,7 +5,7 @@ use crate::{ errors::BooleanError, GroupType, }; -use leo_types::InputValue; +use leo_types::{InputValue, Span}; use snarkos_errors::gadgets::SynthesisError; use snarkos_models::{ @@ -22,6 +22,7 @@ impl> ConstrainedProgram { cs: &mut CS, name: String, input_value: Option, + span: Span, ) -> Result, BooleanError> { // Check that the input value is the correct type let bool_value = match input_value { @@ -29,21 +30,29 @@ impl> ConstrainedProgram { if let InputValue::Boolean(bool) = input { Some(bool) } else { - return Err(BooleanError::SynthesisError(SynthesisError::AssignmentMissing)); + return Err(BooleanError::invalid_boolean(name, span)); } } None => None, }; - let number = Boolean::alloc(cs.ns(|| name), || bool_value.ok_or(SynthesisError::AssignmentMissing))?; + let boolean_name = format!("{}: bool", name); + let boolean_name_unique = format!("`{}` {}:{}", boolean_name, span.line, span.start); + let number = Boolean::alloc(cs.ns(|| boolean_name_unique), || { + bool_value.ok_or(SynthesisError::AssignmentMissing) + }) + .map_err(|_| BooleanError::missing_boolean(boolean_name, span))?; Ok(ConstrainedValue::Boolean(number)) } - pub(crate) fn evaluate_not(value: ConstrainedValue) -> Result, BooleanError> { + pub(crate) fn evaluate_not( + value: ConstrainedValue, + span: Span, + ) -> Result, BooleanError> { match value { ConstrainedValue::Boolean(boolean) => Ok(ConstrainedValue::Boolean(boolean.not())), - value => Err(BooleanError::CannotEvaluate(format!("!{}", value))), + value => Err(BooleanError::cannot_evaluate(format!("!{}", value), span)), } } @@ -52,15 +61,19 @@ impl> ConstrainedProgram { cs: &mut CS, left: ConstrainedValue, right: ConstrainedValue, + span: Span, ) -> Result, BooleanError> { match (left, right) { (ConstrainedValue::Boolean(left_bool), ConstrainedValue::Boolean(right_bool)) => { - Ok(ConstrainedValue::Boolean(Boolean::or(cs, &left_bool, &right_bool)?)) + Ok(ConstrainedValue::Boolean( + Boolean::or(cs, &left_bool, &right_bool) + .map_err(|e| BooleanError::cannot_enforce(format!("||"), e, span))?, + )) } - (left_value, right_value) => Err(BooleanError::CannotEnforce(format!( - "{} || {}", - left_value, right_value - ))), + (left_value, right_value) => Err(BooleanError::cannot_evaluate( + format!("{} || {}", left_value, right_value), + span, + )), } } @@ -69,15 +82,19 @@ impl> ConstrainedProgram { cs: &mut CS, left: ConstrainedValue, right: ConstrainedValue, + span: Span, ) -> Result, BooleanError> { match (left, right) { (ConstrainedValue::Boolean(left_bool), ConstrainedValue::Boolean(right_bool)) => { - Ok(ConstrainedValue::Boolean(Boolean::and(cs, &left_bool, &right_bool)?)) + Ok(ConstrainedValue::Boolean( + Boolean::and(cs, &left_bool, &right_bool) + .map_err(|e| BooleanError::cannot_enforce(format!("&&"), e, span))?, + )) } - (left_value, right_value) => Err(BooleanError::CannotEnforce(format!( - "{} && {}", - left_value, right_value - ))), + (left_value, right_value) => Err(BooleanError::cannot_evaluate( + format!("{} && {}", left_value, right_value), + span, + )), } } } diff --git a/compiler/src/constraints/expression.rs b/compiler/src/constraints/expression.rs index 46c2a9ca03..e954dd4272 100644 --- a/compiler/src/constraints/expression.rs +++ b/compiler/src/constraints/expression.rs @@ -939,13 +939,10 @@ impl> ConstrainedProgram { } // Boolean operations - Expression::Not(expression) => Ok(Self::evaluate_not(self.enforce_expression( - cs, - file_scope, - function_scope, - expected_types, - *expression, - )?)?), + Expression::Not(expression, span) => Ok(Self::evaluate_not( + self.enforce_expression(cs, file_scope, function_scope, expected_types, *expression)?, + span, + )?), Expression::Or(left, right, span) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( cs, @@ -957,7 +954,7 @@ impl> ConstrainedProgram { span.clone(), )?; - Ok(self.enforce_or(cs, resolved_left, resolved_right)?) + Ok(self.enforce_or(cs, resolved_left, resolved_right, span)?) } Expression::And(left, right, span) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( @@ -970,7 +967,7 @@ impl> ConstrainedProgram { span.clone(), )?; - Ok(self.enforce_and(cs, resolved_left, resolved_right)?) + Ok(self.enforce_and(cs, resolved_left, resolved_right, span)?) } Expression::Eq(left, right, span) => { let (resolved_left, resolved_right) = self.enforce_binary_expression( diff --git a/compiler/src/constraints/function.rs b/compiler/src/constraints/function.rs index b0fc164fab..92e2a22ed9 100644 --- a/compiler/src/constraints/function.rs +++ b/compiler/src/constraints/function.rs @@ -173,7 +173,7 @@ impl> ConstrainedProgram { )?)), Type::Field => Ok(field_from_input(cs, name, input_value, span)?), Type::Group => Ok(group_from_input(cs, name, input_value, span)?), - Type::Boolean => Ok(self.bool_from_input(cs, name, input_value)?), + Type::Boolean => Ok(self.bool_from_input(cs, name, input_value, span)?), Type::Array(_type, dimensions) => self.allocate_array(cs, name, *_type, dimensions, input_value, span), _ => unimplemented!("main function input not implemented for type"), } diff --git a/compiler/src/errors/constraints/boolean.rs b/compiler/src/errors/constraints/boolean.rs index 24e4ed0088..d3f9894e6f 100644 --- a/compiler/src/errors/constraints/boolean.rs +++ b/compiler/src/errors/constraints/boolean.rs @@ -1,9 +1,14 @@ +use leo_types::{Error as FormattedError, Span}; + use snarkos_errors::gadgets::SynthesisError; use std::str::ParseBoolError; #[derive(Debug, Error)] pub enum BooleanError { + #[error("{}", _0)] + Error(#[from] FormattedError), + #[error("Cannot evaluate {}", _0)] CannotEvaluate(String), @@ -16,3 +21,36 @@ pub enum BooleanError { #[error("{}", _0)] SynthesisError(#[from] SynthesisError), } + +impl BooleanError { + fn new_from_span(message: String, span: Span) -> Self { + BooleanError::Error(FormattedError::new_from_span(message, span)) + } + + pub fn cannot_enforce(operation: String, error: SynthesisError, span: Span) -> Self { + let message = format!( + "the boolean operation `{}` failed due to the synthesis error `{}`", + operation, error, + ); + + Self::new_from_span(message, span) + } + + pub fn cannot_evaluate(operation: String, span: Span) -> Self { + let message = format!("no implementation found for `{}`", operation); + + Self::new_from_span(message, span) + } + + pub fn invalid_boolean(actual: String, span: Span) -> Self { + let message = format!("expected boolean input type, found `{}`", actual); + + Self::new_from_span(message, span) + } + + pub fn missing_boolean(expected: String, span: Span) -> Self { + let message = format!("expected boolean input `{}` not found", expected); + + Self::new_from_span(message, span) + } +} diff --git a/compiler/tests/boolean/mod.rs b/compiler/tests/boolean/mod.rs index 87e236419a..fc849e6008 100644 --- a/compiler/tests/boolean/mod.rs +++ b/compiler/tests/boolean/mod.rs @@ -23,35 +23,19 @@ pub fn output_false(program: EdwardsTestCompiler) { output_expected_boolean(program, false) } -fn fail_evaluate(program: EdwardsTestCompiler) { - match get_error(program) { - CompilerError::FunctionError(FunctionError::StatementError(StatementError::ExpressionError( - ExpressionError::BooleanError(BooleanError::CannotEvaluate(_string)), - ))) => {} - error => panic!("Expected evaluate error, got {}", error), - } -} - -fn fail_enforce(program: EdwardsTestCompiler) { - match get_error(program) { - CompilerError::FunctionError(FunctionError::StatementError(StatementError::ExpressionError( - ExpressionError::BooleanError(BooleanError::CannotEnforce(_string)), - ))) => {} - error => panic!("Expected evaluate error, got {}", error), - } -} - fn fail_boolean(program: EdwardsTestCompiler) { match get_error(program) { - CompilerError::FunctionError(FunctionError::BooleanError(BooleanError::SynthesisError(_))) => {} - error => panic!("Expected invalid boolean error, got {}", error), + CompilerError::FunctionError(FunctionError::BooleanError(BooleanError::Error(_))) => {} + error => panic!("Expected boolean error, got {}", error), } } -fn fail_synthesis(program: EdwardsTestCompiler) { +fn fail_boolean_statement(program: EdwardsTestCompiler) { match get_error(program) { - CompilerError::FunctionError(FunctionError::BooleanError(BooleanError::SynthesisError(_string))) => {} - error => panic!("Expected synthesis error, got {}", error), + CompilerError::FunctionError(FunctionError::StatementError(StatementError::ExpressionError( + ExpressionError::BooleanError(BooleanError::Error(_)), + ))) => {} + _ => panic!("Expected boolean error, got {}"), } } @@ -88,7 +72,7 @@ fn test_input_bool_none() { program.set_inputs(vec![None]); - fail_synthesis(program); + fail_boolean(program); } // Boolean not ! @@ -114,7 +98,7 @@ fn test_not_u32() { let bytes = include_bytes!("not_u32.leo"); let program = parse_program(bytes).unwrap(); - fail_evaluate(program); + fail_boolean_statement(program) } // Boolean or || @@ -148,7 +132,7 @@ fn test_true_or_u32() { let bytes = include_bytes!("true_or_u32.leo"); let program = parse_program(bytes).unwrap(); - fail_enforce(program); + fail_boolean_statement(program); } // Boolean and && @@ -182,7 +166,7 @@ fn test_true_and_u32() { let bytes = include_bytes!("true_and_u32.leo"); let program = parse_program(bytes).unwrap(); - fail_enforce(program); + fail_boolean_statement(program); } // All diff --git a/types/src/expression.rs b/types/src/expression.rs index 9a4c4f3ccc..deabe8cf8d 100644 --- a/types/src/expression.rs +++ b/types/src/expression.rs @@ -41,7 +41,7 @@ pub enum Expression { Pow(Box, Box, Span), // Boolean operations - Not(Box), + Not(Box, Span), Or(Box, Box, Span), And(Box, Box, Span), Eq(Box, Box, Span), @@ -78,6 +78,7 @@ impl Expression { Expression::Div(_, _, old_span) => *old_span = new_span.clone(), Expression::Pow(_, _, old_span) => *old_span = new_span.clone(), + Expression::Not(_, old_span) => *old_span = new_span.clone(), Expression::Or(_, _, old_span) => *old_span = new_span.clone(), Expression::And(_, _, old_span) => *old_span = new_span.clone(), Expression::Eq(_, _, old_span) => *old_span = new_span.clone(), @@ -135,7 +136,7 @@ impl<'ast> fmt::Display for Expression { Expression::Pow(ref left, ref right, ref _span) => write!(f, "{} ** {}", left, right), // Boolean operations - Expression::Not(ref expression) => write!(f, "!{}", expression), + Expression::Not(ref expression, ref _span) => write!(f, "!{}", expression), Expression::Or(ref lhs, ref rhs, ref _span) => write!(f, "{} || {}", lhs, rhs), Expression::And(ref lhs, ref rhs, ref _span) => write!(f, "{} && {}", lhs, rhs), Expression::Eq(ref lhs, ref rhs, ref _span) => write!(f, "{} == {}", lhs, rhs), @@ -312,7 +313,10 @@ impl<'ast> From> for Expression { Box::new(Expression::from(*expression.right)), Span::from(expression.span), ), - BinaryOperation::Ne => Expression::Not(Box::new(Expression::from(expression))), + BinaryOperation::Ne => Expression::Not( + Box::new(Expression::from(expression.clone())), + Span::from(expression.span), + ), BinaryOperation::Ge => Expression::Ge( Box::new(Expression::from(*expression.left)), Box::new(Expression::from(*expression.right)), @@ -410,7 +414,10 @@ impl<'ast> From> for Expression { impl<'ast> From> for Expression { fn from(expression: NotExpression<'ast>) -> Self { - Expression::Not(Box::new(Expression::from(*expression.expression))) + Expression::Not( + Box::new(Expression::from(*expression.expression)), + Span::from(expression.span), + ) } }