This commit is contained in:
evan-schott 2023-10-03 10:14:37 -07:00
parent c5464db066
commit 24c88795fe
35 changed files with 324 additions and 206 deletions

View File

@ -78,6 +78,7 @@ pub trait StatementConsumer {
Statement::Block(stmt) => self.consume_block(stmt),
Statement::Conditional(stmt) => self.consume_conditional(stmt),
Statement::Console(stmt) => self.consume_console(stmt),
Statement::Const(stmt) => self.consume_const(stmt),
Statement::Definition(stmt) => self.consume_definition(stmt),
Statement::Expression(stmt) => self.consume_expression_statement(stmt),
Statement::Iteration(stmt) => self.consume_iteration(*stmt),
@ -95,6 +96,8 @@ pub trait StatementConsumer {
fn consume_console(&mut self, input: ConsoleStatement) -> Self::Output;
fn consume_const(&mut self, input: ConstDeclaration) -> Self::Output;
fn consume_definition(&mut self, input: DefinitionStatement) -> Self::Output;
fn consume_expression_statement(&mut self, input: ExpressionStatement) -> Self::Output;

View File

@ -202,6 +202,7 @@ pub trait StatementReconstructor: ExpressionReconstructor {
}
Statement::Conditional(stmt) => self.reconstruct_conditional(stmt),
Statement::Console(stmt) => self.reconstruct_console(stmt),
Statement::Const(stmt) => self.reconstruct_const(stmt),
Statement::Definition(stmt) => self.reconstruct_definition(stmt),
Statement::Expression(stmt) => self.reconstruct_expression_statement(stmt),
Statement::Iteration(stmt) => self.reconstruct_iteration(*stmt),
@ -287,6 +288,19 @@ pub trait StatementReconstructor: ExpressionReconstructor {
)
}
fn reconstruct_const(&mut self, input: ConstDeclaration) -> (Statement, Self::AdditionalOutput) {
(
Statement::Const(ConstDeclaration {
place: input.place,
type_: input.type_,
value: self.reconstruct_expression(input.value).0,
span: input.span,
id: input.id,
}),
Default::default(),
)
}
fn reconstruct_definition(&mut self, input: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
(
Statement::Definition(DefinitionStatement {
@ -368,7 +382,7 @@ pub trait ProgramReconstructor: StatementReconstructor {
structs: input.structs.into_iter().map(|(i, c)| (i, self.reconstruct_struct(c))).collect(),
mappings: input.mappings.into_iter().map(|(id, mapping)| (id, self.reconstruct_mapping(mapping))).collect(),
functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function(f))).collect(),
consts: input.consts.into_iter().map(|(i, c)| (i, self.reconstruct_const(c))).collect(),
consts: input.consts.into_iter().map(|(i, c)| (i, self.reconstruct_global_const(c))).collect(),
span: input.span,
}
}
@ -396,7 +410,7 @@ pub trait ProgramReconstructor: StatementReconstructor {
}
}
fn reconstruct_const(&mut self, input: DefinitionStatement) -> DefinitionStatement {
fn reconstruct_global_const(&mut self, input: ConstDeclaration) -> ConstDeclaration {
input
}

View File

@ -128,6 +128,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
Statement::Block(stmt) => self.visit_block(stmt),
Statement::Conditional(stmt) => self.visit_conditional(stmt),
Statement::Console(stmt) => self.visit_console(stmt),
Statement::Const(stmt) => self.visit_const(stmt),
Statement::Definition(stmt) => self.visit_definition(stmt),
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
Statement::Iteration(stmt) => self.visit_iteration(stmt),
@ -177,6 +178,10 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
};
}
fn visit_const(&mut self, input: &'a ConstDeclaration) {
self.visit_expression(&input.value, &Default::default());
}
fn visit_definition(&mut self, input: &'a DefinitionStatement) {
self.visit_expression(&input.value, &Default::default());
}
@ -233,6 +238,4 @@ pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
self.visit_block(&finalize.block);
}
}
fn visit_const(&mut self, _input: &'a DefinitionStatement) {}
}

View File

@ -16,7 +16,7 @@
//! A Leo program scope consists of struct, function, and mapping definitions.
use crate::{DefinitionStatement, Function, Mapping, ProgramId, Struct};
use crate::{ConstDeclaration, Function, Mapping, ProgramId, Struct};
use indexmap::IndexMap;
use leo_span::{Span, Symbol};
@ -29,7 +29,7 @@ pub struct ProgramScope {
/// The program id of the program scope.
pub program_id: ProgramId,
/// A map from const names to const definitions.
pub consts: IndexMap<Symbol, DefinitionStatement>,
pub consts: IndexMap<Symbol, ConstDeclaration>,
/// A map from struct names to struct definitions.
pub structs: IndexMap<Symbol, Struct>,
/// A map from mapping names to mapping definitions.

View File

@ -0,0 +1,46 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, Identifier, Node, NodeID, Type};
use leo_span::Span;
use serde::{Deserialize, Serialize};
use std::fmt;
/// A constant declaration statement.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ConstDeclaration {
/// The place to assign to. As opposed to `DefinitionStatement`, this can only be an identifier
pub place: Identifier,
/// The type of the binding, if specified, or inferred otherwise.
pub type_: Type,
/// An initializer value for the binding.
pub value: Expression,
/// The span excluding the semicolon.
pub span: Span,
/// The ID of the node.
pub id: NodeID,
}
impl fmt::Display for ConstDeclaration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.place)?;
write!(f, ": {}", self.type_)?;
write!(f, " = {};", self.value)
}
}
crate::simple_node_impl!(ConstDeclaration);

