diff --git a/compiler/src/errors/function.rs b/compiler/src/errors/function.rs index 43a2179365..837d927ec5 100644 --- a/compiler/src/errors/function.rs +++ b/compiler/src/errors/function.rs @@ -118,6 +118,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 +150,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/function/input/main_function_input.rs b/compiler/src/function/input/main_function_input.rs index 459a0856e2..a54e4a34e5 100644 --- a/compiler/src/function/input/main_function_input.rs +++ b/compiler/src/function/input/main_function_input.rs @@ -60,18 +60,18 @@ 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_), } } } /// Process constant inputs and return ConstrainedValue with constant value in it. impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { - pub fn parse_constant_main_function_input>( + pub fn constant_main_function_input>( &mut self, // TODO: remove unnecessary arguments _cs: &mut CS, - _type_: &Type, + type_: &Type, name: &str, input_option: Option, span: &Span, @@ -101,18 +101,48 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { ConstrainedValue::Integer(Integer::new(&const_int)) } - InputValue::Array(values) => ConstrainedValue::Array( - values - .iter() - .map(|x| self.parse_constant_main_function_input(_cs, _type_, name, Some(x.clone()), span)) - .collect::, _>>()?, - ), - InputValue::Tuple(values) => ConstrainedValue::Tuple( - values - .iter() - .map(|x| self.parse_constant_main_function_input(_cs, _type_, name, Some(x.clone()), span)) - .collect::, _>>()?, - ), + InputValue::Array(values) => { + // Get ASG type and array length to compare with provided inputs. + let (type_, arr_len) = if let Type::Array(type_, len) = type_ { + (type_, *len) + } else { + return Err(FunctionError::input_not_found("expected".to_string(), &span)); + }; + + if arr_len != values.len() { + return Err(FunctionError::invalid_input_array_dimensions( + arr_len, + values.len(), + span, + )); + } + + ConstrainedValue::Array( + values + .iter() + .map(|x| self.constant_main_function_input(_cs, type_, name, Some(x.clone()), span)) + .collect::, _>>()?, + ) + } + InputValue::Tuple(values) => { + // Get ASG tuple size and compare it to input tuple size. + let tuple_len = if let Type::Tuple(types) = type_ { + types.len() + } else { + return Err(FunctionError::tuple_size_mismatch(0, values.len(), span)); + }; + + if values.len() != tuple_len { + return Err(FunctionError::tuple_size_mismatch(tuple_len, values.len(), span)); + } + + ConstrainedValue::Tuple( + values + .iter() + .map(|x| self.constant_main_function_input(_cs, type_, name, Some(x.clone()), span)) + .collect::, _>>()?, + ) + } }) } } diff --git a/compiler/src/function/main_function.rs b/compiler/src/function/main_function.rs index dd94ebf4ed..a5ff44360d 100644 --- a/compiler/src/function/main_function.rs +++ b/compiler/src/function/main_function.rs @@ -63,8 +63,15 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { let name = input_variable.name.name.clone(); let input_value = match (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. - (Some(input_option), _) => self.parse_constant_main_function_input( + (Some(input_option), _) => self.allocate_main_function_input( cs, &input_variable.type_.clone(), &name, @@ -72,7 +79,7 @@ impl<'a, F: PrimeField, G: GroupType> ConstrainedProgram<'a, F, G> { &function.span.clone().unwrap_or_default(), )?, // If input option is found in [constants] section. - (_, Some(input_option)) => self.allocate_main_function_input( + (_, Some(input_option)) => self.constant_main_function_input( cs, &input_variable.type_.clone(), &name, 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_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/main_tuple_fail.leo b/compiler/tests/input_files/program_input_constants/main_tuple_fail.leo new file mode 100644 index 0000000000..7874773eff --- /dev/null +++ b/compiler/tests/input_files/program_input_constants/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_constants/mod.rs b/compiler/tests/input_files/program_input_constants/mod.rs index 529a512fdb..30e891f1a6 100644 --- a/compiler/tests/input_files/program_input_constants/mod.rs +++ b/compiler/tests/input_files/program_input_constants/mod.rs @@ -94,6 +94,26 @@ fn test_input_array_dimensions_mismatch() { 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_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");