Skip to content

Commit

Permalink
Merge pull request #2626 from AleoHQ/feat/reset-updates
Browse files Browse the repository at this point in the history
[Update] Supports new features for the upcoming network reset.
  • Loading branch information
d0cd authored Sep 29, 2023
2 parents 1d71bc5 + 67cdc56 commit 1bfcfac
Show file tree
Hide file tree
Showing 674 changed files with 4,231 additions and 4,116 deletions.
362 changes: 177 additions & 185 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion compiler/ast/src/program/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct Program {
/// A map from import names to import definitions.
pub imports: IndexMap<Symbol, (Program, Span)>,
/// A map from program names to program scopes.
pub program_scopes: IndexMap<ProgramId, ProgramScope>,
pub program_scopes: IndexMap<Symbol, ProgramScope>,
}

impl fmt::Display for Program {
Expand Down
2 changes: 1 addition & 1 deletion compiler/compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ impl<'a> Compiler<'a> {
struct_graph: &StructGraph,
call_graph: &CallGraph,
) -> Result<String> {
CodeGenerator::do_pass((&self.ast, symbol_table, struct_graph, call_graph))
CodeGenerator::do_pass((&self.ast, symbol_table, struct_graph, call_graph, &self.ast.ast))
}

/// Runs the compiler stages.
Expand Down
14 changes: 8 additions & 6 deletions compiler/compiler/tests/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,17 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
.map(|input| console::program::Value::<Network>::from_str(input.as_str().unwrap()).unwrap())
.collect();
let input_string = format!("[{}]", inputs.iter().map(|input| input.to_string()).join(", "));
let private_key = match case.get(&Value::from("private_key")) {
Some(private_key) => {
PrivateKey::from_str(private_key.as_str().expect("expected string for private key"))
.expect("unable to parse private key")
}
None => dotenv_private_key(package.directory()).unwrap(),
};

// TODO: Add support for custom config like custom private keys.
// Execute the program and get the outputs.
let output_string = match package.run::<Aleo, _>(
&dotenv_private_key(package.directory()).unwrap(),
function_name,
&inputs,
rng,
) {
let output_string = match package.run::<Aleo, _>(&private_key, function_name, &inputs, rng) {
Ok((response, _)) => format!(
"[{}]",
response
Expand Down
2 changes: 1 addition & 1 deletion compiler/compiler/tests/utilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ pub fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<String, L
parsed.dead_code_elimination_pass()?;

// Compile Leo program to bytecode.
let bytecode = CodeGenerator::do_pass((&parsed.ast, &st, &struct_graph, &call_graph))?;
let bytecode = CodeGenerator::do_pass((&parsed.ast, &st, &struct_graph, &call_graph, &parsed.ast.ast))?;

Ok(bytecode)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/parser/src/parser/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl ParserContext<'_> {
false => {
parsed_program_scope = true;
let program_scope = self.parse_program_scope()?;
program_scopes.insert(program_scope.program_id, program_scope);
program_scopes.insert(program_scope.program_id.name.name, program_scope);
}
}
}
Expand Down
19 changes: 17 additions & 2 deletions compiler/passes/src/code_generation/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

use crate::{CallGraph, StructGraph, SymbolTable};

use leo_ast::Function;
use leo_ast::{Function, Program, ProgramId};
use leo_span::Symbol;

use indexmap::IndexMap;
Expand Down Expand Up @@ -44,11 +44,23 @@ pub struct CodeGenerator<'a> {
pub(crate) is_transition_function: bool,
/// Are we traversing a finalize block?
pub(crate) in_finalize: bool,
// TODO (@d0cd): There are a temporary solution to be compatible with futures introduced in Aleo instructions.
// The registers containing futures produced in the current transition.
pub(crate) futures: Vec<(String, String)>,
// A reference to program. This is needed to look up external programs.
pub(crate) program: &'a Program,
// The program ID of the current program.
pub(crate) program_id: Option<ProgramId>,
}

impl<'a> CodeGenerator<'a> {
/// Initializes a new `CodeGenerator`.
pub fn new(symbol_table: &'a SymbolTable, struct_graph: &'a StructGraph, _call_graph: &'a CallGraph) -> Self {
pub fn new(
symbol_table: &'a SymbolTable,
struct_graph: &'a StructGraph,
_call_graph: &'a CallGraph,
program: &'a Program,
) -> Self {
// Initialize variable mapping.
Self {
symbol_table,
Expand All @@ -61,6 +73,9 @@ impl<'a> CodeGenerator<'a> {
global_mapping: IndexMap::new(),
is_transition_function: false,
in_finalize: false,
futures: Vec::new(),
program,
program_id: None,
}
}
}
8 changes: 4 additions & 4 deletions compiler/passes/src/code_generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ mod visit_type;

use crate::{CallGraph, Pass, StructGraph, SymbolTable};

use leo_ast::Ast;
use leo_ast::{Ast, Program};
use leo_errors::Result;

impl<'a> Pass for CodeGenerator<'a> {
type Input = (&'a Ast, &'a SymbolTable, &'a StructGraph, &'a CallGraph);
type Input = (&'a Ast, &'a SymbolTable, &'a StructGraph, &'a CallGraph, &'a Program);
type Output = Result<String>;

fn do_pass((ast, symbol_table, struct_graph, call_graph): Self::Input) -> Self::Output {
let mut generator = Self::new(symbol_table, struct_graph, call_graph);
fn do_pass((ast, symbol_table, struct_graph, call_graph, program): Self::Input) -> Self::Output {
let mut generator = Self::new(symbol_table, struct_graph, call_graph, program);
let bytecode = generator.visit_program(ast.as_repr());

Ok(bytecode)
Expand Down
86 changes: 66 additions & 20 deletions compiler/passes/src/code_generation/visit_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,36 @@ impl<'a> CodeGenerator<'a> {

// TODO: Cleanup
fn visit_call(&mut self, input: &'a CallExpression) -> (String, String) {
let mut call_instruction = match &input.external {
Some(external) => format!(" call {external}.aleo/{}", input.function),
None => format!(" call {}", input.function),
let (mut call_instruction, has_finalize) = match &input.external {
Some(external) => {
// If the function is an external call, then check whether or not it has an associated finalize block.
// Extract the program name from the external call.
let program_name = match **external {
Expression::Identifier(identifier) => identifier.name,
_ => unreachable!("Parsing guarantees that a program name is always an identifier."),
};
// Lookup the imported program scope.
let imported_program_scope = match self
.program
.imports
.get(&program_name)
.and_then(|(program, _)| program.program_scopes.get(&program_name))
{
Some(program) => program,
None => unreachable!("Type checking guarantees that imported programs are well defined."),
};
// Check if the external function has a finalize block.
let function_name = match *input.function {
Expression::Identifier(identifier) => identifier.name,
_ => unreachable!("Parsing guarantees that a function name is always an identifier."),
};
let has_finalize = match imported_program_scope.functions.get(&function_name) {
Some(function) => function.finalize.is_some(),
None => unreachable!("Type checking guarantees that imported functions are well defined."),
};
(format!(" call {external}.aleo/{}", input.function), has_finalize)
}
None => (format!(" call {}", input.function), false),
};
let mut instructions = String::new();

Expand All @@ -488,41 +515,60 @@ impl<'a> CodeGenerator<'a> {
Expression::Identifier(identifier) => identifier.name,
_ => unreachable!("Parsing guarantees that a function name is always an identifier."),
};

// Initialize storage for the destination registers.
let mut destinations = Vec::new();

let return_type = &self.symbol_table.lookup_fn_symbol(function_name).unwrap().output_type;
match return_type {
Type::Unit => {
call_instruction.push(';');
instructions.push_str(&call_instruction);
(String::new(), instructions)
} // Do nothing
Type::Unit => {} // Do nothing
Type::Tuple(tuple) => match tuple.len() {
0 | 1 => unreachable!("Parsing guarantees that a tuple type has at least two elements"),
len => {
let mut destinations = Vec::new();
for _ in 0..len {
let destination_register = format!("r{}", self.next_register);
destinations.push(destination_register);
self.next_register += 1;
}
let destinations = destinations.join(" ");
writeln!(call_instruction, " into {destinations};").expect("failed to write to string");
instructions.push_str(&call_instruction);

(destinations, instructions)
}
},
_ => {
// Push destination register to call instruction.
let destination_register = format!("r{}", self.next_register);
writeln!(call_instruction, " into {destination_register};").expect("failed to write to string");
instructions.push_str(&call_instruction);

// Increment the register counter.
destinations.push(destination_register);
self.next_register += 1;
}
}

(destination_register, instructions)
// If `has_finalize`, create another destination register for the future.
if has_finalize {
// Construct the future register.
let future_register = format!("r{}", self.next_register);
self.next_register += 1;

// Construct the future type.
let program_id = match input.external.as_deref() {
Some(Expression::Identifier(identifier)) => identifier,
_ => unreachable!("If `has_finalize` is true, then the external call must be an identifier."),
};
self.futures.push((future_register, format!("{program_id}/{function_name}")));
}

// If destination registers were created, write them to the call instruction.
if !destinations.is_empty() {
write!(call_instruction, " into").expect("failed to write to string");
for destination in &destinations {
write!(call_instruction, " {}", destination).expect("failed to write to string");
}
}

// Write the closing semicolon.
writeln!(call_instruction, ";").expect("failed to write to string");

// Push the call instruction to the list of instructions.
instructions.push_str(&call_instruction);

// Return the destination registers as a string and the instructions.
(destinations.join(" "), instructions)
}

fn visit_tuple(&mut self, input: &'a TupleExpression) -> (String, String) {
Expand Down
23 changes: 21 additions & 2 deletions compiler/passes/src/code_generation/visit_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ impl<'a> CodeGenerator<'a> {
// Note that type checking guarantees that there is exactly one program scope.
let program_scope: &ProgramScope = input.program_scopes.values().next().unwrap();

self.program_id = Some(program_scope.program_id);

// Print the program id.
writeln!(program_string, "program {};", program_scope.program_id)
.expect("Failed to write program id to string.");
Expand Down Expand Up @@ -157,6 +159,7 @@ impl<'a> CodeGenerator<'a> {
// Initialize the state of `self` with the appropriate values before visiting `function`.
self.next_register = 0;
self.variable_mapping = IndexMap::new();
self.futures.clear();
// TODO: Figure out a better way to initialize.
self.variable_mapping.insert(&sym::SelfLower, "self".to_string());
self.variable_mapping.insert(&sym::block, "block".to_string());
Expand Down Expand Up @@ -214,6 +217,17 @@ impl<'a> CodeGenerator<'a> {

function_string.push_str(&format!("\nfinalize {}:\n", finalize.identifier));

// If the function contained calls that produced futures, then we need to add the futures to the finalize block as input.
// Store the new future registers.
let mut future_registers = Vec::new();
for (_, future_type) in &self.futures {
let register_string = format!("r{}", self.next_register);
writeln!(function_string, " input {register_string} as {future_type};")
.expect("failed to write to string");
future_registers.push(register_string);
self.next_register += 1;
}

// Construct and append the input declarations of the finalize block.
for input in finalize.input.iter() {
let register_string = format!("r{}", self.next_register);
Expand All @@ -240,6 +254,11 @@ impl<'a> CodeGenerator<'a> {
.expect("failed to write to string");
}

// Invoke `await` on each future.
for register in future_registers {
writeln!(function_string, " await {register};").expect("failed to write to string");
}

// Construct and append the finalize block body.
function_string.push_str(&self.visit_block(&finalize.block));

Expand Down Expand Up @@ -273,10 +292,10 @@ impl<'a> CodeGenerator<'a> {
};

// Create the key string, e.g. ` key as address.public`.
mapping_string.push_str(&format!("\tkey left as {};\n", create_type(&mapping.key_type)));
mapping_string.push_str(&format!("\tkey as {};\n", create_type(&mapping.key_type)));

// Create the value string, e.g. ` value as address.public`.
mapping_string.push_str(&format!("\tvalue right as {};\n", create_type(&mapping.value_type)));
mapping_string.push_str(&format!("\tvalue as {};\n", create_type(&mapping.value_type)));

// Add the mapping to the variable mapping.
self.global_mapping.insert(&mapping.identifier.name, mapping.identifier.to_string());
Expand Down
52 changes: 39 additions & 13 deletions compiler/passes/src/code_generation/visit_statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl<'a> CodeGenerator<'a> {
}

fn visit_return(&mut self, input: &'a ReturnStatement) -> String {
let mut instructions = match input.expression {
let mut outputs = match input.expression {
// Skip empty return statements.
Expression::Unit(_) => String::new(),
_ => {
Expand Down Expand Up @@ -141,21 +141,47 @@ impl<'a> CodeGenerator<'a> {
}
};

// Output a finalize instruction if needed.
// TODO: Check formatting.
if let Some(arguments) = &input.finalize_arguments {
let mut finalize_instruction = "\n finalize".to_string();

for argument in arguments.iter() {
let (argument, argument_instructions) = self.visit_expression(argument);
write!(finalize_instruction, " {argument}").expect("failed to write to string");
instructions.push_str(&argument_instructions);
// Initialize storage for the instructions.
let mut instructions = String::new();

// If there are any futures or if the return instruction has `finalize_arguments`, then
// create an `async` instruction that uses them.
if !self.futures.is_empty() || input.finalize_arguments.is_some() {
// Note that this unwrap is safe, since `current_function` is set in `visit_function`.
let function_id = self.current_function.unwrap().name();
let mut async_instruction = format!(" async {function_id}");
// Add the futures to the async instruction.
for (future_register, _) in self.futures.iter() {
write!(async_instruction, " {}", future_register).expect("failed to write to string");
}
writeln!(finalize_instruction, ";").expect("failed to write to string");

instructions.push_str(&finalize_instruction);
// Add the finalize arguments to the async instruction.
if let Some(arguments) = &input.finalize_arguments {
for argument in arguments.iter() {
let (argument, argument_instructions) = self.visit_expression(argument);
write!(async_instruction, " {argument}").expect("failed to write to string");
instructions.push_str(&argument_instructions);
}
}
// Write the destination register.
let destination_register = format!("r{}", self.next_register);
write!(async_instruction, " into {};", destination_register).expect("failed to write to string");
// Increment the register counter.
self.next_register += 1;
// Add the async instruction to the instructions.
instructions.push_str(&async_instruction);

// Add the destination register to the outputs.
let program_id = match self.program_id {
Some(program_id) => program_id,
None => unreachable!("`program_id` should be set in `visit_function`"),
};
outputs
.push_str(&format!(" output {} as {}/{}.future;\n", destination_register, program_id, function_id));
}

// Extend the instructions with the outputs.
instructions.push_str(&outputs);

instructions
}

Expand Down
10 changes: 10 additions & 0 deletions compiler/passes/src/type_checking/check_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
return Some(Type::Address);
}
sym::signer => {
// Check that operation is not invoked in a `finalize` block.
if self.is_finalize {
self.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(
"self.signer",
access.name.span(),
))
}
return Some(Type::Address);
}
_ => {
self.emit_err(TypeCheckerError::invalid_self_access(access.name.span()));
}
Expand Down
1 change: 1 addition & 0 deletions compiler/span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ symbols! {
Return: "return",
SelfLower: "self",
SelfUpper: "Self",
signer,
Star: "*",
then,
transition,
Expand Down
Loading

0 comments on commit 1bfcfac

Please sign in to comment.