usage changes

This commit is contained in:
evan-schott 2024-01-11 13:12:59 -08:00
parent c3c5ff152e
commit 278f59bba7
11 changed files with 57 additions and 36 deletions

View File

@ -434,7 +434,11 @@ pub trait ProgramReconstructor: StatementReconstructor {
structs: input.structs,
mappings: input.mappings,
span: input.span,
functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function_stub(f))).collect(),
functions: input
.functions
.into_iter()
.map(|(i, f)| (i, self.reconstruct_function_stub(f, input.stub_id)))
.collect(),
}
}
@ -479,7 +483,7 @@ pub trait ProgramReconstructor: StatementReconstructor {
}
}
fn reconstruct_function_stub(&mut self, input: FunctionStub) -> FunctionStub {
fn reconstruct_function_stub(&mut self, input: FunctionStub, _program: ProgramId) -> FunctionStub {
input
}

View File

@ -19,6 +19,7 @@
//! given the type of node its visiting.
use crate::*;
use leo_span::Symbol;
/// A Visitor trait for expressions in the AST.
pub trait ExpressionVisitor<'a> {
@ -253,7 +254,7 @@ pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
}
}
fn visit_function_stub(&mut self, _input: &'a FunctionStub) {}
fn visit_function_stub(&mut self, _input: &'a FunctionStub, _program: ProgramId) {}
fn visit_struct_stub(&mut self, _input: &'a Struct) {}
}

View File

@ -17,6 +17,7 @@
use crate::Identifier;
use core::fmt;
use leo_span::Symbol;
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use snarkvm::{console::program::ProgramID, prelude::Network};
use std::collections::BTreeMap;
@ -99,3 +100,12 @@ impl<N: Network> From<&ProgramID<N>> for ProgramId {
Self { name: Identifier::from(program.name()), network: Identifier::from(program.network()) }
}
}
impl From<Identifier> for ProgramId {
fn from(name: Identifier) -> Self {
Self {
name,
network: Identifier { name: Symbol::intern("aleo"), span: Default::default(), id: Default::default() },
}
}
}

View File

@ -106,7 +106,7 @@ impl<'a> ExpressionVisitor<'a> for CheckUniqueNodeIds<'a> {
self.visit_expression(argument, &Default::default());
}
if let Some(external) = external {
self.visit_expression(external, &Default::default());
self.visit_identifier(&external.name, &Default::default());
}
self.check(*id);
}

View File

