diff --git a/compiler/src/console/assert.rs b/compiler/src/console/assert.rs new file mode 100644 index 0000000000..b313d40f2b --- /dev/null +++ b/compiler/src/console/assert.rs @@ -0,0 +1,43 @@ +//! Enforces an assert equals statement in a compiled Leo program. + +use crate::{errors::ConsoleError, evaluate_eq, program::ConstrainedProgram, value::ConstrainedValue, GroupType}; +use leo_typed::{Expression, Span, Type}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean}, +}; + +impl> ConstrainedProgram { + pub fn evaluate_console_assert>( + &mut self, + cs: &mut CS, + file_scope: String, + function_scope: String, + expression: Expression, + span: Span, + ) -> Result<(), ConsoleError> { + let expected_type = Some(Type::Boolean); + let expression_string = expression.to_string(); + + // Evaluate assert expression + let assert_expression = self.enforce_expression(cs, file_scope, function_scope, expected_type, expression)?; + + // Expect assert expression to evaluate to true + let expect_true = ConstrainedValue::Boolean(Boolean::Constant(true)); + let result = evaluate_eq(cs, expect_true, assert_expression, span.clone())?; + + // Unwrap assertion value and handle errors + let result_option = match result { + ConstrainedValue::Boolean(boolean) => boolean.get_value(), + _ => unreachable!("evaluate_eq must return boolean"), + }; + let result_bool = result_option.ok_or(ConsoleError::assertion_depends_on_input(span.clone()))?; + + if !result_bool { + Err(ConsoleError::assertion_failed(expression_string, span)) + } else { + Ok(()) + } + } +} diff --git a/compiler/src/console/console.rs b/compiler/src/console/console.rs new file mode 100644 index 0000000000..5f4134f520 --- /dev/null +++ b/compiler/src/console/console.rs @@ -0,0 +1,42 @@ +//! Evaluates a macro in a compiled Leo program. + +use crate::{errors::ConsoleError, program::ConstrainedProgram, GroupType}; +use leo_typed::{ConsoleFunction, ConsoleFunctionCall}; + +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::r1cs::ConstraintSystem, +}; + +impl> ConstrainedProgram { + pub fn evaluate_console_function_call>( + &mut self, + cs: &mut CS, + file_scope: String, + function_scope: String, + console: ConsoleFunctionCall, + ) -> Result<(), ConsoleError> { + match console.function { + ConsoleFunction::Assert(expression) => { + self.evaluate_console_assert(cs, file_scope, function_scope, expression, console.span)?; + } + ConsoleFunction::Debug(string) => { + let string = self.format(cs, file_scope, function_scope, string)?; + + log::debug!("{}", string); + } + ConsoleFunction::Error(string) => { + let string = self.format(cs, file_scope, function_scope, string)?; + + log::error!("{}", string); + } + ConsoleFunction::Log(string) => { + let string = self.format(cs, file_scope, function_scope, string)?; + + println!("{}", string); + } + } + + Ok(()) + } +} diff --git a/compiler/src/macro_/format.rs b/compiler/src/console/format.rs similarity index 90% rename from compiler/src/macro_/format.rs rename to compiler/src/console/format.rs index 6065bdedf5..c53185e3b5 100644 --- a/compiler/src/macro_/format.rs +++ b/compiler/src/console/format.rs @@ -1,6 +1,6 @@ //! Evaluates a formatted string in a compiled Leo program. -use crate::{errors::MacroError, program::ConstrainedProgram, GroupType}; +use crate::{errors::ConsoleError, program::ConstrainedProgram, GroupType}; use leo_typed::FormattedString; use snarkos_models::{ @@ -15,10 +15,10 @@ impl> ConstrainedProgram { file_scope: String, function_scope: String, formatted: FormattedString, - ) -> Result { + ) -> Result { // Check that containers and parameters match if formatted.containers.len() != formatted.parameters.len() { - return Err(MacroError::length( + return Err(ConsoleError::length( formatted.containers.len(), formatted.parameters.len(), formatted.span.clone(), diff --git a/compiler/src/console/mod.rs b/compiler/src/console/mod.rs new file mode 100644 index 0000000000..3ed19d6067 --- /dev/null +++ b/compiler/src/console/mod.rs @@ -0,0 +1,8 @@ +pub mod assert; +pub use assert::*; + +pub mod console; +pub use self::console::*; + +pub mod format; +pub use self::format::*; diff --git a/compiler/src/errors/macro_.rs b/compiler/src/errors/console.rs similarity index 50% rename from compiler/src/errors/macro_.rs rename to compiler/src/errors/console.rs index 83c25fe76e..b363f6f49f 100644 --- a/compiler/src/errors/macro_.rs +++ b/compiler/src/errors/console.rs @@ -4,7 +4,7 @@ use leo_typed::{Error as FormattedError, Span}; use std::path::PathBuf; #[derive(Debug, Error)] -pub enum MacroError { +pub enum ConsoleError { #[error("{}", _0)] Error(#[from] FormattedError), @@ -12,16 +12,16 @@ pub enum MacroError { Expression(#[from] ExpressionError), } -impl MacroError { +impl ConsoleError { pub fn set_path(&mut self, path: PathBuf) { match self { - MacroError::Expression(error) => error.set_path(path), - MacroError::Error(error) => error.set_path(path), + ConsoleError::Expression(error) => error.set_path(path), + ConsoleError::Error(error) => error.set_path(path), } } fn new_from_span(message: String, span: Span) -> Self { - MacroError::Error(FormattedError::new_from_span(message, span)) + ConsoleError::Error(FormattedError::new_from_span(message, span)) } pub fn length(containers: usize, parameters: usize, span: Span) -> Self { @@ -32,4 +32,16 @@ impl MacroError { Self::new_from_span(message, span) } + + pub fn assertion_depends_on_input(span: Span) -> Self { + let message = format!("console.assert() failed to evaluate. This error is caused by empty input file values"); + + Self::new_from_span(message, span) + } + + pub fn assertion_failed(expression: String, span: Span) -> Self { + let message = format!("Assertion `true == {}` failed", expression); + + Self::new_from_span(message, span) + } } diff --git a/compiler/src/errors/mod.rs b/compiler/src/errors/mod.rs index 96ec87bdf4..884c6971b8 100644 --- a/compiler/src/errors/mod.rs +++ b/compiler/src/errors/mod.rs @@ -10,8 +10,8 @@ pub use self::function::*; pub mod import; pub use self::import::*; -pub mod macro_; -pub use self::macro_::*; +pub mod console; +pub use self::console::*; pub mod output_file; pub use self::output_file::*; diff --git a/compiler/src/errors/statement.rs b/compiler/src/errors/statement.rs index b6e4c3d81e..a0deeee7e4 100644 --- a/compiler/src/errors/statement.rs +++ b/compiler/src/errors/statement.rs @@ -1,4 +1,4 @@ -use crate::errors::{AddressError, BooleanError, ExpressionError, IntegerError, MacroError, ValueError}; +use crate::errors::{AddressError, BooleanError, ConsoleError, ExpressionError, IntegerError, ValueError}; use leo_typed::{Error as FormattedError, Span, Type}; use std::path::PathBuf; @@ -21,7 +21,7 @@ pub enum StatementError { IntegerError(#[from] IntegerError), #[error("{}", _0)] - MacroError(#[from] MacroError), + MacroError(#[from] ConsoleError), #[error("{}", _0)] ValueError(#[from] ValueError), @@ -62,12 +62,6 @@ impl StatementError { Self::new_from_span(message, span) } - pub fn assertion_failed(left: String, right: String, span: Span) -> Self { - let message = format!("Assertion `{} == {}` failed", left, right); - - Self::new_from_span(message, span) - } - pub fn conditional_boolean(actual: String, span: Span) -> Self { let message = format!("If, else conditional must resolve to a boolean, found `{}`", actual); diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 1b0cd3a006..61a54ac0ae 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -5,6 +5,9 @@ extern crate thiserror; pub mod compiler; +pub mod console; +pub use self::console::*; + pub mod constraints; pub use self::constraints::*; @@ -21,9 +24,6 @@ pub use self::function::*; pub mod import; pub use self::import::*; -pub mod macro_; -pub use self::macro_::*; - pub mod output; pub use self::output::*; diff --git a/compiler/src/macro_/macro_.rs b/compiler/src/macro_/macro_.rs deleted file mode 100644 index 84e406b802..0000000000 --- a/compiler/src/macro_/macro_.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Evaluates a macro in a compiled Leo program. - -use crate::{errors::MacroError, program::ConstrainedProgram, GroupType}; -use leo_typed::{FormattedMacro, MacroName}; - -use snarkos_models::{ - curves::{Field, PrimeField}, - gadgets::r1cs::ConstraintSystem, -}; - -impl> ConstrainedProgram { - pub fn evaluate_macro>( - &mut self, - cs: &mut CS, - file_scope: String, - function_scope: String, - macro_: FormattedMacro, - ) -> Result<(), MacroError> { - let string = macro_ - .string - .map(|string| self.format(cs, file_scope, function_scope, string)) - .unwrap_or(Ok("".to_string()))?; - - match macro_.name { - MacroName::Debug(_) => log::debug!("{}", string), - MacroName::Error(_) => log::error!("{}", string), - MacroName::Print(_) => println!("{}", string), - } - - Ok(()) - } -} diff --git a/compiler/src/statement/assert/assert_eq.rs b/compiler/src/statement/assert/assert_eq.rs deleted file mode 100644 index b7633e2289..0000000000 --- a/compiler/src/statement/assert/assert_eq.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Enforces an assert equals statement in a compiled Leo program. - -use crate::{errors::StatementError, program::ConstrainedProgram, value::ConstrainedValue, GroupType}; -use leo_typed::Span; - -use snarkos_models::{ - curves::{Field, PrimeField}, - gadgets::{ - r1cs::ConstraintSystem, - utilities::{boolean::Boolean, eq::ConditionalEqGadget}, - }, -}; - -impl> ConstrainedProgram { - pub fn enforce_assert_eq_statement>( - &mut self, - cs: &mut CS, - indicator: Option, - left: &ConstrainedValue, - right: &ConstrainedValue, - span: Span, - ) -> Result<(), StatementError> { - let condition = indicator.unwrap_or(Boolean::Constant(true)); - let name_unique = format!("assert {} == {} {}:{}", left, right, span.line, span.start); - let result = left.conditional_enforce_equal(cs.ns(|| name_unique), right, &condition); - - Ok(result.map_err(|_| StatementError::assertion_failed(left.to_string(), right.to_string(), span))?) - } -} diff --git a/compiler/src/statement/assert/mod.rs b/compiler/src/statement/assert/mod.rs deleted file mode 100644 index 7315daf7d8..0000000000 --- a/compiler/src/statement/assert/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Methods to enforce constraints on assert statements in a Leo program. - -pub mod assert_eq; -pub use self::assert_eq::*; diff --git a/compiler/src/statement/mod.rs b/compiler/src/statement/mod.rs index 48e762a01a..4e50633f64 100644 --- a/compiler/src/statement/mod.rs +++ b/compiler/src/statement/mod.rs @@ -1,8 +1,5 @@ //! Methods to enforce constraints on statements in a Leo program. -pub mod assert; -pub use self::assert::*; - pub mod assign; pub use self::assign::*; diff --git a/compiler/src/statement/statement.rs b/compiler/src/statement/statement.rs index e7f73a5d49..d1c95a7f21 100644 --- a/compiler/src/statement/statement.rs +++ b/compiler/src/statement/statement.rs @@ -77,14 +77,8 @@ impl> ConstrainedProgram { results.append(&mut result); } - Statement::Assert(left, right, span) => { - let (resolved_left, resolved_right) = - self.enforce_binary_expression(cs, file_scope, function_scope, None, left, right, span.clone())?; - - self.enforce_assert_eq_statement(cs, indicator, &resolved_left, &resolved_right, span)?; - } - Statement::Console(macro_) => { - self.evaluate_macro(cs, file_scope, function_scope, macro_)?; + Statement::Console(console) => { + self.evaluate_console_function_call(cs, file_scope, function_scope, console)?; } Statement::Expression(expression, span) => { let expression_string = expression.to_string();