View File

@ -29,6 +29,9 @@ pub use conditional::*;
pub mod console;
pub use console::*;
pub mod const_;
pub use const_::*;
pub mod definition;
pub use definition::*;
@ -61,6 +64,8 @@ pub enum Statement {
Conditional(ConditionalStatement),
/// A console logging statement.
Console(ConsoleStatement),
/// A binding from identifier to constant value.
Const(ConstDeclaration),
/// A binding or set of bindings / variables to declare.
Definition(DefinitionStatement),
/// An expression statement
@ -86,6 +91,7 @@ impl fmt::Display for Statement {
Statement::Block(x) => x.fmt(f),
Statement::Conditional(x) => x.fmt(f),
Statement::Console(x) => x.fmt(f),
Statement::Const(x) => x.fmt(f),
Statement::Definition(x) => x.fmt(f),
Statement::Expression(x) => x.fmt(f),
Statement::Iteration(x) => x.fmt(f),
@ -103,6 +109,7 @@ impl Node for Statement {
Block(n) => n.span(),
Conditional(n) => n.span(),
Console(n) => n.span(),
Const(n) => n.span(),
Definition(n) => n.span(),
Expression(n) => n.span(),
Iteration(n) => n.span(),
@ -118,6 +125,7 @@ impl Node for Statement {
Block(n) => n.set_span(span),
Conditional(n) => n.set_span(span),
Console(n) => n.set_span(span),
Const(n) => n.set_span(span),
Definition(n) => n.set_span(span),
Expression(n) => n.set_span(span),
Iteration(n) => n.set_span(span),
@ -133,6 +141,7 @@ impl Node for Statement {
Block(n) => n.id(),
Conditional(n) => n.id(),
Console(n) => n.id(),
Const(n) => n.id(),
Definition(n) => n.id(),
Expression(n) => n.id(),
Iteration(n) => n.id(),
@ -148,6 +157,7 @@ impl Node for Statement {
Block(n) => n.set_id(id),
Conditional(n) => n.set_id(id),
Console(n) => n.set_id(id),
Const(n) => n.set_id(id),
Definition(n) => n.set_id(id),
Expression(n) => n.set_id(id),
Iteration(n) => n.set_id(id),

View File

@ -146,8 +146,8 @@ impl ParserContext<'_> {
while self.has_next() {
match &self.token.token {
Token::Const => {
let definition = self.parse_const_definition_statement()?;
consts.insert(Symbol::intern(&definition.place.to_string()), definition);
let declaration = self.parse_const_declaration_statement()?;
consts.insert(Symbol::intern(&declaration.place.to_string()), declaration);
}
Token::Struct | Token::Record => {
let (id, struct_) = self.parse_struct()?;

View File

@ -45,7 +45,7 @@ impl ParserContext<'_> {
Token::For => Ok(Statement::Iteration(Box::new(self.parse_loop_statement()?))),
Token::Assert | Token::AssertEq | Token::AssertNeq => Ok(self.parse_assert_statement()?),
Token::Let => Ok(Statement::Definition(self.parse_definition_statement()?)),
Token::Const => Ok(Statement::Definition(self.parse_const_definition_statement()?)),
Token::Const => Ok(Statement::Const(self.parse_const_declaration_statement()?)),
Token::LeftCurly => Ok(Statement::Block(self.parse_block()?)),
Token::Console => Err(ParserError::console_statements_are_not_yet_supported(self.token.span).into()),
Token::Finalize => Err(ParserError::finalize_statements_are_deprecated(self.token.span).into()),
@ -315,34 +315,19 @@ impl ParserContext<'_> {
Ok(ConsoleStatement { span: keyword + span, function, id: self.node_builder.next_id() })
}
/// Returns a [`DefinitionStatement`] AST node if the next tokens represent a const definition statement.
pub(super) fn parse_const_definition_statement(&mut self) -> Result<DefinitionStatement> {
/// Returns a [`ConstDeclaration`] AST node if the next tokens represent a const declaration statement.
pub(super) fn parse_const_declaration_statement(&mut self) -> Result<ConstDeclaration> {
self.expect(&Token::Const)?;
let decl_span = self.prev_token.span;
let decl_type = match &self.prev_token.token {
Token::Const => DeclarationType::Const,
_ => unreachable!(
"parse_const_definition_statement shouldn't produce this as have already ensured that first token was 'Const'"
),
};
// Parse variable name and type.
let place = self.parse_expression()?;
self.expect(&Token::Colon)?;
let type_ = self.parse_type()?.0;
let (place, type_, _) = self.parse_typed_ident()?;
self.expect(&Token::Assign)?;
let value = self.parse_expression()?;
self.expect(&Token::Semicolon)?;
Ok(DefinitionStatement {
span: decl_span + value.span(),
declaration_type: decl_type,
place,
type_,
value,
id: self.node_builder.next_id(),
})
Ok(ConstDeclaration { span: decl_span + value.span(), place, type_, value, id: self.node_builder.next_id() })
}
/// Returns a [`DefinitionStatement`] AST node if the next tokens represent a definition statement.

View File

@ -44,6 +44,9 @@ impl<'a> CodeGenerator<'a> {
Statement::Block(stmt) => self.visit_block(stmt),
Statement::Conditional(stmt) => self.visit_conditional(stmt),
Statement::Console(stmt) => self.visit_console(stmt),
Statement::Const(_) => {
unreachable!("`ConstStatement`s should not be in the AST at this phase of compilation.")
}
Statement::Definition(stmt) => self.visit_definition(stmt),
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
Statement::Iteration(stmt) => self.visit_iteration(stmt),

View File

@ -17,7 +17,7 @@
use indexmap::IndexMap;
use std::cell::RefCell;
use leo_ast::Literal;
use leo_ast::Expression;
use leo_errors::Result;
use leo_span::Symbol;
@ -30,7 +30,7 @@ pub struct ConstantPropagationTable {
pub(crate) parent: Option<Box<ConstantPropagationTable>>,
/// The known constants in the current scope
/// This field is populated as necessary.
pub(crate) constants: IndexMap<Symbol, Literal>,
pub(crate) constants: IndexMap<Symbol, Expression>,
/// The index of the current scope.
pub(crate) scope_index: usize,
/// The sub-scopes of this scope.
@ -47,8 +47,8 @@ impl ConstantPropagationTable {
}
/// Inserts a constant into the constant propagation table.
pub fn insert_constant(&mut self, symbol: Symbol, literal: Literal) -> Result<()> {
self.constants.insert(symbol, literal);
pub fn insert_constant(&mut self, symbol: Symbol, expr: Expression) -> Result<()> {
self.constants.insert(symbol, expr);
Ok(())
}
@ -66,7 +66,7 @@ impl ConstantPropagationTable {
}
/// Attempts to lookup a constant in the constant propagation table.
pub fn lookup_constant(&self, symbol: Symbol) -> Option<&Literal> {
pub fn lookup_constant(&self, symbol: Symbol) -> Option<&Expression> {
if let Some(constant) = self.constants.get(&symbol) {
Some(constant)
} else if let Some(parent) = self.parent.as_ref() {

View File

@ -23,8 +23,8 @@ impl ExpressionReconstructor for Unroller<'_> {
fn reconstruct_identifier(&mut self, input: Identifier) -> (Expression, Self::AdditionalOutput) {
// Substitute the identifier with the constant value if it is a constant.
if let Some(literal) = self.constant_propagation_table.borrow().lookup_constant(input.name) {
return (Expression::Literal(literal.clone()), Default::default());
if let Some(expr) = self.constant_propagation_table.borrow().lookup_constant(input.name) {
return (expr.clone(), Default::default());
}
(Expression::Identifier(input), Default::default())
}

View File

@ -14,20 +14,23 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use leo_ast::{Expression::Literal, *};
use leo_span::Symbol;
use indexmap::IndexMap;
use leo_ast::*;
use crate::Unroller;
impl ProgramReconstructor for Unroller<'_> {
fn reconstruct_program_scope(&mut self, input: ProgramScope) -> ProgramScope {
let new_consts = input.consts.into_iter().map(|(i, c)| (i, self.reconstruct_const(c))).collect();
// Don't need to reconstructed consts, just need to add them to constant propagation table
input.consts.into_iter().for_each(|(_, c)| {
self.reconstruct_const(c);
});
ProgramScope {
program_id: input.program_id,
structs: input.structs,
mappings: input.mappings,
functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function(f))).collect(),
consts: new_consts,
consts: IndexMap::new(),
span: input.span,
}
}
@ -83,56 +86,4 @@ impl ProgramReconstructor for Unroller<'_> {
reconstructed_function
}
fn reconstruct_const(&mut self, input: DefinitionStatement) -> DefinitionStatement {
// Reconstruct the RHS expression to allow for constant propagation
let reconstructed_value_expression = self.reconstruct_expression(input.value.clone()).0;
// Helper function to add global constants to constant variable table
let insert_variable = |symbol: Symbol, value: &Expression| {
if let Literal(literal) = value {
if let Err(err) = self.constant_propagation_table.borrow_mut().insert_constant(symbol, literal.clone())
{
self.handler.emit_err(err);
}
} else {
unreachable!("Type checking guarantees that the value of a constant is a literal.");
}
};
// No matter if doing multiple definitions in one line or not, insert all global constants into the constant propagation table
match &input.place {
Expression::Identifier(identifier) => {
insert_variable(identifier.name, &reconstructed_value_expression);
}
Expression::Tuple(tuple_expression) => {
let tuple_values: &Vec<Expression> = match &reconstructed_value_expression {
Expression::Tuple(tuple_value_expression) => &tuple_value_expression.elements,
_ => unreachable!(
"Definition statement that defines tuple of variables must be assigned to tuple of values"
),
};
for (i, element) in tuple_expression.elements.iter().enumerate() {
let identifier = match element {
Expression::Identifier(identifier) => identifier,
_ => unreachable!("All elements of a definition tuple must be identifiers"),
};
insert_variable(identifier.name, &tuple_values[i].clone());
}
}
_ => unreachable!(
"Type checking guarantees that the lhs of a `DefinitionStatement` is either an identifier or tuple."
),
}
DefinitionStatement {
declaration_type: input.declaration_type,
place: input.place,
type_: input.type_,
value: reconstructed_value_expression,
span: input.span,
id: input.id,
}
}
}

View File

@ -50,26 +50,51 @@ impl StatementReconstructor for Unroller<'_> {
(block, Default::default())
}
fn reconstruct_const(&mut self, input: ConstDeclaration) -> (Statement, Self::AdditionalOutput) {
// Reconstruct the RHS expression to allow for constant propagation
let reconstructed_value_expression = self.reconstruct_expression(input.value.clone()).0;
// Add to constant propagation table. Since TC completed we know that the RHS is a literal or tuple of literals.
if let Err(err) = self.constant_propagation_table.borrow_mut().insert_constant(input.place.name, input.value) {
self.handler.emit_err(err);
}
// Remove from symbol table
if self.symbol_table.borrow().lookup_variable(input.place.name).is_some() {
if let Err(err) = self.symbol_table.borrow_mut().remove_variable_from_current_scope(input.place.name) {
self.handler.emit_err(err);
}
}
(
Statement::Const(ConstDeclaration {
place: input.place,
type_: input.type_,
value: reconstructed_value_expression,
span: input.span,
id: input.id,
}),
true,
)
}
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, declaration: VariableType| {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(symbol, VariableSymbol { type_, span, declaration })
{
let insert_variable = |symbol: Symbol, type_: Type, span: Span| {
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(symbol, VariableSymbol {
type_,
span,
declaration: VariableType::Mut,
}) {
self.handler.emit_err(err);
}
};
let declaration =
if input.declaration_type == DeclarationType::Const { VariableType::Const } else { VariableType::Mut };
// If we are unrolling a loop, then we need to repopulate the symbol table.
// If we are not unrolling a loop, the we need to remove constants from the symbol table.
// We always need to add constant variables to the constant variable table.
if declaration == VariableType::Mut && self.is_unrolling {
if self.is_unrolling {
match &input.place {
Expression::Identifier(identifier) => {
insert_variable(identifier.name, input.type_.clone(), input.span, declaration);
insert_variable(identifier.name, input.type_.clone(), input.span);
}
Expression::Tuple(tuple_expression) => {
let tuple_type = match input.type_ {
@ -83,15 +108,13 @@ impl StatementReconstructor for Unroller<'_> {
Expression::Identifier(identifier) => identifier,
_ => unreachable!("Type checking guarantees that if the lhs is a tuple, all of its elements are identifiers.")
};
insert_variable(identifier.name, input.type_.clone(), input.span, declaration);
insert_variable(identifier.name, input.type_.clone(), input.span);
});
}
_ => unreachable!(
"Type checking guarantees that the lhs of a `DefinitionStatement` is either an identifier or tuple."
),
}
} else if declaration == VariableType::Const {
return (Statement::Definition(self.reconstruct_const(input.clone())), true);
}
// Reconstruct the expression and return
@ -104,7 +127,7 @@ impl StatementReconstructor for Unroller<'_> {
span: input.span,
id: input.id,
}),
false,
Default::default(),
)
}

View File

@ -16,6 +16,7 @@
use leo_ast::{
Block,
Expression,
IntegerType,
IterationStatement,
Literal,
@ -240,12 +241,24 @@ impl<'a> Unroller<'a> {
// Add the loop variable as a constant for the current scope
self.constant_propagation_table
.borrow_mut()
.insert_constant(input.variable.name, value)
.insert_constant(input.variable.name, Expression::Literal(value.clone()))
.expect("Failed to insert constant into CPT");
// Reconstruct the statements in the loop body.
let statements: Vec<_> =
input.block.statements.clone().into_iter().map(|s| self.reconstruct_statement(s).0).collect();
let statements: Vec<_> = input
.block
.statements
.clone()
.into_iter()
.filter_map(|s| {
let (reconstructed_statement, additional_output) = self.reconstruct_statement(s);
if additional_output {
None // Exclude this statement from the block since it is a constant variable definition
} else {
Some(reconstructed_statement)
}
})
.collect();
let block = Statement::Block(Block { statements, span: input.block.span, id: input.block.id });

View File

@ -26,6 +26,7 @@ use leo_ast::{
CallExpression,
ConditionalStatement,
ConsoleStatement,
ConstDeclaration,
DefinitionStatement,
Expression,
ExpressionConsumer,
@ -214,6 +215,10 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
unreachable!("Parsing guarantees that console statements are not present in the program.")
}
fn consume_const(&mut self, _: ConstDeclaration) -> Self::Output {
unreachable!("Loop unrolling pass removes all constant declaration statements in the program.")
}
/// Consumes the `DefinitionStatement` into an `AssignStatement`, renaming the left-hand-side as appropriate.
fn consume_definition(&mut self, definition: DefinitionStatement) -> Self::Output {
// First consume the right-hand-side of the definition.

View File

@ -54,7 +54,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_program_scope(&mut self, input: &'a ProgramScope) {
// Typecheck each const definition, and append to symbol table.
input.consts.values().for_each(|c| self.visit_definition(c));
input.consts.values().for_each(|c| self.visit_const(c));
// Typecheck each struct definition.
input.structs.values().for_each(|function| self.visit_struct(function));

View File

@ -35,6 +35,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
Statement::Block(stmt) => self.visit_block(stmt),
Statement::Conditional(stmt) => self.visit_conditional(stmt),
Statement::Console(stmt) => self.visit_console(stmt),
Statement::Const(stmt) => self.visit_const(stmt),
Statement::Definition(stmt) => self.visit_definition(stmt),
Statement::Expression(stmt) => self.visit_expression_statement(stmt),
Statement::Iteration(stmt) => self.visit_iteration(stmt),
@ -150,10 +151,57 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
unreachable!("Parsing guarantees that console statements are not present in the AST.");
}
fn visit_definition(&mut self, input: &'a DefinitionStatement) {
let declaration =
if input.declaration_type == DeclarationType::Const { VariableType::Const } else { VariableType::Mut };
fn visit_const(&mut self, input: &'a ConstDeclaration) {
// Check that the type of the definition is not a unit type, singleton tuple type, or nested tuple type.
match &input.type_ {
// If the type is an empty tuple, return an error.
Type::Unit => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.span)),
// If the type is a singleton tuple, return an error.
Type::Tuple(tuple) => match tuple.len() {
0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
_ => {
if tuple.iter().any(|type_| matches!(type_, Type::Tuple(_))) {
self.emit_err(TypeCheckerError::nested_tuple_type(input.span))
}
}
},
Type::Mapping(_) | Type::Err => unreachable!(
"Parsing guarantees that `mapping` and `err` types are not present at this location in the AST."
),
// Otherwise, the type is valid.
_ => (), // Do nothing
}
// Enforce that Constant variables have literal expressions on right-hand side
match &input.value {
Expression::Literal(_) => (),
Expression::Tuple(tuple_expression) => match tuple_expression.elements.len() {
0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
_ => {
if tuple_expression.elements.iter().any(|expr| !matches!(expr, Expression::Literal(_))) {
self.emit_err(TypeCheckerError::const_declaration_must_be_literal_or_tuple_of_literals(
input.span,
))
}
}
},
_ => self.emit_err(TypeCheckerError::const_declaration_must_be_literal_or_tuple_of_literals(input.span())),
}
// Check the expression on the right-hand side.
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(input.place.name, VariableSymbol {
type_: input.type_.clone(),
span: input.place.span,
declaration: VariableType::Const,
}) {
self.handler.emit_err(err);
}
}
fn visit_definition(&mut self, input: &'a DefinitionStatement) {
// Check that the type of the definition is defined.
self.assert_type_is_defined(&input.type_, input.span);
@ -177,35 +225,17 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
_ => (), // Do nothing
}
// Enforce additional constraint that constant definitions can only be literals or tuples of literals. This will be removed after constant folding.
if input.declaration_type == DeclarationType::Const {
// Enforce that Constant variables have literal expressions on right-hand side
match &input.value {
Expression::Literal(_) => (),
Expression::Tuple(tuple_expression) => match tuple_expression.elements.len() {
0 | 1 => unreachable!("Parsing guarantees that tuple types have at least two elements."),
_ => {
if tuple_expression.elements.iter().any(|expr| !matches!(expr, Expression::Literal(_))) {
self.emit_err(TypeCheckerError::const_declaration_must_be_literal_or_tuple_of_literals(
input.span,
))
}
}
},
_ => self
.emit_err(TypeCheckerError::const_declaration_must_be_literal_or_tuple_of_literals(input.span())),
}
}
// Check the expression on the right-hand side.
self.visit_expression(&input.value, &Some(input.type_.clone()));
// TODO: Dedup with unrolling pass.
// Helper to insert the variables into the symbol table.
let insert_variable = |symbol: Symbol, type_: Type, span: Span, declaration: VariableType| {
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(symbol, VariableSymbol { type_, span, declaration })
{
let insert_variable = |symbol: Symbol, type_: Type, span: Span| {
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(symbol, VariableSymbol {
type_,
span,
declaration: VariableType::Mut,
}) {
self.handler.emit_err(err);
}
};
@ -213,7 +243,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// Insert the variables in the into the symbol table.
match &input.place {
Expression::Identifier(identifier) => {
insert_variable(identifier.name, input.type_.clone(), identifier.span, declaration)
insert_variable(identifier.name, input.type_.clone(), identifier.span)
}
Expression::Tuple(tuple_expression) => {
let tuple_type = match &input.type_ {
@ -222,6 +252,14 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
"Type checking guarantees that if the lhs is a tuple, its associated type is also a tuple."
),
};
if tuple_expression.elements.len() != tuple_type.len() {
return self.emit_err(TypeCheckerError::incorrect_num_tuple_elements(
tuple_expression.elements.len(),
tuple_type.len(),
input.place.span(),
));
}
tuple_expression.elements.iter().zip_eq(tuple_type.0.iter()).for_each(|(expression, type_)| {
let identifier = match expression {
Expression::Identifier(identifier) => identifier,
@ -231,7 +269,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
));
}
};
insert_variable(identifier.name, type_.clone(), identifier.span, declaration)
insert_variable(identifier.name, type_.clone(), identifier.span)
});
}
_ => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.place.span())),

View File

@ -49,10 +49,14 @@ The errors for the `leo-compiler` crate. Its error codes will range from 6_000-6
The errors for the `leo-imports` crate. Its error codes will range from 4_000-4_999 and be prefixed with the characters `IMP`.
## Input
### Input
The errors for the `leo-ast` crate. Its error codes will range from 8_000-8_999 and be prefixed with the characters `INP`.
### Loop Unrolling
The errors for loop unrolling in the `leo-passes` crate. Its error codes will range from 9_000-9_999 and be prefixed with the characters `LUN`.
### Package
The errors for the `leo-package` crate. Its error codes will range from 5_000-5_999 and be prefixed with the characters `PAK`.

View File

@ -18,9 +18,9 @@ use crate::create_messages;
use std::fmt::Debug;
create_messages!(
/// AstError enum that represents all the errors for the `leo-ast` crate.
/// LoopUnrollerError enum that represents all the errors for the loop unrolling errors in the `leo-loop_unroller` crate.
LoopUnrollerError,
code_mask: 8000i32,
code_mask: 9000i32,
code_prefix: "LUN",
@formatted

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::loop_unroller::LoopUnrollerError;
/// Contains the ASG error definitions.
use crate::LeoMessageCode;
@ -38,6 +37,9 @@ pub use self::flattener::*;
pub mod input;
pub use self::input::*;
pub mod loop_unroller;
pub use self::loop_unroller::*;
/// Contains the Package error definitions.
pub mod package;
pub use self::package::*;
@ -46,7 +48,6 @@ pub use self::package::*;
pub mod parser;
pub use self::parser::*;
pub mod loop_unroller;
/// Contains the Type Checker error definitions.
pub mod type_checker;

View File

@ -671,4 +671,17 @@ create_messages!(
help: None,
}
@formatted
incorrect_num_tuple_elements {
args: (identifiers: impl Display, types: impl Display),
msg: format!("Expected a tuple with {types} elements, found one with {identifiers} elements"),
help: None,
}
@formatted
const_declaration_can_only_have_one_binding {
args: (),
msg: format!("A constant declaration statement can only bind a single value"),
help: None,
}
);

View File

@ -0,0 +1,15 @@
---
namespace: Compile
expectation: Pass
outputs:
- - initial_symbol_table: af2effe11f5047f1accaca1df1d8456dbb355969e1e843ba37eda44257570551
type_checked_symbol_table: 61642b3cd24a2f6303c7abf5dac2821ed2f739f5d009f448b7eef2693fbfb0eb
unrolled_symbol_table: 751fa39b0cb4c55b0bd8ac36ef03aefc90829cb4f06ed664b5915b16dbaaaa70
initial_ast: 8147cde7c4f4b40f45e67afc32bb4f1c28e994968b52b4d2c7f40fa9b9ef3d0c
unrolled_ast: d0850814c6ead0d76661bf37b45212d2edbb6b47d38a006d43e5cba4fcc779f1
ssa_ast: b02e95a4c70b6d2fe4b9ba66d742d6001acf85fee058f0f065525265b5ee9c2b
flattened_ast: 7d8d33926bf19f4e5e00d9611b77284ce467483bb35192e7f4f9c7ae295c559b
inlined_ast: 7d8d33926bf19f4e5e00d9611b77284ce467483bb35192e7f4f9c7ae295c559b
dce_ast: 419b58124f548bd498b1cb0dda20f13505372d86cc2d405e9e97cca9327a3bd9
bytecode: acfb8fc365ba153cf8598a04dad8ff4ac65b9df6c6356cb077fcf9dafbead7e9
warnings: ""

View File

@ -4,12 +4,12 @@ expectation: Pass
outputs:
- - initial_symbol_table: 77b127880b95652f948bc184d24fb51eb368cc2ccb022093cd27c2fbc03bb70e
type_checked_symbol_table: be1bdc9825b662e2070dabbd8e16d24d51ed989e91e385f9ebf27e11dc12b902
unrolled_symbol_table: be1bdc9825b662e2070dabbd8e16d24d51ed989e91e385f9ebf27e11dc12b902
initial_ast: 0ab8753999f0db720682d3600df3575fcb8da517f539ee23844406eef6cdf0d0
unrolled_ast: 3f0b5beb875ff9de3b98978d7136c946f450f32b0c7bab7ac47257a9c8d4a7ea
ssa_ast: 2fd2a759cd7ab2242c9f7b97bc1241ab81bd26d6e1f35a308792edff76226e8f
flattened_ast: f6ea27693b4afa98e03b9201a01ba174c999395159b952bd2057291224a363f2
inlined_ast: f6ea27693b4afa98e03b9201a01ba174c999395159b952bd2057291224a363f2
dce_ast: f6ea27693b4afa98e03b9201a01ba174c999395159b952bd2057291224a363f2
unrolled_symbol_table: 435f5b6da45d68cf00a82aca5b9bd6e326c5d7d2252811db0e96afd1de00a727
initial_ast: f7f9f8362006d7a5e3659df72b883a2f21bc5ee43381ccbd023b32de23c5f833
unrolled_ast: 6ec80c2610eb0231078e39c207e6fef0346325d175c821c4e16d8aa96becedec
ssa_ast: bb89e164033dca2cb1b61d21c2e012bf72ce6d832c94de647a94f3a0ddf579e6
flattened_ast: 40c0e11be11bed1d1214f8b9a1dba9a34a86070eb9ffbc92827185ceaa458f08
inlined_ast: 40c0e11be11bed1d1214f8b9a1dba9a34a86070eb9ffbc92827185ceaa458f08
dce_ast: 40c0e11be11bed1d1214f8b9a1dba9a34a86070eb9ffbc92827185ceaa458f08
bytecode: 88de5519495f51852482cfc5ab129cde77b8d2b53fd2eebfd66472c6fe0cdafe
warnings: ""

View File

@ -4,12 +4,12 @@ expectation: Pass
outputs:
- - initial_symbol_table: d1eed24f01e5256fec3b444fd3a38b7e25756c5fb20010872884a34d54ef888c
type_checked_symbol_table: 89c060252a9e229b91f2ac52e5e3823e04400f3e060ead04999aa4911f42c731
unrolled_symbol_table: 26537147843c8e9daeda6a7af59b2b4bca86c6f067b08a4f4c3a7b7ffe30e59a
initial_ast: 1b2029fc1b5100e84675a87052375b38af1da27315d22daf1ccf49f17c5e5cf9
unrolled_ast: 6c9e5266587d4b90c0ea7522edcc4c3a6b00f6cfaadb91c59daba698b2da5b91
ssa_ast: a69548faa4852b092997ff059e4bbeb2161caada00423171ff0c98a167891165
flattened_ast: c67bdab30e2af4d0130fc11772176f267e1a7144488fab3ae7bcd33136b3ee47
inlined_ast: c67bdab30e2af4d0130fc11772176f267e1a7144488fab3ae7bcd33136b3ee47
dce_ast: c67bdab30e2af4d0130fc11772176f267e1a7144488fab3ae7bcd33136b3ee47
unrolled_symbol_table: c00e0818651bd9e2c068becdf3819b8d46238e0cfad46c87791efa9c97c6f9de
initial_ast: 310ef81cd18a675700a7c5d315978a5aeddfcb5220d7cb3ebb2c22cdefc72c27
unrolled_ast: 7e5cc7fedc616597a85fa4a5b46cc5432b93cf2d1f76f615f72d0a287f17232a
ssa_ast: a789faf5dde9e018665a22e2aa96674ca424dfdc6abc954c099113d7136e7e02
flattened_ast: 6d1021575d55836a866f7b99e87fed633869521e08d97f3d25fdfc2e7e60648d
inlined_ast: 6d1021575d55836a866f7b99e87fed633869521e08d97f3d25fdfc2e7e60648d
dce_ast: 6d1021575d55836a866f7b99e87fed633869521e08d97f3d25fdfc2e7e60648d
bytecode: a6350aaded46f7047061f7e68a8ae41eb8aa0d29f02560257ecdc582a6c684f9
warnings: ""

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ELUN0378000]: The loop range must be increasing.\n --> compiler-test:6:26\n |\n 6 | const STOP: u8 = 0u8;\n | ^^^\n"
- "Error [ELUN0379000]: The loop range must be increasing.\n --> compiler-test:6:26\n |\n 6 | const STOP: u8 = 0u8;\n | ^^^\n"

View File

@ -4,12 +4,12 @@ expectation: Pass
outputs:
- - initial_symbol_table: c6a4e40ae8f466c3ff6bf5d356d6ba89684438f88015e8ea23ff43eadb662b49
type_checked_symbol_table: 0b88104308fe0b9e390a59a4359d6245170347557911b21ba04cd1d9124da14d
unrolled_symbol_table: 0f5bcb03574cbdb2a6a0f335693d8bc0bca4926ce259fa0e0acc09baba07bcdd
initial_ast: 0418d21afa3636a1e9b0c4440bb1b06633c7674a0157ffb2b1b26b478147fee8
unrolled_ast: e07e1eab58e31a09d2b4464a0644fe3b4189aeb4d4418963ecdb9c0fdb8a25cc
ssa_ast: b5306dfc16a75fa20fa0c8c8c8df27de7c9cc76399f8245e7baa4998e77d4b5a
flattened_ast: 88e816cddb1f37e20dbf4c53e4c12095a8b6374af790d5210b431c0787607f73
inlined_ast: 88e816cddb1f37e20dbf4c53e4c12095a8b6374af790d5210b431c0787607f73
dce_ast: e01c208549e62b4372dcbfd2c24fb4c3bba4e7cb20e7e0a47acf67dd9dda5c63
unrolled_symbol_table: af56532f8dd6c6ca6f5fc8af3667202298898a54fe2f871a7874684a712f141d
initial_ast: 542dc099a401102c60c971fc4c4a97dc5a885e9b949b647ffd12fa418b82ede0
unrolled_ast: 6cee219515c51e5b250b0e1905eaeeb27b2d6de0690055bc9e2a8fbb50f5330a
ssa_ast: cd15cc9451d67fd9489a4de0ad5debd70f410ca9b8608bacd408948ee1d79e18
flattened_ast: a9cb531c3f1a8f3ae4d192d5c630fb0e92800ce2b974017835c90658969cda46
inlined_ast: a9cb531c3f1a8f3ae4d192d5c630fb0e92800ce2b974017835c90658969cda46
dce_ast: a9cb531c3f1a8f3ae4d192d5c630fb0e92800ce2b974017835c90658969cda46
bytecode: d9595550f8a3d55b350b4f46059fb01bf63308aa4b4416594c2eb20231f6483a
warnings: ""

View File

@ -1,15 +0,0 @@
---
namespace: Compile
expectation: Pass
outputs:
- - initial_symbol_table: d698a6988e2f4caf3321c2a5b0c0228721f13930b072f738e97747b7d3ade0de
type_checked_symbol_table: 88168597bb3de8d73a3ada9b63c026594d716c75f2deb4b7d20abd2758d6b4d3
unrolled_symbol_table: 88168597bb3de8d73a3ada9b63c026594d716c75f2deb4b7d20abd2758d6b4d3
initial_ast: f24907cd9e0f0b5f3fce4efb5eeea7945ec22c4780a31df6870816eb9480632f
unrolled_ast: 764e6b1b9204c5d98a6ccb9354fbee08aac7cc83647c5ebcd2e9c2880daa2544
ssa_ast: 764e6b1b9204c5d98a6ccb9354fbee08aac7cc83647c5ebcd2e9c2880daa2544
flattened_ast: 96c6bdf5200642b65e8f6e83228dc0a01c493c13d3b4d9686829b2efca7451fb
inlined_ast: 96c6bdf5200642b65e8f6e83228dc0a01c493c13d3b4d9686829b2efca7451fb
dce_ast: 96c6bdf5200642b65e8f6e83228dc0a01c493c13d3b4d9686829b2efca7451fb
bytecode: 91d9f12af3f91ba9332bdf7303424e4369e14db4b713c13f43afc72a55cfcd4a
warnings: ""

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372080]: The value of a const declaration must be a literal\n --> compiler-test:4:5\n |\n 4 | const (GOODBYE,HELLO): (u8,u8) = (0u8,(1u8,1u8));\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nError [ETYC0372058]: A tuple expression cannot contain another tuple expression.\n --> compiler-test:4:43\n |\n 4 | const (GOODBYE,HELLO): (u8,u8) = (0u8,(1u8,1u8));\n | ^^^^^^^^^\nError [ETYC0372023]: Tuples must be explicitly typed in Leo\n --> compiler-test:4:43\n |\n 4 | const (GOODBYE,HELLO): (u8,u8) = (0u8,(1u8,1u8));\n | ^^^^^^^^^\n |\n = The function definition must match the function return statement\n"
- "Error [EPAR0370009]: unexpected string: expected 'identifier', found '('\n --> compiler-test:4:11\n |\n 4 | const (HELLO,GOODBYE): (u8,u8) = (1u8, 1u8);\n | ^"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [EAST0372009]: variable `HELLO` shadowed by\n --> compiler-test:5:11\n |\n 5 | const HELLO: u8 = 0u8;\n | ^^^^^\n"
- "Error [EPAR0370009]: unexpected string: expected 'identifier', found '('\n --> compiler-test:4:11\n |\n 4 | const (HELLO,GOODBYE): (u8,u8) = (0u8,0u8);\n | ^"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ELUN0378000]: The loop range must be increasing.\n --> compiler-test:7:28\n |\n 7 | for i: i8 in 10i8..5i8 {\n | ^^^\n"
- "Error [ELUN0379000]: The loop range must be increasing.\n --> compiler-test:7:28\n |\n 7 | for i: i8 in 10i8..5i8 {\n | ^^^\n"

View File

@ -4,13 +4,13 @@ expectation: Pass
outputs:
- - initial_symbol_table: 4194c6207c716b52d30529becd081afc9b7313e8e1ce400cc65c33fac50fab31
type_checked_symbol_table: 51dec1877b8da96c81929eabd624019ae50c181362703756b68d94d002cb2f56
unrolled_symbol_table: f62721c061790b66cf66c0dbac8f85ca582bb420e6b315e081e7894bb81e8cdf
initial_ast: 40297565663ad5f424bb0ac5a571ea56a99043ce11acc018df07cbbcc2df2994
unrolled_ast: d00d781141ee230a9b6602ed35a264bde7f84cc3ea904e7aa83ea3edd877e723
ssa_ast: 5455c22f4e05610c0bd8c2a7ac0d15d8aab1f8ae6cc23fcdb3cc75e75e16519d
flattened_ast: 84a675cf8525bd7a82dabe9fb1542926b34e611fcf4a51e82528cd855b18b5c8
inlined_ast: 84a675cf8525bd7a82dabe9fb1542926b34e611fcf4a51e82528cd855b18b5c8
dce_ast: 84a675cf8525bd7a82dabe9fb1542926b34e611fcf4a51e82528cd855b18b5c8
unrolled_symbol_table: 0b6340ef766a4154f31b5fa00d9bebe8478a8e3c81f091b8433e870ad7213b25
initial_ast: bc84c92328b456b2a88fadd3998ae180078447418a1adacbc9495f3d9177a38a
unrolled_ast: e9972535de2da936d1db8f65497b529ceedc0a691ea34a9e407447c84311332f
ssa_ast: 5a9e0d139821e42ea9cdb3a75c9b9ccdd10e4368490f2c5a1edca0e3d41913d5
flattened_ast: 99f8c4ca373d413eba9db8b8949bdf493e6a201298578c22608de3bc8f4abcbf
inlined_ast: 99f8c4ca373d413eba9db8b8949bdf493e6a201298578c22608de3bc8f4abcbf
dce_ast: 99f8c4ca373d413eba9db8b8949bdf493e6a201298578c22608de3bc8f4abcbf
bytecode: 6f468335c2ba73721aece1b1018fff421437eee9d52956368d33822d46e8c012
warnings: ""
results:

View File

@ -0,0 +1,17 @@
/*
namespace: Compile
expectation: Pass
*/
program test.aleo {
const B: (u8,u32) = (1u8,1u32);
transition foo() -> u32 {
for i: u32 in 0u32..1u32 {
let d:(u8,u32) = B;
const A:u32 = 1u32;
let c:u32 = A;
}
return 1u32;
}
}

View File

@ -1,7 +1,7 @@
/*
namespace: Compile
expectation: Pass
*/
namespace: Compile
expectation: Pass
*/
program test.aleo {
const START: u32 = 0u32;

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
*/
program test.aleo {
const (HELLO,GOODBYE): (u8,u8) = (1u8, 1u8);
transition foo(a: u8, b: u8, flag: bool) -> u8 {
return GOODBYE;
}
}

View File

@ -4,7 +4,7 @@ expectation: Fail
*/
program test.aleo {
const (GOODBYE,HELLO): (u8,u8) = (0u8,(1u8,1u8));
const (HELLO,GOODBYE): (u8,u8) = (1u8, 1u8);
transition foo(a: u8, b: u8, flag: bool) -> u8 {
return GOODBYE;
}