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, + } );