More tyc restricting nested tuples

This commit is contained in:
d0cd 2022-10-11 18:42:16 -05:00
parent 2839de13c6
commit 4963a11ee7
5 changed files with 69 additions and 30 deletions

View File

@ -674,6 +674,10 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
.iter()
.zip(input.elements.iter())
.for_each(|(expected, expr)| {
// Check that the component expression is not a tuple.
if matches!(expr, Expression::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_expression(expr.span()))
}
self.visit_expression(expr, &Some(expected.clone()));
});

View File

@ -30,6 +30,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_struct(&mut self, input: &'a Struct) {
// Check for conflicting struct/record member names.
let mut used = HashSet::new();
// TODO: Better span to target duplicate member.
if !input.members.iter().all(|Member { identifier, type_ }| {
// Check that the member types are defined.
self.assert_type_is_defined(type_, identifier.span);
@ -109,6 +110,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Check that the function's annotations are valid.
// Note that Leo does not natively support any specific annotations.
for annotation in function.annotations.iter() {
// TODO: Change to compiler warning.
self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span))
}
@ -146,7 +148,6 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
if matches!(input_var.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input_var.span()))
}
self.assert_valid_declaration_or_parameter_type(&input_var.type_(), input_var.span());
match self.is_transition_function {
// If the function is a transition function, then check that the parameter mode is not a constant.
@ -174,8 +175,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
});
// Type check the function's return type.
// Note that checking that each of the component types are defined is sufficient to check that `output_type` is defined.
function.output.iter().for_each(|output_type| {
match output_type {
// TODO: Verify that this is not needed when the import system is updated.
Output::External(_) => {} // Do not type check external record function outputs.
Output::Internal(output_type) => {
// Check that the type of output is defined.
@ -184,9 +187,8 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
if matches!(&output_type.type_, Type::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_type(output_type.span))
}
self.assert_valid_declaration_or_parameter_type(&output_type.type_, output_type.span);
// Check that the mode of the output is valid.
// For functions, only public and private outputs are allowed
if output_type.mode == Mode::Const {
self.emit_err(TypeCheckerError::cannot_have_constant_output_mode(output_type.span));
}
@ -196,9 +198,6 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
self.visit_block(&function.block);
// Check that the return type is defined. Note that the component types are already checked.
self.assert_type_is_defined(&function.output_type, function.span);
// If the function has a return type, then check that it has a return.
if function.output_type != Type::Unit && !self.has_return {
self.emit_err(TypeCheckerError::missing_return(function.span));
@ -250,7 +249,6 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
if input_var.mode() == Mode::Const || input_var.mode() == Mode::Private {
self.emit_err(TypeCheckerError::finalize_input_mode_must_be_public(input_var.span()));
}
// Check for conflicting variable names.
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
input_var.identifier().name,
@ -265,6 +263,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
});
// Type check the function's return type.
// Note that checking that each of the component types are defined is sufficient to guarantee that the `output_type` is defined.
finalize.output.iter().for_each(|output_type| {
// Check that the type of output is defined.
self.assert_type_is_defined(&output_type.type_(), output_type.span());
@ -272,14 +271,16 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
if matches!(&output_type.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_type(output_type.span()))
}
// Check that the mode of the output is valid.
if output_type.mode() == Mode::Const {
self.emit_err(TypeCheckerError::finalize_input_mode_must_be_public(output_type.span()));
// Note that a finalize block can have only public outputs.
if matches!(output_type.mode(), Mode::Const | Mode::Private) {
self.emit_err(TypeCheckerError::finalize_output_mode_must_be_public(
output_type.span(),
));
}
});
// TODO: Remove when this restriction is removed.
// TODO: Remove if this restriction is relaxed at Aleo instructions level.
// Check that the finalize block is not empty.
if finalize.block.statements.is_empty() {
self.emit_err(TypeCheckerError::finalize_block_must_not_be_empty(finalize.span));

View File

@ -189,8 +189,24 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// Check that the type of the definition is defined.
self.assert_type_is_defined(&input.type_, input.span);
// Check that the type of the definition is valid.
self.assert_valid_declaration_or_parameter_type(&input.type_, input.span);
// 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)),
// If the type is a singleton tuple, return an error.
Type::Tuple(tuple) => match tuple.len() {
0 => unreachable!("Tuples must have a length of at least one."),
1 => self.emit_err(TypeCheckerError::singleton_tuple(input.span)),
_ => {
if tuple.iter().any(|type_| matches!(type_, Type::Tuple(_))) {
self.emit_err(TypeCheckerError::nested_tuple_type(input.span))
}
}
},
Type::Mapping(_) | Type::Err => unreachable!(),
// Otherwise, the type is valid.
_ => (), // Do nothing
}
// Check the expression on the left-hand side.
self.visit_expression(&input.value, &Some(input.type_.clone()));
@ -244,6 +260,12 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
.iter()
.zip(input.arguments.iter())
.for_each(|(expected, argument)| {
// Check that none of the arguments are tuple expressions.
if matches!(argument, Expression::Tuple(_)) {
self.emit_err(TypeCheckerError::finalize_statement_cannot_contain_tuples(
argument.span(),
));
}
self.visit_expression(argument, &Some(expected.type_()));
});
}
@ -358,6 +380,13 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
self.has_return = true;
// Check that the return expression is not a tuple.
if matches!(&input.expression, Expression::Tuple(_)) {
self.emit_err(TypeCheckerError::finalize_statement_cannot_contain_tuples(
input.expression.span(),
))
}
self.visit_expression(&input.expression, return_type);
}
}

View File

@ -415,22 +415,6 @@ impl<'a> TypeChecker<'a> {
span,
)
}
// TODO: Fuse with defined check
// TODO: Do we need to check that an identifier does not correspond to a mapping type?
/// Emits an error if the type on the left-hand side of the assignment is invalid.
pub(crate) fn assert_valid_declaration_or_parameter_type(&self, type_: &Type, span: Span) {
match type_ {
// If the type is an empty tuple, return an error.
Type::Unit => self.emit_err(TypeCheckerError::unit_tuple(span)),
// If the type is a singleton tuple, return an error.
Type::Tuple(tuple) if tuple.len() == 1 => self.emit_err(TypeCheckerError::singleton_tuple(span)),
// The type can never be a mapping or error type.
Type::Mapping(_) | Type::Err => unreachable!(),
// Otherwise, the type is valid.
_ => (), // Do nothing
}
}
}
fn types_to_string(types: &[Type]) -> String {

View File

@ -312,6 +312,13 @@ create_messages!(
help: Some("Add a `public` modifier to the input variable declaration or remove the visibility modifier entirely.".to_string()),
}
@formatted
finalize_output_mode_must_be_public {
args: (),
msg: format!("An output of a finalize block must be public."),
help: Some("Add a `public` modifier to the output type declaration or remove the visibility modifier entirely.".to_string()),
}
@formatted
finalize_in_finalize {
args: (),
@ -467,7 +474,7 @@ create_messages!(
@formatted
nested_tuple_type {
args: (),
msg: format!("Nested tuple types are not supported."),
msg: format!("A tuple type cannot contain a tuple."),
help: None,
}
@ -498,4 +505,18 @@ create_messages!(
msg: format!("A finalize block cannot take in a tuple as input."),
help: None,
}
@formatted
nested_tuple_expression {
args: (),
msg: format!("A tuple expression cannot contain another tuple expression."),
help: None,
}
@formatted
finalize_statement_cannot_contain_tuples {
args: (),
msg: format!("A finalize statement cannot contain tuple expressions."),
help: None,
}
);