diff --git a/asg/src/const_value.rs b/asg/src/const_value.rs index f093a65559..c0ddb90d07 100644 --- a/asg/src/const_value.rs +++ b/asg/src/const_value.rs @@ -91,6 +91,16 @@ pub enum GroupValue { Tuple(GroupCoordinate, GroupCoordinate), } +impl From for GroupValue { + fn from(other: leo_ast::GroupValue) -> Self { + use leo_ast::GroupValue::*; + match other { + Single(value, _) => GroupValue::Single(value), + Tuple(value) => GroupValue::Tuple(GroupCoordinate::from(&value.x), GroupCoordinate::from(&value.y)), + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum ConstValue { Int(ConstInt), diff --git a/ast/src/input/input.rs b/ast/src/input/input.rs index ae201c2aec..c37bb4b4f9 100644 --- a/ast/src/input/input.rs +++ b/ast/src/input/input.rs @@ -94,12 +94,18 @@ impl Input { Ok(()) } - /// Returns the main function input value with the given `name` + /// Returns the main function input value with the given `name`. #[allow(clippy::ptr_arg)] pub fn get(&self, name: &String) -> Option> { self.program_input.get(name) } + /// Returns the constant input value with the given `name`. + #[allow(clippy::ptr_arg)] + pub fn get_constant(&self, name: &String) -> Option> { + self.program_input.get_constant(name) + } + /// Returns the runtime register input values pub fn get_registers(&self) -> &Registers { self.program_input.get_registers() diff --git a/ast/src/input/macros.rs b/ast/src/input/macros.rs index d77655ecae..d2064ccbd7 100644 --- a/ast/src/input/macros.rs +++ b/ast/src/input/macros.rs @@ -14,11 +14,13 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . +/// Constructs an input section to store data parsed from a Leo input file. +/// Constructs sections that pass variables to the main function through the input keyword. #[macro_export] -macro_rules! input_section_impl { +macro_rules! record_input_section { ($($name: ident), *) => ($( - /// An input section declared in an input file with `[$name]` + /// An input section declared in an input file with `[$name]`. #[derive(Clone, PartialEq, Eq, Default)] pub struct $name { is_present: bool, @@ -63,10 +65,68 @@ macro_rules! input_section_impl { Ok(()) } - /// Returns this section's [IndexMap] of values + /// Returns this section's [IndexMap] of values. pub fn values(&self) -> IndexMap> { self.values.clone() } } )*) } + +/// Constructs an input section to store data parsed from a Leo input file. +/// Constructs sections that pass variables directly to the main function. +#[macro_export] +macro_rules! main_input_section { + ($($name: ident), *) => ($( + + /// `[$name]` program input section. + #[derive(Clone, PartialEq, Eq, Default)] + pub struct $name { + input: IndexMap>, + } + + #[allow(clippy::len_without_is_empty)] + impl $name { + pub fn new() -> Self { + Self::default() + } + + /// Returns an empty version of this struct with `None` values. + /// Called during constraint synthesis to provide private input variables. + pub fn empty(&self) -> Self { + let mut input = self.input.clone(); + + input.iter_mut().for_each(|(_name, value)| { + *value = None; + }); + + Self { input } + } + + pub fn len(&self) -> usize { + self.input.len() + } + + pub fn insert(&mut self, key: String, value: Option) { + self.input.insert(key, value); + } + + /// Parses main input definitions and stores them in `self`. + pub fn parse(&mut self, definitions: Vec) -> Result<(), InputParserError> { + for definition in definitions { + let name = definition.parameter.variable.value; + let value = InputValue::from_expression(definition.parameter.type_, definition.expression)?; + + self.insert(name, Some(value)); + } + + Ok(()) + } + + /// Returns an `Option` of the main function input at `name`. + pub fn get(&self, name: &str) -> Option> { + self.input.get(name).cloned() + } + } + )*) +} diff --git a/ast/src/input/program_input/constant_input.rs b/ast/src/input/program_input/constant_input.rs new file mode 100644 index 0000000000..a55d25ea3e --- /dev/null +++ b/ast/src/input/program_input/constant_input.rs @@ -0,0 +1,22 @@ +// Copyright (C) 2019-2021 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::InputValue; +use leo_input::{definitions::Definition, InputParserError}; + +use indexmap::IndexMap; + +main_input_section!(ConstantInput); diff --git a/ast/src/input/program_input/main_input.rs b/ast/src/input/program_input/main_input.rs index 7d1b496134..894b846c75 100644 --- a/ast/src/input/program_input/main_input.rs +++ b/ast/src/input/program_input/main_input.rs @@ -19,51 +19,4 @@ use leo_input::{definitions::Definition, InputParserError}; use indexmap::IndexMap; -#[derive(Clone, PartialEq, Eq, Default)] -pub struct MainInput { - input: IndexMap>, -} - -#[allow(clippy::len_without_is_empty)] -impl MainInput { - pub fn new() -> Self { - Self::default() - } - - /// Returns an empty version of this struct with `None` values. - /// Called during constraint synthesis to provide private input variables. - pub fn empty(&self) -> Self { - let mut input = self.input.clone(); - - input.iter_mut().for_each(|(_name, value)| { - *value = None; - }); - - Self { input } - } - - pub fn len(&self) -> usize { - self.input.len() - } - - pub fn insert(&mut self, key: String, value: Option) { - self.input.insert(key, value); - } - - /// Parses main input definitions and stores them in `self`. - pub fn parse(&mut self, definitions: Vec) -> Result<(), InputParserError> { - for definition in definitions { - let name = definition.parameter.variable.value; - let value = InputValue::from_expression(definition.parameter.type_, definition.expression)?; - - self.insert(name, Some(value)); - } - - Ok(()) - } - - /// Returns an `Option` of the main function input at `name` - pub fn get(&self, name: &str) -> Option> { - self.input.get(name).cloned() - } -} +main_input_section!(MainInput); diff --git a/ast/src/input/program_input/mod.rs b/ast/src/input/program_input/mod.rs index dfda5a073f..89005a1d0a 100644 --- a/ast/src/input/program_input/mod.rs +++ b/ast/src/input/program_input/mod.rs @@ -16,6 +16,9 @@ #![allow(clippy::module_inception)] +pub mod constant_input; +pub use constant_input::*; + pub mod main_input; pub use main_input::*; diff --git a/ast/src/input/program_input/program_input.rs b/ast/src/input/program_input/program_input.rs index 42efab1a25..a2c99f2570 100644 --- a/ast/src/input/program_input/program_input.rs +++ b/ast/src/input/program_input/program_input.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{InputValue, MainInput, Registers}; +use crate::{ConstantInput, InputValue, MainInput, Registers}; use leo_input::{ sections::{Header, Section}, InputParserError, @@ -23,6 +23,7 @@ use leo_input::{ #[derive(Clone, PartialEq, Eq, Default)] pub struct ProgramInput { pub main: MainInput, + pub constants: ConstantInput, registers: Registers, } @@ -36,18 +37,24 @@ impl ProgramInput { /// Called during constraint synthesis to provide private input values. pub fn empty(&self) -> Self { let main = self.main.empty(); + let constants = self.constants.empty(); let registers = self.registers.empty(); - Self { main, registers } + Self { + main, + constants, + registers, + } } pub fn len(&self) -> usize { let mut len = 0; - // add main input variables + // Add main input variables and constants. len += self.main.len(); + len += self.constants.len(); - // add registers + // Add registers. if self.registers.is_present() { len += 1; } @@ -58,6 +65,7 @@ impl ProgramInput { /// Parse each input included in a file and store them in `self`. pub fn parse(&mut self, section: Section) -> Result<(), InputParserError> { match section.header { + Header::Constants(_constants) => self.constants.parse(section.definitions), Header::Main(_main) => self.main.parse(section.definitions), Header::Registers(_registers) => self.registers.parse(section.definitions), header => Err(InputParserError::input_section_header(header)), @@ -70,6 +78,11 @@ impl ProgramInput { self.main.get(name) } + #[allow(clippy::ptr_arg)] + pub fn get_constant(&self, name: &String) -> Option> { + self.constants.get(name) + } + /// Returns the runtime register input values pub fn get_registers(&self) -> &Registers { &self.registers diff --git a/ast/src/input/program_input/registers.rs b/ast/src/input/program_input/registers.rs index 21e25030e3..59c4f63ffa 100644 --- a/ast/src/input/program_input/registers.rs +++ b/ast/src/input/program_input/registers.rs @@ -19,4 +19,4 @@ use leo_input::{definitions::Definition, InputParserError}; use indexmap::IndexMap; -input_section_impl!(Registers); +record_input_section!(Registers); diff --git a/ast/src/input/program_state/private_state/record.rs b/ast/src/input/program_state/private_state/record.rs index 4cb71920aa..9235078bd6 100644 --- a/ast/src/input/program_state/private_state/record.rs +++ b/ast/src/input/program_state/private_state/record.rs @@ -19,4 +19,4 @@ use leo_input::{definitions::Definition, InputParserError}; use indexmap::IndexMap; -input_section_impl!(Record); +record_input_section!(Record); diff --git a/ast/src/input/program_state/private_state/state_leaf.rs b/ast/src/input/program_state/private_state/state_leaf.rs index 8e9b0e231b..a5c54aa5bb 100644 --- a/ast/src/input/program_state/private_state/state_leaf.rs +++ b/ast/src/input/program_state/private_state/state_leaf.rs @@ -19,4 +19,4 @@ use leo_input::{definitions::Definition, InputParserError}; use indexmap::IndexMap; -input_section_impl!(StateLeaf); +record_input_section!(StateLeaf); diff --git a/ast/src/input/program_state/public_state/state.rs b/ast/src/input/program_state/public_state/state.rs index dda85ddf94..c54414a2f3 100644 --- a/ast/src/input/program_state/public_state/state.rs +++ b/ast/src/input/program_state/public_state/state.rs @@ -19,4 +19,4 @@ use leo_input::{definitions::Definition, InputParserError}; use indexmap::IndexMap; -input_section_impl!(State); +record_input_section!(State); diff --git a/ast/src/types/integer_type.rs b/ast/src/types/integer_type.rs index 579ca2c6c0..e2f3cab5bc 100644 --- a/ast/src/types/integer_type.rs +++ b/ast/src/types/integer_type.rs @@ -23,7 +23,7 @@ use leo_input::types::{ use serde::{Deserialize, Serialize}; use std::fmt; -/// Explicit integer type +/// Explicit integer type. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum IntegerType { U8, diff --git a/compiler/src/errors/function.rs b/compiler/src/errors/function.rs index 43a2179365..42beafc154 100644 --- a/compiler/src/errors/function.rs +++ b/compiler/src/errors/function.rs @@ -103,6 +103,33 @@ impl FunctionError { FunctionError::Error(FormattedError::new_from_span(message, span)) } + pub fn input_type_mismatch(expected: String, actual: String, variable: String, span: &Span) -> Self { + let message = format!( + "Expected input variable `{}` to be type `{}`, found type `{}`", + variable, expected, actual + ); + + Self::new_from_span(message, span) + } + + pub fn expected_const_input(variable: String, span: &Span) -> Self { + let message = format!( + "Expected input variable `{}` to be constant. Move input variable `{}` to [constants] section of input file", + variable, variable + ); + + Self::new_from_span(message, span) + } + + pub fn expected_non_const_input(variable: String, span: &Span) -> Self { + let message = format!( + "Expected input variable `{}` to be non-constant. Move input variable `{}` to [main] section of input file", + variable, variable + ); + + Self::new_from_span(message, span) + } + pub fn invalid_array(actual: String, span: &Span) -> Self { let message = format!("Expected function input array, found `{}`", actual); @@ -118,6 +145,15 @@ impl FunctionError { Self::new_from_span(message, span) } + pub fn tuple_size_mismatch(expected: usize, actual: usize, span: &Span) -> Self { + let message = format!( + "Input tuple size mismatch expected {}, found tuple with length {}", + expected, actual + ); + + Self::new_from_span(message, span) + } + pub fn invalid_tuple(actual: String, span: &Span) -> Self { let message = format!("Expected function input tuple, found `{}`", actual); @@ -141,4 +177,10 @@ impl FunctionError { Self::new_from_span(message, span) } + + pub fn double_input_declaration(input_name: String, span: &Span) -> Self { + let message = format!("Input variable {} declared twice", input_name); + + Self::new_from_span(message, span) + } } diff --git a/compiler/src/expression/arithmetic/bit_not.rs b/compiler/src/expression/arithmetic/bit_not.rs index c040836e61..3f6f973839 100644 --- a/compiler/src/expression/arithmetic/bit_not.rs +++ b/compiler/src/expression/arithmetic/bit_not.rs @@ -25,8 +25,5 @@ pub fn evaluate_bit_not<'a, F: PrimeField, G: GroupType>( value: ConstrainedValue<'a, F, G>, span: &Span, ) -> Result, IntegerError> { - match value { - // ConstrainedValue::Integer(i) => Ok(ConstrainedValue::Integer(i.not())), - value => Err(IntegerError::cannot_evaluate(format!("~{}", value), span)), - } + Err(IntegerError::cannot_evaluate(format!("!{}", value), span)) } diff --git a/compiler/src/function/input/main_function_input.rs b/compiler/src/function/input/main_function_input.rs index cbf9150a64..5eab3a9a7c 100644 --- a/compiler/src/function/input/main_function_input.rs +++ b/compiler/src/function/input/main_function_input.rs @@ -26,13 +26,15 @@ use crate::{ group::input::group_from_input, ConstrainedValue, }, + FieldType, GroupType, Integer, }; - -use leo_asg::Type; +use leo_asg::{ConstInt, Type}; use leo_ast::{InputValue, Span}; + use snarkvm_fields::PrimeField; +use snarkvm_gadgets::traits::utilities::boolean::Boolean; use snarkvm_r1cs::ConstraintSystem; impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { @@ -58,7 +60,68 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { )?)), Type::Array(type_, len) => self.allocate_array(cs, name, &*type_, *len, input_option, span), Type::Tuple(types) => self.allocate_tuple(cs, &name, types, input_option, span), - _ => unimplemented!("main function input not implemented for type"), + _ => unimplemented!("main function input not implemented for type {}", type_), // Should not happen. + } + } +} + +/// Process constant inputs and return ConstrainedValue with constant value in it. +impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { + pub fn constant_main_function_input>( + &mut self, + _cs: &mut CS, + type_: &Type, + name: &str, + input_option: Option, + span: &Span, + ) -> Result, FunctionError> { + let input = input_option.ok_or_else(|| FunctionError::input_not_found(name.to_string(), span))?; + + match (type_, input) { + (Type::Address, InputValue::Address(addr)) => Ok(ConstrainedValue::Address(Address::constant(addr, span)?)), + (Type::Boolean, InputValue::Boolean(value)) => Ok(ConstrainedValue::Boolean(Boolean::constant(value))), + (Type::Field, InputValue::Field(value)) => Ok(ConstrainedValue::Field(FieldType::constant(value, span)?)), + (Type::Group, InputValue::Group(value)) => Ok(ConstrainedValue::Group(G::constant(&value.into(), span)?)), + (Type::Integer(integer_type), InputValue::Integer(_, value)) => Ok(ConstrainedValue::Integer( + Integer::new(&ConstInt::parse(integer_type, &value, span)?), + )), + (Type::Array(type_, arr_len), InputValue::Array(values)) => { + if *arr_len != values.len() { + return Err(FunctionError::invalid_input_array_dimensions( + *arr_len, + values.len(), + span, + )); + } + + Ok(ConstrainedValue::Array( + values + .iter() + .map(|x| self.constant_main_function_input(_cs, type_, name, Some(x.clone()), span)) + .collect::, _>>()?, + )) + } + (Type::Tuple(types), InputValue::Tuple(values)) => { + if values.len() != types.len() { + return Err(FunctionError::tuple_size_mismatch(types.len(), values.len(), span)); + } + + Ok(ConstrainedValue::Tuple( + values + .iter() + .map(|x| self.constant_main_function_input(_cs, type_, name, Some(x.clone()), span)) + .collect::, _>>()?, + )) + } + (Type::Circuit(_), _) => unimplemented!("main function input not implemented for type {}", type_), // Should not happen. + + // Return an error if the input type and input value do not match. + (_, input) => Err(FunctionError::input_type_mismatch( + type_.to_string(), + input.to_string(), + name.to_string(), + span, + )), } } } diff --git a/compiler/src/function/input/tuple.rs b/compiler/src/function/input/tuple.rs index e31a20620e..f35922718b 100644 --- a/compiler/src/function/input/tuple.rs +++ b/compiler/src/function/input/tuple.rs @@ -37,7 +37,11 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { match input_value { Some(InputValue::Tuple(values)) => { - // Allocate each value in the tuple + if values.len() != types.len() { + return Err(FunctionError::tuple_size_mismatch(types.len(), values.len(), span)); + } + + // Allocate each value in the tuple. for (i, (value, type_)) in values.into_iter().zip(types.iter()).enumerate() { let value_name = format!("{}_{}", &name, &i.to_string()); diff --git a/compiler/src/function/main_function.rs b/compiler/src/function/main_function.rs index 8a77d3fd21..b7502f3caf 100644 --- a/compiler/src/function/main_function.rs +++ b/compiler/src/function/main_function.rs @@ -61,18 +61,55 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { { let input_variable = input_variable.get().borrow(); let name = input_variable.name.name.clone(); - let input_option = input.get(&name).ok_or_else(|| { - FunctionError::input_not_found(name.clone(), &function.span.clone().unwrap_or_default()) - })?; - let input_value = self.allocate_main_function_input( - cs, - &input_variable.type_.clone(), - &name, - input_option, - &function.span.clone().unwrap_or_default(), - )?; - // Store a new variable for every allocated main function input + let input_value = match (input_variable.const_, input.get(&name), input.get_constant(&name)) { + // If variable is in both [main] and [constants] sections - error. + (_, Some(_), Some(_)) => { + return Err(FunctionError::double_input_declaration( + name.clone(), + &function.span.clone().unwrap_or_default(), + )); + } + // If input option is found in [main] section and input is not const. + (false, Some(input_option), _) => self.allocate_main_function_input( + cs, + &input_variable.type_.clone(), + &name, + input_option, + &function.span.clone().unwrap_or_default(), + )?, + // If input option is found in [constants] section and function argument is const. + (true, _, Some(input_option)) => self.constant_main_function_input( + cs, + &input_variable.type_.clone(), + &name, + input_option, + &function.span.clone().unwrap_or_default(), + )?, + // Function argument is const, input is not. + (true, Some(_), None) => { + return Err(FunctionError::expected_const_input( + name.clone(), + &function.span.clone().unwrap_or_default(), + )); + } + // Input is const, function argument is not. + (false, None, Some(_)) => { + return Err(FunctionError::expected_non_const_input( + name.clone(), + &function.span.clone().unwrap_or_default(), + )); + } + // When not found - Error out. + (_, _, _) => { + return Err(FunctionError::input_not_found( + name.clone(), + &function.span.clone().unwrap_or_default(), + )); + } + }; + + // Store a new variable for every function input. self.store(input_variable.id, input_value); } arguments.push(Cell::new(&*function.scope.alloc_expression(Expression::VariableRef( diff --git a/compiler/tests/input_files/mod.rs b/compiler/tests/input_files/mod.rs index c0dc97c1a3..af220752b8 100644 --- a/compiler/tests/input_files/mod.rs +++ b/compiler/tests/input_files/mod.rs @@ -16,5 +16,6 @@ mod program_input; mod program_input_and_program_state; +mod program_input_constants; mod program_registers; mod program_state; diff --git a/compiler/tests/input_files/program_input/input/main_tuple_fail.in b/compiler/tests/input_files/program_input/input/main_tuple_fail.in new file mode 100644 index 0000000000..aada924c29 --- /dev/null +++ b/compiler/tests/input_files/program_input/input/main_tuple_fail.in @@ -0,0 +1,2 @@ +[main] +x: (u8, bool) = (10, true); // wrong size here; main expects (u8, bool, u8) \ No newline at end of file diff --git a/compiler/tests/input_files/program_input/main_tuple_fail.leo b/compiler/tests/input_files/program_input/main_tuple_fail.leo new file mode 100644 index 0000000000..7874773eff --- /dev/null +++ b/compiler/tests/input_files/program_input/main_tuple_fail.leo @@ -0,0 +1,3 @@ +function main(x: (u8, bool, u8)) { + console.log("x: {}", x); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input/mod.rs b/compiler/tests/input_files/program_input/mod.rs index 529a512fdb..9fa79b9790 100644 --- a/compiler/tests/input_files/program_input/mod.rs +++ b/compiler/tests/input_files/program_input/mod.rs @@ -94,6 +94,16 @@ fn test_input_array_dimensions_mismatch() { expect_fail(program); } +#[test] +fn test_tuple_size_mismatch() { + let program_string = include_str!("main_tuple_fail.leo"); + let input_string = include_str!("input/main_tuple_fail.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + #[test] fn test_field_input() { let program_string = include_str!("main_field.leo"); diff --git a/compiler/tests/input_files/program_input_constants/input/main.in b/compiler/tests/input_files/program_input_constants/input/main.in new file mode 100644 index 0000000000..625ffc1ffb --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main.in @@ -0,0 +1,2 @@ +[constants] +a: bool = true; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_array.in b/compiler/tests/input_files/program_input_constants/input/main_array.in new file mode 100644 index 0000000000..3fe6502e8f --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_array.in @@ -0,0 +1,2 @@ +[constants] +x: [i16; 1] = [0i16; 1]; diff --git a/compiler/tests/input_files/program_input_constants/input/main_array_fail.in b/compiler/tests/input_files/program_input_constants/input/main_array_fail.in new file mode 100644 index 0000000000..e63331af22 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_array_fail.in @@ -0,0 +1,2 @@ +[constants] +x: [i16; 1] = [0i16; 1]; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_double_declaration_fail.in b/compiler/tests/input_files/program_input_constants/input/main_double_declaration_fail.in new file mode 100644 index 0000000000..fcc95402a3 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_double_declaration_fail.in @@ -0,0 +1,5 @@ +[main] +a: bool = true; + +[constants] +a: bool = false; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_fail_name.in b/compiler/tests/input_files/program_input_constants/input/main_fail_name.in new file mode 100644 index 0000000000..0cae21cc28 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_fail_name.in @@ -0,0 +1,2 @@ +[constants] +bad_name: bool = true; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_fail_type.in b/compiler/tests/input_files/program_input_constants/input/main_fail_type.in new file mode 100644 index 0000000000..0bb8f694fd --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_fail_type.in @@ -0,0 +1,2 @@ +[constants] +a: u8 = 1; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_field.in b/compiler/tests/input_files/program_input_constants/input/main_field.in new file mode 100644 index 0000000000..deea2a01a3 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_field.in @@ -0,0 +1,3 @@ +[constants] +a: field = 1; +b: field = -1; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_group.in b/compiler/tests/input_files/program_input_constants/input/main_group.in new file mode 100644 index 0000000000..52e52dc6a8 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_group.in @@ -0,0 +1,4 @@ +[constants] +a: group = 1group; +b: group = -1group; +c: group = (0, -1)group; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_multi_dimension_array.in b/compiler/tests/input_files/program_input_constants/input/main_multi_dimension_array.in new file mode 100644 index 0000000000..0d052ae99b --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_multi_dimension_array.in @@ -0,0 +1,2 @@ +[constants] +x: [i16; (2, 2, 3)] = [0i16; (2, 2, 3)]; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_multiple.in b/compiler/tests/input_files/program_input_constants/input/main_multiple.in new file mode 100644 index 0000000000..3e61056b7b --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_multiple.in @@ -0,0 +1,3 @@ +[constants] +a: bool = true; +b: bool = false; diff --git a/compiler/tests/input_files/program_input_constants/input/main_not_const_input_fail.in b/compiler/tests/input_files/program_input_constants/input/main_not_const_input_fail.in new file mode 100644 index 0000000000..b2aaee854e --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_not_const_input_fail.in @@ -0,0 +1,2 @@ +[main] +a: bool = true; // expecting const a, not main a \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_tuple_fail.in b/compiler/tests/input_files/program_input_constants/input/main_tuple_fail.in new file mode 100644 index 0000000000..4341911fba --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_tuple_fail.in @@ -0,0 +1,2 @@ +[constants] +x: (u8, bool) = (10, true); // wrong size here; main expects (u8, bool, u8) \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/input/main_type_mismatch.in b/compiler/tests/input_files/program_input_constants/input/main_type_mismatch.in new file mode 100644 index 0000000000..d50f65428d --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/input/main_type_mismatch.in @@ -0,0 +1,2 @@ +[constants] +a: u8 = 10; \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main.leo b/compiler/tests/input_files/program_input_constants/main.leo new file mode 100644 index 0000000000..0c33516ba8 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main.leo @@ -0,0 +1,3 @@ +function main(const a: bool) { + console.assert(a == true); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_array.leo b/compiler/tests/input_files/program_input_constants/main_array.leo new file mode 100644 index 0000000000..aa592183da --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_array.leo @@ -0,0 +1,4 @@ +function main (const x: [i16; 1]) { + console.log("{}", x); + console.assert(x[0] == 0); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_array_fail.leo b/compiler/tests/input_files/program_input_constants/main_array_fail.leo new file mode 100644 index 0000000000..6e7656f9b9 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_array_fail.leo @@ -0,0 +1,3 @@ +function main(const x: [i16; 2]){ + console.log("x: {}", x); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_field.leo b/compiler/tests/input_files/program_input_constants/main_field.leo new file mode 100644 index 0000000000..60fc67fc74 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_field.leo @@ -0,0 +1,5 @@ +function main(const a: field, const b: field) { + // Change to assert when == is implemented for field. + console.log("a: {}", a); + console.log("b: {}", b); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_group.leo b/compiler/tests/input_files/program_input_constants/main_group.leo new file mode 100644 index 0000000000..93accf710d --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_group.leo @@ -0,0 +1,6 @@ +function main(const a: group, const b: group, const c: group) { + // Change to assert when == is implemented for group. + console.log("a: {}", a); + console.log("b: {}", b); + console.log("c: {}", c); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_multi_dimension_array.leo b/compiler/tests/input_files/program_input_constants/main_multi_dimension_array.leo new file mode 100644 index 0000000000..56a1b3c7cf --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_multi_dimension_array.leo @@ -0,0 +1,9 @@ +function main(const x: [i16; (2, 2, 3)]){ + console.log("x: {}", x); + + let y: [i16; (2, 2, 3)] = [0i16; (2, 2, 3)]; + console.log("y: {}", y); + + console.assert(x[0][0][0] == y[0][0][0]); + console.assert(x[1][1][2] == y[1][1][2]); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_multiple.leo b/compiler/tests/input_files/program_input_constants/main_multiple.leo new file mode 100644 index 0000000000..2c2179cc3d --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_multiple.leo @@ -0,0 +1,4 @@ +function main(const a: bool, const b: bool) { + console.assert(a == true); + console.assert(b == false); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/main_tuple_fail.leo b/compiler/tests/input_files/program_input_constants/main_tuple_fail.leo new file mode 100644 index 0000000000..639ec42835 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/main_tuple_fail.leo @@ -0,0 +1,3 @@ +function main(const x: (u8, bool, u8)) { + console.log("x: {}", x); +} \ No newline at end of file diff --git a/compiler/tests/input_files/program_input_constants/mod.rs b/compiler/tests/input_files/program_input_constants/mod.rs new file mode 100644 index 0000000000..284aef6312 --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/mod.rs @@ -0,0 +1,145 @@ +// Copyright (C) 2019-2021 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::{assert_satisfied, expect_compiler_error, parse_program_with_input, EdwardsTestCompiler}; +use leo_compiler::errors::CompilerError; + +fn expect_fail(program: EdwardsTestCompiler) { + match expect_compiler_error(program) { + CompilerError::FunctionError(_) => {} + err => panic!("expected input parser error, got {:?}", err), + } +} + +#[test] +fn test_input_pass() { + let program_string = include_str!("main.leo"); + let input_string = include_str!("input/main.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_input_array_fail() { + let program_string = include_str!("main_array.leo"); + let input_string = include_str!("input/main_array.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_input_multi_dimension_array() { + let program_string = include_str!("main_multi_dimension_array.leo"); + let input_string = include_str!("input/main_multi_dimension_array.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_input_fail_name() { + let program_string = include_str!("main.leo"); + let input_string = include_str!("input/main_fail_name.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + +#[test] +fn test_input_fail_type() { + let program_string = include_str!("main.leo"); + let input_string = include_str!("input/main_fail_type.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + +#[test] +fn test_input_multiple() { + let program_string = include_str!("main_multiple.leo"); + let input_string = include_str!("input/main_multiple.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_input_array_dimensions_mismatch() { + let program_string = include_str!("main_array_fail.leo"); + let input_string = include_str!("input/main_array_fail.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + +#[test] +fn test_input_double_declaration() { + let program_string = include_str!("main.leo"); + let input_string = include_str!("input/main_double_declaration_fail.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + +#[test] +fn test_non_constant_input() { + let program_string = include_str!("main.leo"); + let input_string = include_str!("input/main_not_const_input_fail.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + +#[test] +fn test_tuple_size_mismatch() { + let program_string = include_str!("main_tuple_fail.leo"); + let input_string = include_str!("input/main_tuple_fail.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + expect_fail(program); +} + +#[test] +fn test_field_input() { + let program_string = include_str!("main_field.leo"); + let input_string = include_str!("input/main_field.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + assert_satisfied(program); +} + +#[test] +fn test_group_input() { + let program_string = include_str!("main_group.leo"); + let input_string = include_str!("input/main_group.in"); + + let program = parse_program_with_input(program_string, input_string).unwrap(); + + assert_satisfied(program); +} diff --git a/examples/pedersen-hash/inputs/pedersen-hash.in b/examples/pedersen-hash/inputs/pedersen-hash.in index 4434b65eb8..14028c3bf4 100644 --- a/examples/pedersen-hash/inputs/pedersen-hash.in +++ b/examples/pedersen-hash/inputs/pedersen-hash.in @@ -1,4 +1,8 @@ [main] +hash_input: [bool; 256] = [true; 256]; + +[constants] +parameters: [group; 256] = [1group; 256]; [registers] r0: group = (1, 0)group; \ No newline at end of file diff --git a/examples/pedersen-hash/src/main.leo b/examples/pedersen-hash/src/main.leo index e0adfecaec..aed79399f4 100644 --- a/examples/pedersen-hash/src/main.leo +++ b/examples/pedersen-hash/src/main.leo @@ -18,10 +18,8 @@ circuit PedersenHash { } // The 'pedersen-hash' main function. -function main() -> group { - let parameters = [1group; 256]; +function main(hash_input: [bool; 256], const parameters: [group; 256]) -> group { let pedersen = PedersenHash::new(parameters); - let hash_input: [bool; 256] = [true; 256]; return pedersen.hash(hash_input) } diff --git a/input/src/leo-input.pest b/input/src/leo-input.pest index f4c5ac0bdf..064dc40584 100644 --- a/input/src/leo-input.pest +++ b/input/src/leo-input.pest @@ -210,11 +210,14 @@ registers = { "registers" } // Declared in sections/state.rs state = { "state" } +// Declared in sections/constants.rs +constants = { "constants" } + // Declared in sections/state_leaf.rs state_leaf = { "state_leaf" } // Declared in sections/header.rs -header = { main | record | registers | state_leaf | state | identifier } +header = { main | constants | record | registers | state_leaf | state | identifier } /// Definitions diff --git a/input/src/sections/constants.rs b/input/src/sections/constants.rs new file mode 100644 index 0000000000..5e238f59c1 --- /dev/null +++ b/input/src/sections/constants.rs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2021 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::ast::Rule; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::constants))] +pub struct Constants<'ast> { + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/input/src/sections/header.rs b/input/src/sections/header.rs index ca437c0a45..e0296bf165 100644 --- a/input/src/sections/header.rs +++ b/input/src/sections/header.rs @@ -17,7 +17,7 @@ use crate::{ ast::Rule, common::Identifier, - sections::{Main, Record, Registers, State, StateLeaf}, + sections::{Constants, Main, Record, Registers, State, StateLeaf}, }; use pest::Span; @@ -27,6 +27,7 @@ use std::fmt; #[derive(Clone, Debug, FromPest, PartialEq)] #[pest_ast(rule(Rule::header))] pub enum Header<'ast> { + Constants(Constants<'ast>), Main(Main<'ast>), Record(Record<'ast>), Registers(Registers<'ast>), @@ -38,6 +39,7 @@ pub enum Header<'ast> { impl<'ast> Header<'ast> { pub fn span(self) -> Span<'ast> { match self { + Header::Constants(constants) => constants.span, Header::Main(main) => main.span, Header::Record(record) => record.span, Header::Registers(registers) => registers.span, @@ -51,6 +53,7 @@ impl<'ast> Header<'ast> { impl<'ast> fmt::Display for Header<'ast> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + Header::Constants(_constants) => write!(f, "constants"), Header::Main(_main) => write!(f, "main"), Header::Record(_record) => write!(f, "record"), Header::Registers(_registers) => write!(f, "registers"), diff --git a/input/src/sections/mod.rs b/input/src/sections/mod.rs index 918990dd8d..45025d9cb0 100644 --- a/input/src/sections/mod.rs +++ b/input/src/sections/mod.rs @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . +pub mod constants; +pub use constants::*; + pub mod header; pub use header::*; diff --git a/synthesizer/src/circuit_synthesizer.rs b/synthesizer/src/circuit_synthesizer.rs index e12a3292d0..ed9361ca7d 100644 --- a/synthesizer/src/circuit_synthesizer.rs +++ b/synthesizer/src/circuit_synthesizer.rs @@ -113,19 +113,20 @@ impl ConstraintSystem for CircuitSynthesizer { } fn pop_namespace(&mut self) { - if let Some(ns) = self.namespaces.pop() { - for idx in ns.constraint_indices { - self.constraints.remove(idx); - } - - for idx in ns.private_var_indices { - self.private_variables.remove(idx); - } - - for idx in ns.public_var_indices { - self.public_variables.remove(idx); - } - } + // Todo @ljedrz: Fix constraint system optimizations. + // if let Some(ns) = self.namespaces.pop() { + // for idx in ns.constraint_indices { + // self.constraints.remove(idx); + // } + // + // for idx in ns.private_var_indices { + // self.private_variables.remove(idx); + // } + // + // for idx in ns.public_var_indices { + // self.public_variables.remove(idx); + // } + // } } fn get_root(&mut self) -> &mut Self::Root {