diff --git a/compiler/ast/src/expressions/unary.rs b/compiler/ast/src/expressions/unary.rs index b1557cd58b..0355179f57 100644 --- a/compiler/ast/src/expressions/unary.rs +++ b/compiler/ast/src/expressions/unary.rs @@ -36,6 +36,10 @@ pub enum UnaryOperation { Square, /// Square root operation, i.e. `.sqrt()`. SquareRoot, + /// Converts a group element to its x-coordinate, i.e. `.to_x_coordinate()`. + ToXCoordinate, + /// Converts a group element to its y-coordinate, i.e. `.to_y_coordinate()`. + ToYCoordinate, } impl UnaryOperation { @@ -50,6 +54,8 @@ impl UnaryOperation { sym::not => Self::Not, sym::square => Self::Square, sym::square_root => Self::SquareRoot, + sym::to_x_coordinate => Self::ToXCoordinate, + sym::to_y_coordinate => Self::ToYCoordinate, _ => return None, }) } @@ -65,6 +71,8 @@ impl UnaryOperation { Self::Not => "not", Self::Square => "square", Self::SquareRoot => "square_root", + Self::ToXCoordinate => "to_x_coordinate", + Self::ToYCoordinate => "to_y_coordinate", } } } diff --git a/compiler/ast/src/functions/core_function.rs b/compiler/ast/src/functions/core_function.rs index 1f8f8c0c3f..379271ff0d 100644 --- a/compiler/ast/src/functions/core_function.rs +++ b/compiler/ast/src/functions/core_function.rs @@ -175,6 +175,9 @@ pub enum CoreFunction { MappingGet, MappingGetOrUse, MappingSet, + + GroupToXCoordinate, + GroupToYCoordinate, } impl CoreFunction { @@ -338,6 +341,9 @@ impl CoreFunction { (sym::Mapping, sym::get) => Self::MappingGet, (sym::Mapping, sym::get_or_use) => Self::MappingGetOrUse, (sym::Mapping, sym::set) => Self::MappingSet, + + (sym::group, sym::to_x_coordinate) => Self::GroupToXCoordinate, + (sym::group, sym::to_y_coordinate) => Self::GroupToYCoordinate, _ => return None, }) } @@ -502,6 +508,9 @@ impl CoreFunction { Self::MappingGet => 2, Self::MappingGetOrUse => 3, Self::MappingSet => 3, + + Self::GroupToXCoordinate => 1, + Self::GroupToYCoordinate => 1, } } } diff --git a/compiler/passes/src/code_generation/visit_expressions.rs b/compiler/passes/src/code_generation/visit_expressions.rs index a5378e68a4..40f38d23ea 100644 --- a/compiler/passes/src/code_generation/visit_expressions.rs +++ b/compiler/passes/src/code_generation/visit_expressions.rs @@ -149,19 +149,22 @@ impl<'a> CodeGenerator<'a> { fn visit_unary(&mut self, input: &'a UnaryExpression) -> (String, String) { let (expression_operand, expression_instructions) = self.visit_expression(&input.receiver); - let opcode = match input.op { - UnaryOperation::Abs => String::from("abs"), - UnaryOperation::AbsWrapped => String::from("abs.w"), - UnaryOperation::Double => String::from("double"), - UnaryOperation::Inverse => String::from("inv"), - UnaryOperation::Not => String::from("not"), - UnaryOperation::Negate => String::from("neg"), - UnaryOperation::Square => String::from("square"), - UnaryOperation::SquareRoot => String::from("sqrt"), + // Note that non-empty suffixes must be preceded by a space. + let (opcode, suffix) = match input.op { + UnaryOperation::Abs => ("abs", ""), + UnaryOperation::AbsWrapped => ("abs.w", ""), + UnaryOperation::Double => ("double", ""), + UnaryOperation::Inverse => ("inv", ""), + UnaryOperation::Not => ("not", ""), + UnaryOperation::Negate => ("neg", ""), + UnaryOperation::Square => ("square", ""), + UnaryOperation::SquareRoot => ("sqrt", ""), + UnaryOperation::ToXCoordinate => ("cast", " as group.x"), + UnaryOperation::ToYCoordinate => ("cast", " as group.y"), }; let destination_register = format!("r{}", self.next_register); - let unary_instruction = format!(" {opcode} {expression_operand} into {destination_register};\n"); + let unary_instruction = format!(" {opcode} {expression_operand} into {destination_register}{suffix};\n"); // Increment the register counter. self.next_register += 1; @@ -299,7 +302,7 @@ impl<'a> CodeGenerator<'a> { }; // Construct the instruction. - let (destination, instruction) = match input.ty { + let (destination, instruction) = match &input.ty { Type::Identifier(Identifier { name: sym::BHP256, .. }) => { construct_simple_function_call(&input.name, "bhp256", arguments) } @@ -357,6 +360,27 @@ impl<'a> CodeGenerator<'a> { } _ => unreachable!("The only variants of Mapping are get, get_or, and set"), }, + Type::Identifier(Identifier { name: sym::group, .. }) => { + match input.name { + Identifier { name: sym::to_x_coordinate, .. } => { + let mut instruction = " cast".to_string(); + let destination_register = get_destination_register(); + // Write the argument and the destination register. + writeln!(instruction, " {} into {destination_register} as group.x;", arguments[0],) + .expect("failed to write to string"); + (destination_register, instruction) + } + Identifier { name: sym::to_y_coordinate, .. } => { + let mut instruction = " cast".to_string(); + let destination_register = get_destination_register(); + // Write the argument and the destination register. + writeln!(instruction, " {} into {destination_register} as group.y;", arguments[0],) + .expect("failed to write to string"); + (destination_register, instruction) + } + _ => unreachable!("The only associated methods of group are to_x_coordinate and to_y_coordinate"), + } + } _ => unreachable!("All core functions should be known at this phase of compilation"), }; // Add the instruction to the list of instructions. diff --git a/compiler/passes/src/type_checking/check_expressions.rs b/compiler/passes/src/type_checking/check_expressions.rs index bbd9b673a1..13d1b76169 100644 --- a/compiler/passes/src/type_checking/check_expressions.rs +++ b/compiler/passes/src/type_checking/check_expressions.rs @@ -730,6 +730,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> { self.assert_field_type(destination, input.span()); self.visit_expression(&input.receiver, destination) } + UnaryOperation::ToXCoordinate | UnaryOperation::ToYCoordinate => { + // Only field type. + self.assert_field_type(destination, input.span()); + self.visit_expression(&input.receiver, &Some(Type::Group)) + } } } diff --git a/compiler/passes/src/type_checking/checker.rs b/compiler/passes/src/type_checking/checker.rs index 8baf15ddd2..2eb3a2c1ea 100644 --- a/compiler/passes/src/type_checking/checker.rs +++ b/compiler/passes/src/type_checking/checker.rs @@ -861,6 +861,11 @@ impl<'a> TypeChecker<'a> { None } } + CoreFunction::GroupToXCoordinate | CoreFunction::GroupToYCoordinate => { + // Check that the first argument is a group. + self.assert_group_type(&arguments[0].0, arguments[0].1); + Some(Type::Field) + } } } diff --git a/compiler/span/src/lib.rs b/compiler/span/src/lib.rs index 7bc511f832..03dbe28720 100644 --- a/compiler/span/src/lib.rs +++ b/compiler/span/src/lib.rs @@ -15,6 +15,7 @@ // along with the Leo library. If not, see . #![forbid(unsafe_code)] +#![recursion_limit = "256"] pub mod symbol; pub use symbol::{sym, Symbol}; diff --git a/compiler/span/src/symbol.rs b/compiler/span/src/symbol.rs index 57beba56ce..fd0575b6a5 100644 --- a/compiler/span/src/symbol.rs +++ b/compiler/span/src/symbol.rs @@ -175,6 +175,8 @@ symbols! { Poseidon4, Poseidon8, set, + to_x_coordinate, + to_y_coordinate, // types address, diff --git a/tests/expectations/compiler/group/to_x_coordinate.out b/tests/expectations/compiler/group/to_x_coordinate.out new file mode 100644 index 0000000000..d708a63580 --- /dev/null +++ b/tests/expectations/compiler/group/to_x_coordinate.out @@ -0,0 +1,12 @@ +--- +namespace: Compile +expectation: Pass +outputs: + - - initial_ast: 20332c6a83a3628dd4f17d8653acdf28dd8e54bcaf9cab071fda7cbbf3ff3d29 + unrolled_ast: 20332c6a83a3628dd4f17d8653acdf28dd8e54bcaf9cab071fda7cbbf3ff3d29 + ssa_ast: 2bfa5ff05133abdf9553186a96a35c8466b5a49366ea07cea92e01f30c04f769 + flattened_ast: f7fd524a8a3e98f0e01f4c71b09bea3032867a086447ea72f4cdded1a581983d + inlined_ast: f7fd524a8a3e98f0e01f4c71b09bea3032867a086447ea72f4cdded1a581983d + dce_ast: 644ec7d38093f28ca0b0908282d7ff8032f7b22a0cf98c47d5ffa0ad16f047b8 + bytecode: 51e95e10668242bec30e9917715d9856da632e933c33207ee41c5ed38d6366aa + warnings: "" diff --git a/tests/expectations/compiler/group/to_y_coordinate.out b/tests/expectations/compiler/group/to_y_coordinate.out new file mode 100644 index 0000000000..c182766986 --- /dev/null +++ b/tests/expectations/compiler/group/to_y_coordinate.out @@ -0,0 +1,12 @@ +--- +namespace: Compile +expectation: Pass +outputs: + - - initial_ast: 62e89c72fcd4f62002450d1c82060f2f662ad6e1c12a19f0e6b994c50ba0491b + unrolled_ast: 62e89c72fcd4f62002450d1c82060f2f662ad6e1c12a19f0e6b994c50ba0491b + ssa_ast: b3c38a64899eef777c4bdd38d5db3f68148334795f4e9b89449eb9148db03eb3 + flattened_ast: da24a573e4ff569e242f88f73869c2251e19bb5ae62ef602773c0a48863bb9b3 + inlined_ast: da24a573e4ff569e242f88f73869c2251e19bb5ae62ef602773c0a48863bb9b3 + dce_ast: d313e678afef867d5cbca6829c81515387bcc768eeb4044b25c7996f4d177c63 + bytecode: ea2e94f0f589fac4565040575643b1b7cd7813fe513d5b09b17c191bbf0f727e + warnings: "" diff --git a/tests/expectations/execution/group_operations.out b/tests/expectations/execution/group_operations.out new file mode 100644 index 0000000000..b6a5e19dc9 --- /dev/null +++ b/tests/expectations/execution/group_operations.out @@ -0,0 +1,18 @@ +--- +namespace: Execute +expectation: Pass +outputs: + - - initial_ast: ccfa20fa35d720984742081098965806736ba374ad046b3aadf6d899663375da + unrolled_ast: ccfa20fa35d720984742081098965806736ba374ad046b3aadf6d899663375da + ssa_ast: ab06973a60d8da80b174deae3dc4ee88216894503e613d3ba3ecd74c7f38e408 + flattened_ast: 9a9c3bc868b2e83c0cb0822e5bbf1d6f782f7992e0b748c6390a57768c44a4a7 + inlined_ast: 9a9c3bc868b2e83c0cb0822e5bbf1d6f782f7992e0b748c6390a57768c44a4a7 + dce_ast: 9a9c3bc868b2e83c0cb0822e5bbf1d6f782f7992e0b748c6390a57768c44a4a7 + bytecode: 5c20fda21a40464a1462524cf913438776a39383a671949312f48ce8ceb2dd16 + warnings: "" + results: + main: + - input: "[0group]" + output: "[0field, 1field]" + - input: "[2group]" + output: "[2field, 5553594316923449299484601589326170487897520766531075014687114064346375156608field]" diff --git a/tests/expectations/parser/expression/access/associated_function.out b/tests/expectations/parser/expression/access/associated_function.out new file mode 100644 index 0000000000..bc8d7ee84a --- /dev/null +++ b/tests/expectations/parser/expression/access/associated_function.out @@ -0,0 +1,24 @@ +--- +namespace: ParseExpression +expectation: Pass +outputs: + - Access: + AssociatedFunction: + ty: + Identifier: "{\"name\":\"group\",\"span\":\"{\\\"lo\\\":0,\\\"hi\\\":5}\"}" + name: "{\"name\":\"to_x_coordinate\",\"span\":\"{\\\"lo\\\":7,\\\"hi\\\":22}\"}" + arguments: + - Identifier: "{\"name\":\"a\",\"span\":\"{\\\"lo\\\":23,\\\"hi\\\":24}\"}" + span: + lo: 0 + hi: 25 + - Access: + AssociatedFunction: + ty: + Identifier: "{\"name\":\"group\",\"span\":\"{\\\"lo\\\":0,\\\"hi\\\":5}\"}" + name: "{\"name\":\"to_y_coordinate\",\"span\":\"{\\\"lo\\\":7,\\\"hi\\\":22}\"}" + arguments: + - Identifier: "{\"name\":\"a\",\"span\":\"{\\\"lo\\\":23,\\\"hi\\\":24}\"}" + span: + lo: 0 + hi: 25 diff --git a/tests/expectations/parser/expression/access/method_function.out b/tests/expectations/parser/expression/access/method_function.out new file mode 100644 index 0000000000..65a3250809 --- /dev/null +++ b/tests/expectations/parser/expression/access/method_function.out @@ -0,0 +1,18 @@ +--- +namespace: ParseExpression +expectation: Pass +outputs: + - Unary: + receiver: + Identifier: "{\"name\":\"a\",\"span\":\"{\\\"lo\\\":0,\\\"hi\\\":1}\"}" + op: ToXCoordinate + span: + lo: 0 + hi: 19 + - Unary: + receiver: + Identifier: "{\"name\":\"b\",\"span\":\"{\\\"lo\\\":0,\\\"hi\\\":1}\"}" + op: ToYCoordinate + span: + lo: 0 + hi: 19 diff --git a/tests/tests/compiler/group/to_x_coordinate.leo b/tests/tests/compiler/group/to_x_coordinate.leo new file mode 100644 index 0000000000..4bbd40f38b --- /dev/null +++ b/tests/tests/compiler/group/to_x_coordinate.leo @@ -0,0 +1,12 @@ +/* +namespace: Compile +expectation: Pass +*/ + +program test.aleo { + transition main(a: group) -> field { + let x: field = a.to_x_coordinate(); + let y: field = 0group.to_y_coordinate(); + return group::to_x_coordinate(a); + } +} diff --git a/tests/tests/compiler/group/to_y_coordinate.leo b/tests/tests/compiler/group/to_y_coordinate.leo new file mode 100644 index 0000000000..3687999cc0 --- /dev/null +++ b/tests/tests/compiler/group/to_y_coordinate.leo @@ -0,0 +1,12 @@ +/* +namespace: Compile +expectation: Pass +*/ + +program test.aleo { + transition main(a: group) -> field { + let x: field = a.to_x_coordinate(); + let y: field = 0group.to_y_coordinate(); + return group::to_y_coordinate(a); + } +} diff --git a/tests/tests/execution/group_operations.leo b/tests/tests/execution/group_operations.leo new file mode 100644 index 0000000000..6137a10a9e --- /dev/null +++ b/tests/tests/execution/group_operations.leo @@ -0,0 +1,22 @@ +/* +namespace: Execute +expectation: Pass +cases: + main: + - input: ["0group"] + - input: ["2group"] +*/ + +program test.aleo { + transition main(a: group) -> (field, field) { + let b: field = group::to_x_coordinate(a); + let c: field = a as field; + let e: field = a.to_x_coordinate(); + assert_eq(b, c); + assert_eq(b, e); + let d: field = group::to_y_coordinate(a); + let f: field = a.to_y_coordinate(); + assert_eq(d, f); + return (b, d); + } +} diff --git a/tests/tests/parser/expression/access/associated_function.leo b/tests/tests/parser/expression/access/associated_function.leo new file mode 100644 index 0000000000..a688fe6098 --- /dev/null +++ b/tests/tests/parser/expression/access/associated_function.leo @@ -0,0 +1,7 @@ +/* +namespace: ParseExpression +expectation: Pass +*/ + +group::to_x_coordinate(a) +group::to_y_coordinate(a) diff --git a/tests/tests/parser/expression/access/method_function.leo b/tests/tests/parser/expression/access/method_function.leo new file mode 100644 index 0000000000..f2169a44c7 --- /dev/null +++ b/tests/tests/parser/expression/access/method_function.leo @@ -0,0 +1,7 @@ +/* +namespace: ParseExpression +expectation: Pass +*/ + +a.to_x_coordinate() +b.to_y_coordinate()