@ -429,7 +429,7 @@ impl ParserContext<'_> {
self.parse_paren_comma_list(|p| p.parse_expression().map(Some))
}
// Parses an externa function call `credits.aleo/transfer()` or `board.leo/make_move()`
// Parses an external function call `credits.aleo/transfer()` or `board.leo/make_move()`
fn parse_external_call(&mut self, expr: Expression) -> Result<Expression> {
// Eat an external function call.
self.eat(&Token::Div); // todo: Make `/` a more general token.
@ -439,10 +439,17 @@ impl ParserContext<'_> {
// Parse the function call.
let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?;
// Parse the parent program identifier.
let program: Option<ProgramId> = match expr {
Expression::Identifier(identifier) => Some(ProgramId::from(identifier)),
_ => unreachable!("Function called must be preceded by a program identifier."),
};
Ok(Expression::Call(CallExpression {
span: expr.span() + span,
function: Box::new(Expression::Identifier(name)),
external: Some(Box::new(expr)),
external: program,
arguments,
id: self.node_builder.next_id(),
}))

View File

@ -39,7 +39,7 @@ use leo_ast::{
UnaryOperation,
UnitExpression,
};
use leo_span::sym;
use leo_span::{sym, Symbol};
use std::borrow::Borrow;
use std::fmt::Write as _;
@ -524,10 +524,7 @@ impl<'a> CodeGenerator<'a> {
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."),
};
let program_name: Symbol = external.name.name;
let stub_scope: ProgramScope;
// Lookup the imported program scope.
// TODO: Needs refactor. All imports are stubs now.
@ -557,7 +554,7 @@ impl<'a> CodeGenerator<'a> {
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)
(format!(" call {external}/{}", input.function), has_finalize)
}
None => (format!(" call {}", input.function), false),
};
@ -578,7 +575,7 @@ impl<'a> CodeGenerator<'a> {
// 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;
let return_type = &self.symbol_table.lookup_fn_symbol(function_name, input.external).unwrap().output_type;
match return_type {
Type::Unit => {} // Do nothing
Type::Tuple(tuple) => match tuple.length() {
@ -607,14 +604,9 @@ impl<'a> CodeGenerator<'a> {
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."),
};
// Add the futures register to the list of futures.
self.futures.push((future_register.clone(), format!("{program_id}.aleo/{function_name}")));
self.futures
.push((future_register.clone(), format!("{}.aleo/{function_name}", input.external.unwrap().name)));
// Add the future register to the list of destinations.
destinations.push(future_register);

View File

@ -35,10 +35,11 @@ impl ProgramReconstructor for Unroller<'_> {
}
// Don't need to reconstruct anything, just need to add child scopes for constant propagation table
fn reconstruct_function_stub(&mut self, input: FunctionStub) -> FunctionStub {
fn reconstruct_function_stub(&mut self, input: FunctionStub, program: ProgramId) -> FunctionStub {
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.
let function_index = self.symbol_table.borrow().lookup_fn_symbol(input.identifier.name).unwrap().id;
let function_index =
self.symbol_table.borrow().lookup_fn_symbol(input.identifier.name, Some(program)).unwrap().id;
// Enter the function's scope.
let previous_function_index = self.enter_scope(function_index);
@ -52,7 +53,7 @@ impl ProgramReconstructor for Unroller<'_> {
fn reconstruct_function(&mut self, function: Function) -> Function {
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.
let function_index = self.symbol_table.borrow().lookup_fn_symbol(function.identifier.name).unwrap().id;
let function_index = self.symbol_table.borrow().lookup_fn_symbol(function.identifier.name, None).unwrap().id;
// Enter the function's scope.
let previous_function_index = self.enter_scope(function_index);

View File

@ -68,19 +68,20 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
}
fn visit_function(&mut self, input: &'a Function) {
if let Err(err) = self.symbol_table.insert_fn(input.name(), input) {
if let Err(err) = self.symbol_table.insert_fn(input.name(), None, input) {
self.handler.emit_err(err);
}
}
fn visit_stub(&mut self, input: &'a Stub) {
input.functions.iter().for_each(|(_, c)| (self.visit_function_stub(c)));
input.functions.iter().for_each(|(_, c)| (self.visit_function_stub(c, input.stub_id)));
input.structs.iter().for_each(|(_, c)| (self.visit_struct(c)));
}
fn visit_function_stub(&mut self, input: &'a FunctionStub) {
if let Err(err) = self.symbol_table.insert_fn(input.name(), &Function::from(input.clone())) {
fn visit_function_stub(&mut self, input: &'a FunctionStub, program_name: ProgramId) {
if let Err(err) = self.symbol_table.insert_fn(input.name(), Some(program_name), &Function::from(input.clone()))
{
self.handler.emit_err(err);
}
}

View File

@ -566,8 +566,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Expression::Identifier(ident) => {
// Note: The function symbol lookup is performed outside of the `if let Some(func) ...` block to avoid a RefCell lifetime bug in Rust.
// Do not move it into the `if let Some(func) ...` block or it will keep `self.symbol_table_creation` alive for the entire block and will be very memory inefficient!
let func = self.symbol_table.borrow().lookup_fn_symbol(ident.name).cloned();
let func = self.symbol_table.borrow().lookup_fn_symbol(ident.name, input.external).cloned();
if let Some(func) = func {
// Check that the call is valid.
// Note that this unwrap is safe since we always set the variant before traversing the body of the function.
@ -614,7 +613,12 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
None => unreachable!("`self.function` is set every time a function is visited."),
Some(func) => func,
};
self.call_graph.add_edge(caller_name, ident.name);
// Don't add external functions to call graph.
// We check that there is no dependency cycle of imports, so we know that external functions can never lead to a call graph cycle
if input.external.is_none() {
self.call_graph.add_edge(caller_name, ident.name);
}
Some(ret)
} else {

View File

@ -55,10 +55,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
input.structs.iter().for_each(|(_, function)| self.visit_struct_stub(function));
// Typecheck the program's functions.
input.functions.iter().for_each(|(_, function)| self.visit_function_stub(function));
input.functions.iter().for_each(|(_, function)| self.visit_function_stub(function, input.stub_id));
}
fn visit_function_stub(&mut self, input: &'a FunctionStub) {
fn visit_function_stub(&mut self, input: &'a FunctionStub, program: ProgramId) {
// Must not be an inline function
if input.variant == Variant::Inline {
self.emit_err(TypeCheckerError::stub_functions_must_not_be_inlines(input.span));
@ -66,7 +66,8 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.
let function_index = self.symbol_table.borrow().lookup_fn_symbol(input.identifier.name).unwrap().id;
let function_index =
self.symbol_table.borrow().lookup_fn_symbol(input.identifier.name, Some(program)).unwrap().id;
// Enter the function's scope.
self.enter_scope(function_index);
@ -269,7 +270,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.
let function_index = self.symbol_table.borrow().lookup_fn_symbol(function.identifier.name).unwrap().id;
let function_index = self.symbol_table.borrow().lookup_fn_symbol(function.identifier.name, None).unwrap().id;
// Enter the function's scope.
self.enter_scope(function_index);

View File

@ -372,7 +372,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// We can safely unwrap all self.parent instances because
// statements should always have some parent block
let parent = self.function.unwrap();
let return_type = &self.symbol_table.borrow().lookup_fn_symbol(parent).map(|f| match self.is_finalize {
let return_type = &self.symbol_table.borrow().lookup_fn_symbol(parent, None).map(|f| match self.is_finalize {
// TODO: Check this.
// Note that this `unwrap()` is safe since we checked that the function has a finalize block.
true => f.finalize.as_ref().unwrap().output_type.clone(),
@ -410,7 +410,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// Note that `self.function.unwrap()` is safe since every `self.function` is set for every function.
// Note that `(self.function.unwrap()).unwrap()` is safe since all functions have been checked to exist.
let finalize =
self.symbol_table.borrow().lookup_fn_symbol(self.function.unwrap()).unwrap().finalize.clone();
self.symbol_table.borrow().lookup_fn_symbol(self.function.unwrap(), None).unwrap().finalize.clone();
match finalize {
None => self.emit_err(TypeCheckerError::finalize_without_finalize_block(input.span())),
Some(finalize) => {