diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs
index 7f6ff58a15..123c0510ae 100644
--- a/compiler/ast/src/passes/reconstructor.rs
+++ b/compiler/ast/src/passes/reconstructor.rs
@@ -251,7 +251,7 @@ pub trait StatementReconstructor: ExpressionReconstructor {
(
Statement::Definition(DefinitionStatement {
declaration_type: input.declaration_type,
- variable_name: input.variable_name,
+ place: input.place,
type_: input.type_,
value: self.reconstruct_expression(input.value).0,
span: input.span,
diff --git a/compiler/ast/src/statement/definition/mod.rs b/compiler/ast/src/statement/definition/mod.rs
index a1bb31e4cc..e9063887ae 100644
--- a/compiler/ast/src/statement/definition/mod.rs
+++ b/compiler/ast/src/statement/definition/mod.rs
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see .
-use crate::{Expression, Identifier, Node, Type};
+use crate::{Expression, Node, Type};
use leo_span::Span;
use serde::{Deserialize, Serialize};
@@ -29,7 +29,7 @@ pub struct DefinitionStatement {
/// What sort of declaration is this? `let` or `const`?.
pub declaration_type: DeclarationType,
/// The bindings / variable names to declare.
- pub variable_name: Identifier,
+ pub place: Expression,
/// The types of the bindings, if specified, or inferred otherwise.
pub type_: Type,
/// An initializer value for the bindings.
@@ -41,7 +41,7 @@ pub struct DefinitionStatement {
impl fmt::Display for DefinitionStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ", self.declaration_type)?;
- write!(f, "{}", self.variable_name)?;
+ write!(f, "{}", self.place)?;
write!(f, ": {}", self.type_)?;
write!(f, " = {};", self.value)
}
diff --git a/compiler/parser/src/parser/statement.rs b/compiler/parser/src/parser/statement.rs
index 89468e17d2..0a0beb2600 100644
--- a/compiler/parser/src/parser/statement.rs
+++ b/compiler/parser/src/parser/statement.rs
@@ -297,7 +297,9 @@ impl ParserContext<'_> {
};
// Parse variable name and type.
- let (variable_name, type_) = self.parse_typed_ident()?;
+ let place = self.parse_expression()?;
+ self.expect(&Token::Colon)?;
+ let type_ = self.parse_type()?.0;
self.expect(&Token::Assign)?;
let value = self.parse_expression()?;
@@ -306,7 +308,7 @@ impl ParserContext<'_> {
Ok(DefinitionStatement {
span: decl_span + value.span(),
declaration_type: decl_type,
- variable_name,
+ place,
type_,
value,
})
diff --git a/compiler/passes/src/loop_unrolling/unroll_statement.rs b/compiler/passes/src/loop_unrolling/unroll_statement.rs
index ab4e8728cd..22a831df51 100644
--- a/compiler/passes/src/loop_unrolling/unroll_statement.rs
+++ b/compiler/passes/src/loop_unrolling/unroll_statement.rs
@@ -14,7 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see .
+use itertools::Itertools;
use leo_ast::*;
+use leo_span::{Span, Symbol};
use crate::unroller::Unroller;
use crate::{VariableSymbol, VariableType};
@@ -50,15 +52,37 @@ impl StatementReconstructor for Unroller<'_> {
VariableType::Mut
};
- if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
- input.variable_name.name,
- VariableSymbol {
- type_: input.type_.clone(),
- span: input.span(),
- declaration,
+ 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,
+ },
+ ) {
+ self.handler.emit_err(err);
+ }
+ };
+
+ // 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),
+ Expression::Tuple(tuple_expression) => {
+ let tuple_type = match input.type_ {
+ Type::Tuple(ref tuple_type) => tuple_type,
+ _ => unreachable!("Type checking guarantees that if the lhs is a tuple, its associated type is also a tuple.")
+ };
+ tuple_expression.elements.iter().zip_eq(tuple_type.0.iter()).for_each(|(expression, type_)| {
+ let identifier = match expression {
+ 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, type_.clone(), identifier.span, declaration)
+ });
},
- ) {
- self.handler.emit_err(err);
+ _ => unreachable!("Type checking guarantees that the lhs of a `DefinitionStatement` is either an identifier or tuple.")
+
}
}
(Statement::Definition(input), Default::default())
diff --git a/compiler/passes/src/loop_unrolling/unroller.rs b/compiler/passes/src/loop_unrolling/unroller.rs
index e02d5d2baa..76e2b2740b 100644
--- a/compiler/passes/src/loop_unrolling/unroller.rs
+++ b/compiler/passes/src/loop_unrolling/unroller.rs
@@ -191,7 +191,7 @@ impl<'a> Unroller<'a> {
type_: input.type_.clone(),
value: Expression::Literal(value),
span: Default::default(),
- variable_name: input.variable,
+ place: Expression::Identifier(input.variable),
})
.0,
];
diff --git a/compiler/passes/src/static_single_assignment/rename_statement.rs b/compiler/passes/src/static_single_assignment/rename_statement.rs
index 9890ea5e31..fe7fbfa089 100644
--- a/compiler/passes/src/static_single_assignment/rename_statement.rs
+++ b/compiler/passes/src/static_single_assignment/rename_statement.rs
@@ -222,8 +222,9 @@ impl StatementConsumer for StaticSingleAssigner<'_> {
// Then assign a new unique name to the left-hand-side of the definition.
// Note that this order is necessary to ensure that the right-hand-side uses the correct name when consuming a complex assignment.
+ // TODO: Remove identifier assumption.
self.is_lhs = true;
- let identifier = match self.consume_identifier(definition.variable_name).0 {
+ let identifier = match self.consume_expression(definition.place).0 {
Expression::Identifier(identifier) => identifier,
_ => unreachable!("`self.consume_identifier` will always return an `Identifier`."),
};
diff --git a/compiler/passes/src/symbol_table/variable_symbol.rs b/compiler/passes/src/symbol_table/variable_symbol.rs
index 50f3343f65..7255ddeeb0 100644
--- a/compiler/passes/src/symbol_table/variable_symbol.rs
+++ b/compiler/passes/src/symbol_table/variable_symbol.rs
@@ -20,7 +20,7 @@ use leo_ast::{Mode, Type};
use leo_span::Span;
/// An enumeration of the different types of variable type.
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum VariableType {
Const,
Input(Mode),
diff --git a/compiler/passes/src/type_checking/check_statements.rs b/compiler/passes/src/type_checking/check_statements.rs
index 56bb5b80ed..4b862e2dbf 100644
--- a/compiler/passes/src/type_checking/check_statements.rs
+++ b/compiler/passes/src/type_checking/check_statements.rs
@@ -15,9 +15,11 @@
// along with the Leo library. If not, see .
use crate::{TypeChecker, VariableSymbol, VariableType};
+use itertools::Itertools;
use leo_ast::*;
use leo_errors::TypeCheckerError;
+use leo_span::{Span, Symbol};
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_statement(&mut self, input: &'a Statement) {
@@ -212,16 +214,50 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// Check the expression on the left-hand side.
self.visit_expression(&input.value, &Some(input.type_.clone()));
- // Insert the variable into the symbol table.
- if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
- input.variable_name.name,
- VariableSymbol {
- type_: input.type_.clone(),
- span: input.span(),
- declaration,
- },
- ) {
- self.handler.emit_err(err);
+ // 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,
+ },
+ ) {
+ self.handler.emit_err(err);
+ }
+ };
+
+ // 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)
+ }
+ Expression::Tuple(tuple_expression) => {
+ let tuple_type = match &input.type_ {
+ Type::Tuple(tuple_type) => tuple_type,
+ _ => unreachable!(
+ "Type checking guarantees that if the lhs is a tuple, its associated type is also a tuple."
+ ),
+ };
+ tuple_expression
+ .elements
+ .iter()
+ .zip_eq(tuple_type.0.iter())
+ .for_each(|(expression, type_)| {
+ let identifier = match expression {
+ Expression::Identifier(identifier) => identifier,
+ _ => {
+ return self.emit_err(TypeCheckerError::lhs_tuple_element_must_be_an_identifier(
+ expression.span(),
+ ))
+ }
+ };
+ insert_variable(identifier.name, type_.clone(), identifier.span, declaration)
+ });
+ }
+ _ => self.emit_err(TypeCheckerError::lhs_must_be_identifier_or_tuple(input.place.span())),
}
}
diff --git a/errors/src/errors/type_checker/type_checker_error.rs b/errors/src/errors/type_checker/type_checker_error.rs
index c270b5101a..2517aacfc9 100644
--- a/errors/src/errors/type_checker/type_checker_error.rs
+++ b/errors/src/errors/type_checker/type_checker_error.rs
@@ -519,4 +519,18 @@ create_messages!(
msg: format!("An expression statement must be a function call."),
help: None,
}
+
+ @formatted
+ lhs_tuple_element_must_be_an_identifier {
+ args: (),
+ msg: format!("Tuples on the left-hand side of a `DefinitionStatement` can only contain identifiers."),
+ help: None,
+ }
+
+ @formatted
+ lhs_must_be_identifier_or_tuple {
+ args: (),
+ msg: format!("The left-hand side of a `DefinitionStatement` can only be a tuple or identifier."),
+ help: None,
+ }
);