This commit is contained in:
evan-schott 2024-03-28 11:06:45 -07:00
parent e78b292552
commit ff4edada7e
18 changed files with 193 additions and 121 deletions

View File

@ -58,7 +58,7 @@ impl LocatorExpression {
/// Check if the Locator name and program matches the other name and program.
pub fn matches(&self, other: &Self) -> bool {
self.name == other.name && self.program == other.program
}
}
}
impl fmt::Display for LocatorExpression {
@ -134,7 +134,7 @@ impl<'de> Deserialize<'de> for LocatorExpression {
Some(name) => Symbol::intern(name),
None => return Err(E::custom("missing 'name' in serialized Identifier struct")),
};
let program = match key.get("program") {
Some(program) => Symbol::intern(program),
None => return Err(E::custom("missing 'program' in serialized Identifier struct")),

View File

@ -61,7 +61,7 @@ pub trait ExpressionConsumer {
fn consume_identifier(&mut self, _input: Identifier) -> Self::Output;
fn consume_literal(&mut self, _input: Literal) -> Self::Output;
fn consume_locator(&mut self, _input: LocatorExpression) -> Self::Output;
fn consume_ternary(&mut self, _input: TernaryExpression) -> Self::Output;

View File

@ -198,12 +198,9 @@ pub trait ExpressionReconstructor {
fn reconstruct_literal(&mut self, input: Literal) -> (Expression, Self::AdditionalOutput) {
(Expression::Literal(input), Default::default())
}
fn reconstruct_locator(&mut self, input: LocatorExpression) -> (Expression, Self::AdditionalOutput) {
(
Expression::Locator(input),
Default::default(),
)
(Expression::Locator(input), Default::default())
}
fn reconstruct_ternary(&mut self, input: TernaryExpression) -> (Expression, Self::AdditionalOutput) {

View File

@ -107,8 +107,8 @@ pub trait ExpressionVisitor<'a> {
fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_locator(&mut self, input: &'a LocatorExpression, additional: &Self::AdditionalInput) -> Self::Output {
fn visit_locator(&mut self, _input: &'a LocatorExpression, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}

View File

@ -16,9 +16,9 @@
use crate::Type;
use leo_span::Symbol;
use serde::{Deserialize, Serialize};
use std::fmt;
use leo_span::Symbol;
/// A mapping type of a key and value type.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]

View File

@ -494,7 +494,6 @@ impl ParserContext<'_> {
} else if self.eat(&Token::Leo) {
return Err(ParserError::only_aleo_external_calls(expr.span()).into());
} else if self.eat(&Token::Aleo) {
expr = self.parse_external_resource(expr)?;
} else {
// Parse identifier name.

View File

@ -15,7 +15,31 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{CodeGenerator, Location};
use leo_ast::{AccessExpression, ArrayAccess, ArrayExpression, AssociatedConstant, AssociatedFunction, BinaryExpression, BinaryOperation, CallExpression, CastExpression, ErrExpression, Expression, Identifier, Literal, LocatorExpression, MemberAccess, ProgramScope, StructExpression, TernaryExpression, TupleExpression, Type, UnaryExpression, UnaryOperation, UnitExpression};
use leo_ast::{
AccessExpression,
ArrayAccess,
ArrayExpression,
AssociatedConstant,
AssociatedFunction,
BinaryExpression,
BinaryOperation,
CallExpression,
CastExpression,
ErrExpression,
Expression,
Identifier,
Literal,
LocatorExpression,
MemberAccess,
ProgramScope,
StructExpression,
TernaryExpression,
TupleExpression,
Type,
UnaryExpression,
UnaryOperation,
UnitExpression,
};
use leo_span::sym;
use std::borrow::Borrow;
@ -515,7 +539,8 @@ impl<'a> CodeGenerator<'a> {
// Initialize storage for the destination registers.
let mut destinations = Vec::new();
let return_type = &self.symbol_table.lookup_fn_symbol(Location::new(Some(main_program), function_name)).unwrap().output_type;
let return_type =
&self.symbol_table.lookup_fn_symbol(Location::new(Some(main_program), function_name)).unwrap().output_type;
match return_type {
Type::Unit => {} // Do nothing
Type::Tuple(tuple) => match tuple.length() {

View File

@ -37,8 +37,8 @@ impl Serialize for Location {
S: Serializer,
{
let condensed_str = match self.program {
Some(program) => format!("{}/{}", program, self.name),
None => format!("{}", self.name),
Some(program) => format!("{}/{}", program, self.name),
None => format!("{}", self.name),
};
serializer.serialize_str(&condensed_str)
}
@ -51,12 +51,8 @@ impl<'de> Deserialize<'de> for Location {
{
let s = String::deserialize(deserializer)?;
let mut parts: Vec<&str> = s.split('/').collect();
let program = if parts.len() == 1 {
None
} else {
Some(Symbol::intern(parts.remove(0)))
};
let name = Symbol::intern(parts.get(0).unwrap());
let program = if parts.len() == 1 { None } else { Some(Symbol::intern(parts.remove(0))) };
let name = Symbol::intern(parts.first().unwrap());
Ok(Location::new(program, name))
}
}

View File

@ -28,7 +28,7 @@ use std::cell::RefCell;
use leo_ast::{normalize_json_value, remove_key_from_json, Composite, Function};
use leo_errors::{AstError, Result};
use leo_span::{Span, Symbol};
use leo_span::Span;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
@ -60,7 +60,7 @@ impl SymbolTable {
/// Recursively checks if the symbol table contains an entry for the given symbol.
/// Leo does not allow any variable shadowing or overlap between different symbols.
pub fn check_shadowing(&self, location: &Location, span: Span) -> Result<()> {
if let Some(_) = location.program {
if location.program.is_some() {
if self.functions.contains_key(location) {
return Err(AstError::shadowed_function(location.name, span).into());
} else if let Some(existing) = self.structs.get(location) {
@ -68,16 +68,11 @@ impl SymbolTable {
true => Err(AstError::shadowed_record(location.name, span).into()),
false => Err(AstError::shadowed_struct(location.name, span).into()),
};
} else if self.variables.contains_key(location) {
return Err(AstError::shadowed_variable(location.name, span).into());
}
else if self.variables.contains_key(location) {
return Err(AstError::shadowed_variable(location.name, span).into())
}
}
if let Some(parent) = self.parent.as_ref() {
return parent.check_shadowing(location, span)
} else {
Ok(())
}
if let Some(parent) = self.parent.as_ref() { parent.check_shadowing(location, span) } else { Ok(()) }
}
/// Returns the current scope index.
@ -244,30 +239,29 @@ mod tests {
block: Default::default(),
};
symbol_table.insert_fn(func_loc, &insert).unwrap();
symbol_table.insert_variable(
Location::new(Some(Symbol::intern("credits")), Symbol::intern("accounts")),
VariableSymbol {
type_: Type::Address,
span: Default::default(),
declaration: VariableType::Const,
}).unwrap();
symbol_table.insert_struct(
Location::new(Some(Symbol::intern("credits")), Symbol::intern("token")),
&Composite {
symbol_table
.insert_variable(
Location::new(Some(Symbol::intern("credits")), Symbol::intern("accounts")),
VariableSymbol { type_: Type::Address, span: Default::default(), declaration: VariableType::Const },
)
.unwrap();
symbol_table
.insert_struct(Location::new(Some(Symbol::intern("credits")), Symbol::intern("token")), &Composite {
is_record: false,
span: Default::default(),
id: 0,
identifier: Identifier::new(Symbol::intern("token"), Default::default()),
members: Vec::new(),
external: None,
}).unwrap();
symbol_table.insert_variable(
Location::new(None, Symbol::intern("foo")),
VariableSymbol {
})
.unwrap();
symbol_table
.insert_variable(Location::new(None, Symbol::intern("foo")), VariableSymbol {
type_: Type::Address,
span: Default::default(),
declaration: VariableType::Const,
}).unwrap();
})
.unwrap();
let json = symbol_table.to_json_string().unwrap();
let deserialized = SymbolTable::from_json_string(&json).unwrap();
assert_eq!(symbol_table, deserialized);

View File

@ -89,8 +89,10 @@ impl ExpressionReconstructor for Flattener<'_> {
Type::Array(first_type) => self.ternary_array(first_type, &input.condition, &first, &second),
Type::Composite(first_type) => {
// Get the struct definitions.
let first_type =
self.symbol_table.lookup_struct(Location::new(first_type.program, first_type.id.name)).unwrap();
let first_type = self
.symbol_table
.lookup_struct(Location::new(first_type.program, first_type.id.name))
.unwrap();
self.ternary_struct(first_type, &input.condition, &first, &second)
}
Type::Tuple(first_type) => self.ternary_tuple(first_type, &input.condition, &first, &second),

View File

@ -19,7 +19,7 @@ use leo_ast::{Expression::Literal, Type::Integer, *};
use leo_errors::loop_unroller::LoopUnrollerError;
use leo_span::{Span, Symbol};
use crate::{Location, unroller::Unroller, VariableSymbol, VariableType};
use crate::{unroller::Unroller, Location, VariableSymbol, VariableType};
impl StatementReconstructor for Unroller<'_> {
fn reconstruct_block(&mut self, input: Block) -> (Block, Self::AdditionalOutput) {
@ -60,7 +60,7 @@ impl StatementReconstructor for Unroller<'_> {
}
// Remove from symbol table
self.symbol_table.borrow_mut().remove_variable_from_current_scope(Location::new(None,input.place.name));
self.symbol_table.borrow_mut().remove_variable_from_current_scope(Location::new(None, input.place.name));
(
Statement::Const(ConstDeclaration {
@ -77,11 +77,13 @@ impl StatementReconstructor for Unroller<'_> {
fn reconstruct_definition(&mut self, input: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
// Helper function to add variables to symbol table
let insert_variable = |symbol: Symbol, type_: Type, span: Span| {
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(Location::new(None, symbol), VariableSymbol {
type_,
span,
declaration: VariableType::Mut,
}) {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(Location::new(None, symbol), VariableSymbol {
type_,
span,
declaration: VariableType::Mut,
})
{
self.handler.emit_err(err);
}
};

View File

@ -16,7 +16,30 @@
use crate::{Location, StaticSingleAssigner};
use leo_ast::{AccessExpression, ArrayAccess, ArrayExpression, AssociatedFunction, BinaryExpression, CallExpression, CastExpression, Composite, Expression, ExpressionConsumer, Identifier, Literal, LocatorExpression, MemberAccess, Statement, StructExpression, StructVariableInitializer, TernaryExpression, TupleAccess, TupleExpression, UnaryExpression, UnitExpression};
use leo_ast::{
AccessExpression,
ArrayAccess,
ArrayExpression,
AssociatedFunction,
BinaryExpression,
CallExpression,
CastExpression,
Composite,
Expression,
ExpressionConsumer,
Identifier,
Literal,
LocatorExpression,
MemberAccess,
Statement,
StructExpression,
StructVariableInitializer,
TernaryExpression,
TupleAccess,
TupleExpression,
UnaryExpression,
UnitExpression,
};
use leo_span::{sym, Symbol};
use indexmap::IndexMap;
@ -295,14 +318,13 @@ impl ExpressionConsumer for StaticSingleAssigner<'_> {
let (place, statement) = self.unique_simple_assign_statement(Expression::Literal(input));
(Expression::Identifier(place), vec![statement])
}
/// Consumes and returns the locator expression without making any modifciations
/// Consumes and returns the locator expression without making any modifciations
fn consume_locator(&mut self, input: LocatorExpression) -> Self::Output {
// Construct and accumulate a new assignment statement for the locator expression.
let (place, statement) = self.unique_simple_assign_statement(Expression::Locator(input));
(Expression::Identifier(place), vec![statement])
}
/// Consumes a ternary expression, accumulating any statements that are generated.
fn consume_ternary(&mut self, input: TernaryExpression) -> Self::Output {

View File

@ -72,20 +72,22 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
fn visit_mapping(&mut self, input: &'a Mapping) {
// Check if mapping is external.
let program = match self.is_stub {
let program = match self.is_stub {
true => self.program_name,
false => None,
};
// Add the variable associated with the mapping to the symbol table.
if let Err(err) = self.symbol_table.insert_variable(Location::new(program, input.identifier.name), VariableSymbol {
type_: Type::Mapping(MappingType {
key: Box::new(input.key_type.clone()),
value: Box::new(input.value_type.clone()),
program: self.program_name.unwrap(),
}),
span: input.span,
declaration: VariableType::Mut,
}) {
if let Err(err) =
self.symbol_table.insert_variable(Location::new(program, input.identifier.name), VariableSymbol {
type_: Type::Mapping(MappingType {
key: Box::new(input.key_type.clone()),
value: Box::new(input.value_type.clone()),
program: self.program_name.unwrap(),
}),
span: input.span,
declaration: VariableType::Mut,
})
{
self.handler.emit_err(err);
}
}

View File

@ -53,7 +53,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Expression::Err(err) => self.visit_err(err, additional),
Expression::Identifier(identifier) => self.visit_identifier(identifier, additional),
Expression::Literal(literal) => self.visit_literal(literal, additional),
Expression::Locator(locator) => panic!("aiya"), // TODO: self.visit_locator(locator, additional),
Expression::Locator(locator) => self.visit_locator(locator, additional),
Expression::Ternary(ternary) => self.visit_ternary(ternary, additional),
Expression::Tuple(tuple) => self.visit_tuple(tuple, additional),
Expression::Unary(unary) => self.visit_unary(unary, additional),
@ -571,7 +571,8 @@ 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(Location::new(input.program, ident.name)).cloned();
let func =
self.symbol_table.borrow().lookup_fn_symbol(Location::new(input.program, ident.name)).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.
@ -650,7 +651,8 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
fn visit_struct_init(&mut self, input: &'a StructExpression, additional: &Self::AdditionalInput) -> Self::Output {
let struct_ = self.symbol_table.borrow().lookup_struct(Location::new(self.program_name, input.name.name)).cloned();
let struct_ =
self.symbol_table.borrow().lookup_struct(Location::new(self.program_name, input.name.name)).cloned();
if let Some(struct_) = struct_ {
// Check struct type name.
let ret = self.check_expected_struct(&struct_, additional, input.name.span());
@ -767,6 +769,16 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
})
}
fn visit_locator(&mut self, input: &'a LocatorExpression, expected: &Self::AdditionalInput) -> Self::Output {
// Check that the locator points to a valid resource in the ST.
if let Some(var) = self.symbol_table.borrow().lookup_variable(Location::new(Some(input.program), input.name)) {
Some(self.assert_and_return_type(var.type_.clone(), expected, input.span()))
} else {
self.emit_err(TypeCheckerError::unknown_sym("variable", input.name, input.span()));
None
}
}
fn visit_ternary(&mut self, input: &'a TernaryExpression, expected: &Self::AdditionalInput) -> Self::Output {
self.visit_expression(&input.condition, &Some(Type::Boolean));

View File

@ -69,8 +69,12 @@ 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(Location::new(self.program_name, input.identifier.name)).unwrap().id;
let function_index = self
.symbol_table
.borrow()
.lookup_fn_symbol(Location::new(self.program_name, input.identifier.name))
.unwrap()
.id;
// Enter the function's scope.
self.enter_scope(function_index);

View File

@ -68,7 +68,9 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
}
};
let var_type = if let Some(var) = self.symbol_table.borrow_mut().lookup_variable(Location::new(None, var_name.name)) {
let var_type = if let Some(var) =
self.symbol_table.borrow_mut().lookup_variable(Location::new(None, var_name.name))
{
match &var.declaration {
VariableType::Const => self.emit_err(TypeCheckerError::cannot_assign_to_const_var(var_name, var.span)),
VariableType::Input(Mode::Constant) => {
@ -192,11 +194,13 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
self.visit_expression(&input.value, &Some(input.type_.clone()));
// Add constants to symbol table so that any references to them in later statements will pass TC
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(Location::new(None, input.place.name), VariableSymbol {
type_: input.type_.clone(),
span: input.place.span,
declaration: VariableType::Const,
}) {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(Location::new(None, input.place.name), VariableSymbol {
type_: input.type_.clone(),
span: input.place.span,
declaration: VariableType::Const,
})
{
self.handler.emit_err(err);
}
}
@ -240,11 +244,13 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// TODO: Dedup with unrolling pass.
// Helper to insert the variables into the symbol table.
let insert_variable = |symbol: Symbol, type_: Type, span: Span| {
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(Location::new(None, symbol), VariableSymbol {
type_,
span,
declaration: VariableType::Mut,
}) {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(Location::new(None, symbol), VariableSymbol {
type_,
span,
declaration: VariableType::Mut,
})
{
self.handler.emit_err(err);
}
};
@ -308,11 +314,13 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
let scope_index = self.create_child_scope();
// Add the loop variable to the scope of the loop body.
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(Location::new(None, input.variable.name), VariableSymbol {
type_: input.type_.clone(),
span: input.span(),
declaration: VariableType::Const,
}) {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(Location::new(None, input.variable.name), VariableSymbol {
type_: input.type_.clone(),
span: input.span(),
declaration: VariableType::Const,
})
{
self.handler.emit_err(err);
}
@ -381,14 +389,15 @@ 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(Location::new(self.program_name, parent)).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(),
false => f.output_type.clone(),
}
});
let return_type =
&self.symbol_table.borrow().lookup_fn_symbol(Location::new(self.program_name, parent)).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(),
false => f.output_type.clone(),
}
});
// Set the `has_return` flag.
self.has_return = true;

View File

@ -976,10 +976,7 @@ impl<'a> TypeChecker<'a> {
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Cannot modify external mappings.
if mapping_type.program != self.program_name.unwrap() {
self.handler.emit_err(TypeCheckerError::cannot_modify_external_mapping(
"set",
function_span,
));
self.handler.emit_err(TypeCheckerError::cannot_modify_external_mapping("set", function_span));
}
// Check that the second argument matches the key type of the mapping.
self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1);
@ -1003,10 +1000,8 @@ impl<'a> TypeChecker<'a> {
if let Some(mapping_type) = self.assert_mapping_type(&arguments[0].0, arguments[0].1) {
// Cannot modify external mappings.
if mapping_type.program != self.program_name.unwrap() {
self.handler.emit_err(TypeCheckerError::cannot_modify_external_mapping(
"remove",
function_span,
));
self.handler
.emit_err(TypeCheckerError::cannot_modify_external_mapping("remove", function_span));
}
// Check that the second argument matches the key type of the mapping.
self.assert_type(&arguments[1].0, &mapping_type.key, arguments[1].1);
@ -1078,7 +1073,10 @@ impl<'a> TypeChecker<'a> {
pub(crate) fn check_duplicate_struct(&self, name: Symbol, program_1: Symbol, program_2: Symbol) -> bool {
// Make sure that both structs have been defined already.
let st = self.symbol_table.borrow();
let (struct_1, struct_2) = match (st.lookup_struct(Location::new(Some(program_1), name)), st.lookup_struct(Location::new(Some(program_2), name))) {
let (struct_1, struct_2) = match (
st.lookup_struct(Location::new(Some(program_1), name)),
st.lookup_struct(Location::new(Some(program_2), name)),
) {
(Some(struct_1), Some(struct_2)) => (struct_1, struct_2),
_ => return false,
};
@ -1148,7 +1146,11 @@ impl<'a> TypeChecker<'a> {
}
// Check that the named composite type has been defined.
Type::Composite(struct_)
if self.symbol_table.borrow().lookup_struct(Location::new(struct_.program, struct_.id.name)).is_none() =>
if self
.symbol_table
.borrow()
.lookup_struct(Location::new(struct_.program, struct_.id.name))
.is_none() =>
{
is_valid = false;
self.emit_err(TypeCheckerError::undefined_type(struct_.id.name, span));
@ -1181,8 +1183,10 @@ impl<'a> TypeChecker<'a> {
// Array elements cannot be records.
Type::Composite(struct_type) => {
// Look up the type.
if let Some(struct_) =
self.symbol_table.borrow().lookup_struct(Location::new(struct_type.program, struct_type.id.name))
if let Some(struct_) = self
.symbol_table
.borrow()
.lookup_struct(Location::new(struct_type.program, struct_type.id.name))
{
// Check that the type is not a record.
if struct_.is_record {
@ -1241,7 +1245,9 @@ impl<'a> TypeChecker<'a> {
// If the function is not a transition function, then it cannot have a record as input
if let Type::Composite(struct_) = input_var.type_() {
if let Some(val) = self.symbol_table.borrow().lookup_struct(Location::new(struct_.program, struct_.id.name)) {
if let Some(val) =
self.symbol_table.borrow().lookup_struct(Location::new(struct_.program, struct_.id.name))
{
if val.is_record && !matches!(function.variant, Variant::Transition) {
self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(input_var.span()));
}
@ -1250,13 +1256,14 @@ impl<'a> TypeChecker<'a> {
// Add non-stub inputs to the symbol table.
if !self.is_stub {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(Location::new(None, input_var.identifier().name), VariableSymbol {
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
Location::new(None, input_var.identifier().name),
VariableSymbol {
type_: input_var.type_(),
span: input_var.identifier().span(),
declaration: VariableType::Input(input_var.mode()),
})
{
},
) {
self.handler.emit_err(err);
}
}
@ -1349,13 +1356,14 @@ impl<'a> TypeChecker<'a> {
}
// Add non-stub inputs to the symbol table.
if !self.is_stub {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(Location::new(None, input_var.identifier().name), VariableSymbol {
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
Location::new(None, input_var.identifier().name),
VariableSymbol {
type_: input_var.type_(),
span: input_var.identifier().span(),
declaration: VariableType::Input(input_var.mode()),
})
{
},
) {
self.handler.emit_err(err);
}
}

View File

@ -768,7 +768,7 @@ create_messages!(
msg: format!("The definition for `{struct_}` in program `{program_1}.aleo` does not match the definition in program `{program_2}.aleo`"),
help: Some("Check that the struct definition in the current program matches the definition in the imported program.".to_string()),
}
@formatted
cannot_modify_external_mapping {
args: (operation: impl Display),