formatting of futures in CG

This commit is contained in:
evan-schott 2024-03-08 14:35:26 -08:00
parent dd6d40edf9
commit 143e9cdc23
4 changed files with 53 additions and 62 deletions

View File

@ -46,10 +46,12 @@ pub struct CodeGenerator<'a> {
pub(crate) is_transition_function: bool,
/// Are we traversing a finalize block?
pub(crate) in_finalize: bool,
// A reference to program. This is needed to look up external programs.
/// A reference to program. This is needed to look up external programs.
pub(crate) program: &'a Program,
// The program ID of the current program.
/// The program ID of the current program.
pub(crate) program_id: Option<ProgramId>,
/// A reference to the finalize caller.
pub(crate) finalize_caller: Option<Symbol>,
}
impl<'a> CodeGenerator<'a> {
@ -76,6 +78,7 @@ impl<'a> CodeGenerator<'a> {
in_finalize: false,
program,
program_id: None,
finalize_caller: None,
}
}
}

View File

@ -15,30 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::CodeGenerator;
use leo_ast::{
AccessExpression,
ArrayAccess,
ArrayExpression,
AssociatedConstant,
AssociatedFunction,
BinaryExpression,
BinaryOperation,
CallExpression,
CastExpression,
ErrExpression,
Expression,
Identifier,
Literal,
MemberAccess,
MethodCall,
StructExpression,
TernaryExpression,
TupleExpression,
Type,
UnaryExpression,
UnaryOperation,
UnitExpression,
};
use leo_ast::{AccessExpression, ArrayAccess, ArrayExpression, AssociatedConstant, AssociatedFunction, BinaryExpression, BinaryOperation, CallExpression, CastExpression, ErrExpression, Expression, Identifier, Literal, MemberAccess, MethodCall, Node, StructExpression, TernaryExpression, TupleExpression, Type, UnaryExpression, UnaryOperation, UnitExpression, Variant};
use leo_span::sym;
use std::borrow::Borrow;
@ -298,10 +275,9 @@ impl<'a> CodeGenerator<'a> {
fn visit_member_access(&mut self, input: &'a MemberAccess) -> (String, String) {
let (inner_expr, _) = self.visit_expression(&input.inner);
let member_access = match self.type_table.get(&input.id) {
Some(Type::Future(_)) => format!("{inner_expr}[{}]", input.name),
Some(Type::Composite(_)) => format!("{}.{}", inner_expr, input.name),
_ => unreachable!("Member access must be future or struct."),
let member_access = match self.type_table.get(&input.inner.id()) {
Some(Type::Future(_)) => format!("{inner_expr}[{}u32]", input.name),
_ => format!("{}.{}", inner_expr, input.name),
};
(member_access, String::new())
@ -482,12 +458,14 @@ impl<'a> CodeGenerator<'a> {
.expect("failed to write to string");
(destination_register, instruction)
}
sym::Await => {
sym::Future => {
let mut instruction = " await".to_string();
writeln!(instruction, " {};", arguments[0]).expect("failed to write to string");
(String::new(), instruction)
}
_ => unreachable!("All core functions should be known at this phase of compilation"),
_ => {
unreachable!("All core functions should be known at this phase of compilation")
},
};
// Add the instruction to the list of instructions.
instructions.push_str(&instruction);
@ -529,7 +507,7 @@ impl<'a> CodeGenerator<'a> {
// Lookup in symbol table to determine if its an async function.
if let Some(func) = self.symbol_table.lookup_fn_symbol(input.program.unwrap(), function_name) {
if func.is_async && input.program.unwrap() == self.program_id.unwrap().name.name {
format!(" async {}", input.function)
format!(" async {}", self.current_function.unwrap().identifier)
} else {
format!(" call {}", input.function)
}
@ -548,8 +526,9 @@ impl<'a> CodeGenerator<'a> {
// Initialize storage for the destination registers.
let mut destinations = Vec::new();
let return_type = &self.symbol_table.lookup_fn_symbol(main_program, function_name).unwrap().output_type;
match return_type {
// Create operands for the output registers.
let func = self.symbol_table.lookup_fn_symbol(main_program, function_name).unwrap();
match &func.output_type {
Type::Unit => {} // Do nothing
Type::Tuple(tuple) => match tuple.length() {
0 | 1 => unreachable!("Parsing guarantees that a tuple type has at least two elements"),
@ -567,6 +546,13 @@ impl<'a> CodeGenerator<'a> {
self.next_register += 1;
}
}
// Add a register for async functions to represent the future created.
if func.is_async && func.variant == Variant::Standard {
let destination_register = format!("r{}", self.next_register);
destinations.push(destination_register);
self.next_register += 1;
}
// Construct the output operands. These are the destination registers **without** the future.
let output_operands = destinations.join(" ");

View File

@ -95,7 +95,10 @@ impl<'a> CodeGenerator<'a> {
// Attach the associated finalize to async transitions.
if function.variant == Variant::Transition && function.is_async {
// Set state variables.
self.is_transition_function = false;
self.finalize_caller = Some(function.identifier.name.clone());
// Generate code for the associated finalize function.
let finalize = &self
.symbol_table
.lookup_fn_symbol(
@ -188,7 +191,7 @@ impl<'a> CodeGenerator<'a> {
let mut function_string = match (function.is_async, function.variant) {
(_, Variant::Transition) => format!("\nfunction {}:\n", function.identifier),
(false, Variant::Standard) => format!("\nclosure {}:\n", function.identifier),
(true, Variant::Standard) => format!("\nfinalize {}:\n", function.identifier),
(true, Variant::Standard) => format!("\nfinalize {}:\n", self.finalize_caller.unwrap()),
(_, Variant::Inline) => return String::from("\n"),
};
@ -206,8 +209,9 @@ impl<'a> CodeGenerator<'a> {
let type_string = match input {
functions::Input::Internal(input) => {
self.variable_mapping.insert(&input.identifier.name, register_string.clone());
let visibility = match (self.is_transition_function, input.mode) {
(true, Mode::None) => Mode::Private,
let visibility = match (self.is_transition_function, self.in_finalize, input.mode) {
(true, _, Mode::None) => Mode::Private,
(_, true, Mode::None) => Mode::Public,
_ => input.mode,
};
// Futures are displayed differently in the input section. `input r0 as foo.aleo/bar.future;`
@ -223,7 +227,7 @@ impl<'a> CodeGenerator<'a> {
format!("{}.aleo/{}.record", input.program_name, input.record)
}
};
writeln!(function_string, " input {register_string} as {type_string};",)
.expect("failed to write to string");
}

View File

@ -16,22 +16,7 @@
use crate::CodeGenerator;
use leo_ast::{
AssertStatement,
AssertVariant,
AssignStatement,
Block,
ConditionalStatement,
ConsoleStatement,
DefinitionStatement,
Expression,
ExpressionStatement,
IterationStatement,
Mode,
Output,
ReturnStatement,
Statement,
};
use leo_ast::{AssertStatement, AssertVariant, AssignStatement, Block, ConditionalStatement, ConsoleStatement, DefinitionStatement, Expression, ExpressionStatement, IterationStatement, Mode, Output, ReturnStatement, Statement, Type};
use itertools::Itertools;
@ -89,11 +74,13 @@ impl<'a> CodeGenerator<'a> {
// Get the output type of the function.
let output = self.current_function.unwrap().output.iter();
// If the operand string is empty, initialize an empty vector.
let operand_strings = match operand.is_empty() {
let mut operand_strings = match operand.is_empty() {
true => vec![],
false => operand.split(' ').collect_vec(),
};
let instructions = operand_strings
let mut future_output = String::new();
let mut instructions = operand_strings
.iter()
.zip_eq(output)
.map(|(operand, output)| {
@ -116,11 +103,19 @@ impl<'a> CodeGenerator<'a> {
// Only program functions have visibilities associated with their outputs.
Mode::None
};
format!(
" output {} as {};\n",
operand,
self.visit_type_with_visibility(&output.type_, visibility)
)
if let Type::Future(_) = output.type_ {
future_output = format!(
" output {} as {}.aleo/{}.future;\n",
operand, self.program_id.unwrap().name, self.current_function.unwrap().identifier,
);
String::new()
} else {
format!(
" output {} as {};\n",
operand,
self.visit_type_with_visibility(&output.type_, visibility)
)
}
}
Output::External(output) => {
format!(
@ -132,6 +127,9 @@ impl<'a> CodeGenerator<'a> {
})
.join("");
// Insert future output at the end.
instructions.push_str(&future_output);
expression_instructions.push_str(&instructions);
expression_instructions