Restrict tuples in parser; cleanup

This commit is contained in:
d0cd 2022-11-08 10:15:34 -08:00
parent d705fd1cce
commit f606a8b74a
11 changed files with 41 additions and 62 deletions

View File

@ -14,8 +14,12 @@
// 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_errors::{AstError, Result};
use crate::{Tuple, Type};
use super::*;
// TODO: Consider a safe interface for constructing a tuple expression.
/// A tuple expression, e.g., `(foo, false, 42)`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TupleExpression {

View File

@ -16,26 +16,16 @@
use crate::Type;
use leo_errors::{AstError, Result};
use leo_span::Span;
use serde::{Deserialize, Serialize};
use std::{fmt, ops::Deref};
// TODO: Consider defining a safe interface for constructing a tuple type.
/// A type list of at least two types.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Tuple(pub Vec<Type>);
impl Tuple {
/// Returns a new `Type::Tuple` enumeration.
pub fn try_new(elements: Vec<Type>, span: Span) -> Result<Type> {
match elements.len() {
0 => Err(AstError::empty_tuple(span).into()),
1 => Err(AstError::one_element_tuple(span).into()),
_ => Ok(Type::Tuple(Tuple(elements))),
}
}
}
impl Deref for Tuple {
type Target = Vec<Type>;

View File

@ -428,9 +428,14 @@ impl ParserContext<'_> {
match elements.len() {
// If the tuple expression is empty, return a `UnitExpression`.
0 => Ok(Expression::Unit(UnitExpression { span })),
// If there is one element in the tuple but no trailing comma, e.g `(foo)`, return the element.
1 if !trailing => Ok(elements.swap_remove(0)),
1 => match trailing {
// If there is one element in the tuple but no trailing comma, e.g `(foo)`, return the element.
false => Ok(elements.swap_remove(0)),
// If there is one element in the tuple and a trailing comma, e.g `(foo,)`, emit an error since tuples must have at least two elements.
true => Err(ParserError::tuple_must_have_at_least_two_elements("expression", span).into()),
}
// Otherwise, return a tuple expression.
// Note: This is the only place where `TupleExpression` is constructed in the parser.
_ => Ok(Expression::Tuple(TupleExpression { elements, span })),
}
}

View File

@ -16,7 +16,7 @@
use super::*;
use leo_errors::Result;
use leo_errors::{ParserError, Result};
pub(super) const TYPE_TOKENS: &[Token] = &[
Token::Address,
@ -83,7 +83,10 @@ impl ParserContext<'_> {
match types.len() {
// If the parenthetical block is empty, e.g. `()` or `( )`, it should be parsed into `Unit` types.
0 => Ok((Type::Unit, span)),
// If the parenthetical block contains a single type, e.g. `(u8)`, emit an error, since tuples must have at least two elements.
1 => Err(ParserError::tuple_must_have_at_least_two_elements("type", span).into()),
// Otherwise, parse it into a `Tuple` type.
// Note: This is the only place where `Tuple` type is constructed in the parser.
_ => Ok((Type::Tuple(Tuple(types.into_iter().map(|t| t.0).collect())), span)),
}
} else {

View File

@ -307,8 +307,7 @@ impl<'a> CodeGenerator<'a> {
match return_type {
Type::Unit => (String::new(), instructions), // Do nothing
Type::Tuple(tuple) => match tuple.len() {
0 => unreachable!("Parsing guarantees that a tuple type has at least one element"),
1 => unreachable!("Type checking disallows singleton tuples."),
0 | 1 => unreachable!("Parsing guarantees that a tuple type has at least two elements"),
len => {
let mut destinations = Vec::new();
for _ in 0..len {

View File

@ -33,7 +33,7 @@ impl<'a> CodeGenerator<'a> {
unreachable!("Mapping types are not supported at this phase of compilation")
}
Type::Tuple(_) => {
unreachable!("Tuple types are not supported at this phase of compilation")
unreachable!("Tuple types should not be visited at this phase of compilation")
}
Type::Err => unreachable!("Error types should not exist at this phase of compilation"),
Type::Unit => unreachable!("Unit types are not supported at this phase of compilation"),

View File

@ -627,11 +627,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output {
match input.elements.len() {
0 => unreachable!("Parsing guarantees that tuple expressions have at least one element."),
1 => {
self.emit_err(TypeCheckerError::singleton_tuple(input.span()));
None
}
0 | 1 => unreachable!("Parsing guarantees that tuple expressions have at least two elements."),
_ => {
// Check the expected tuple types if they are known.
if let Some(Type::Tuple(expected_types)) = expected {
@ -716,7 +712,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
fn visit_unit(&mut self, input: &'a UnitExpression, _additional: &Self::AdditionalInput) -> Self::Output {
// Unit expression are only allowed inside a return statement.
if !self.is_return {
self.emit_err(TypeCheckerError::unit_tuple(input.span()));
self.emit_err(TypeCheckerError::unit_expression_only_in_return_statements(input.span()));
}
Some(Type::Unit)
}

View File

@ -195,11 +195,10 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// 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::unit_tuple(input.span)),
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 => unreachable!("Parsing guarantees that tuples have a length of at least one."),
1 => self.emit_err(TypeCheckerError::singleton_tuple(input.span)),
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))

View File

@ -74,22 +74,6 @@ create_messages!(
help: None,
}
/// For when a user tries to define an empty tuple.
@formatted
empty_tuple {
args: (),
msg: "Tuples of zero elements are not allowed.",
help: None,
}
/// For when a user tries to define a tuple dimension of one.
@formatted
one_element_tuple {
args: (),
msg: "Tuples of one element are not allowed.",
help: Some("Try defining a single type by removing the parenthesis `( )`".to_string()),
}
/// For when a user shadows a function.
@formatted
shadowed_function {

View File

@ -262,4 +262,11 @@ create_messages!(
msg: "Invalid network identifier. The only supported identifier is `aleo`.",
help: None,
}
@formatted
tuple_must_have_at_least_two_elements {
args: (kind: impl Display),
msg: format!("A tuple {kind} must have at least two elements."),
help: None,
}
);

View File

@ -447,7 +447,8 @@ create_messages!(
help: None,
}
// TODO: Eventually update to warnings.
// TODO: Consider chainging this to a warning.
@formatted
assign_unit_expression_to_variable {
args: (),
@ -455,22 +456,6 @@ create_messages!(
help: None,
}
// TODO: Better error messages for each tuple case. e.g tuple in function parameter, tuple in assignment, tuple in return, etc.
@formatted
singleton_tuple {
args: (),
msg: format!("Singleton tuple expressions and tuple types are not allowed."),
help: None,
}
@formatted
unit_tuple {
args: (),
msg: format!("Empty tuple expressions and tuple types are not allowed."),
help: None,
}
@formatted
nested_tuple_type {
args: (),
@ -488,7 +473,7 @@ create_messages!(
@formatted
function_cannot_take_tuple_as_input {
args: (),
msg: format!("A function cannot take in tuples as input."),
msg: format!("A function cannot take in a tuple as input."),
help: None,
}
@ -530,7 +515,14 @@ create_messages!(
@formatted
lhs_must_be_identifier_or_tuple {
args: (),
msg: format!("The left-hand side of a `DefinitionStatement` can only be a tuple or identifier."),
msg: format!("The left-hand side of a `DefinitionStatement` can only be an identifier or tuple. Note that a tuple must contain at least two elements."),
help: None,
}
@formatted
unit_expression_only_in_return_statements {
args: (),
msg: format!("Unit expressions can only be used in return statements."),
help: None,
}
);