This commit is contained in:
Pranav Gaddamadugu 2022-09-27 13:34:27 -07:00 committed by d0cd
parent 6d82f83c37
commit 605f675ff6
5 changed files with 72 additions and 42 deletions

View File

@ -650,11 +650,16 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output { fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output {
match input.elements.len() { match input.elements.len() {
0 => Some(self.assert_and_return_type(Type::Unit, expected, input.span())), 0 => {
1 => self.visit_expression(&input.elements[0], expected), self.emit_err(TypeCheckerError::unit_tuple(input.span()));
None
}
1 => {
self.emit_err(TypeCheckerError::singleton_tuple(input.span()));
None
}
_ => { _ => {
// Check the expected tuple types if they are known. // Check the expected tuple types if they are known.
if let Some(Type::Tuple(expected_types)) = expected { if let Some(Type::Tuple(expected_types)) = expected {
// Check actual length is equal to expected length. // Check actual length is equal to expected length.
if expected_types.len() != input.elements.len() { if expected_types.len() != input.elements.len() {

View File

@ -24,17 +24,23 @@ use leo_span::sym;
use std::collections::HashSet; use std::collections::HashSet;
// TODO: Generally, cleanup tyc logic. // TODO: Generally, cleanup tyc logic.
// TODO: Cleanup logic for tuples.
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> { impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_struct(&mut self, input: &'a Struct) { fn visit_struct(&mut self, input: &'a Struct) {
// Check for conflicting struct/record member names. // Check for conflicting struct/record member names.
let mut used = HashSet::new(); let mut used = HashSet::new();
if !input.members.iter().all(|Member { identifier, type_ }| { if !input
// TODO: Better spans. .members
// Check that the member types are valid. .iter()
self.assert_type_is_valid(input.span, type_); .all(|Member { identifier, type_} | {
used.insert(identifier.name) // TODO: Better spans.
}) { // Check that the member types are valid.
self.assert_type_is_defined(type_, ident.span);
self.assert_valid_declaration_or_parameter_type(type_, ident.span);
used.insert(ident.name)
})
{
self.emit_err(if input.is_record { self.emit_err(if input.is_record {
TypeCheckerError::duplicate_record_variable(input.name(), input.span()) TypeCheckerError::duplicate_record_variable(input.name(), input.span())
} else { } else {
@ -79,7 +85,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_mapping(&mut self, input: &'a Mapping) { fn visit_mapping(&mut self, input: &'a Mapping) {
// Check that a mapping's key type is valid. // Check that a mapping's key type is valid.
self.assert_type_is_valid(input.span, &input.key_type); self.assert_type_is_defined(&input.key_type, input.span);
// Check that a mapping's key type is not tuple types or mapping types. // Check that a mapping's key type is not tuple types or mapping types.
match input.key_type { match input.key_type {
Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "tuple", input.span)), Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("key", "tuple", input.span)),
@ -89,7 +95,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
} }
// Check that a mapping's value type is valid. // Check that a mapping's value type is valid.
self.assert_type_is_valid(input.span, &input.value_type); self.assert_type_is_defined(&input.value_type, input.span);
// Check that a mapping's value type is not tuple types or mapping types. // Check that a mapping's value type is not tuple types or mapping types.
match input.value_type { match input.value_type {
Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "tuple", input.span)), Type::Tuple(_) => self.emit_err(TypeCheckerError::invalid_mapping_type("value", "tuple", input.span)),
@ -134,9 +140,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Type check the function's parameters. // Type check the function's parameters.
function.input.iter().for_each(|input_var| { function.input.iter().for_each(|input_var| {
// Check that the type of input parameter is valid. // Check that the type of input parameter is defined.
self.assert_type_is_valid(input_var.span(), &input_var.type_()); self.assert_type_is_defined(&input_var.type_(), input_var.span());
self.assert_not_tuple(input_var.span(), &input_var.type_()); // Check that type of the input parameter is valid.
self.assert_valid_declaration_or_parameter_type(&input_var.type_(), input_var.span());
match self.is_transition_function { match self.is_transition_function {
// If the function is a transition function, then check that the parameter mode is not a constant. // If the function is a transition function, then check that the parameter mode is not a constant.
@ -168,8 +175,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
match output_type { match output_type {
Output::External(_) => {} // Do not type check external record function outputs. Output::External(_) => {} // Do not type check external record function outputs.
Output::Internal(output_type) => { Output::Internal(output_type) => {
// Check that the type of output is defined.
self.assert_type_is_defined(&output_type.type_, output_type.span);
// Check that the type of output is valid. // Check that the type of output is valid.
self.assert_type_is_valid(output_type.span, &output_type.type_); self.assert_valid_declaration_or_parameter_type(&output_type.type_, output_type.span);
// Check that the mode of the output is valid. // Check that the mode of the output is valid.
if output_type.mode == Mode::Const { if output_type.mode == Mode::Const {
@ -181,8 +190,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
self.visit_block(&function.block); self.visit_block(&function.block);
// Check that the return type is defined.
self.assert_type_is_defined(&function.output_type, function.span);
// Check that the return type is valid. // Check that the return type is valid.
self.assert_type_is_valid(function.span, &function.output_type); self.assert_valid_declaration_or_parameter_type(&function.output_type, function.span);
// If the function has a return type, then check that it has a return. // If the function has a return type, then check that it has a return.
if function.output_type != Type::Unit && !self.has_return { if function.output_type != Type::Unit && !self.has_return {
@ -225,9 +236,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
let scope_index = self.create_child_scope(); let scope_index = self.create_child_scope();
finalize.input.iter().for_each(|input_var| { finalize.input.iter().for_each(|input_var| {
// Check that the type of input parameter is defined.
self.assert_type_is_defined(&input_var.type_(), input_var.span());
// Check that the type of input parameter is valid. // Check that the type of input parameter is valid.
self.assert_type_is_valid(input_var.span(), &input_var.type_()); self.assert_valid_declaration_or_parameter_type(&input_var.type_(), input_var.span());
self.assert_not_tuple(input_var.span(), &input_var.type_());
// Check that the input parameter is not constant or private. // Check that the input parameter is not constant or private.
if input_var.mode() == Mode::Const || input_var.mode() == Mode::Private { if input_var.mode() == Mode::Const || input_var.mode() == Mode::Private {
@ -249,8 +261,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Type check the function's return type. // Type check the function's return type.
finalize.output.iter().for_each(|output_type| { 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());
// Check that the type of output is valid. // Check that the type of output is valid.
self.assert_type_is_valid(output_type.span(), &output_type.type_()); self.assert_valid_declaration_or_parameter_type(&output_type.type_(), output_type.span());
// Check that the mode of the output is valid. // Check that the mode of the output is valid.
if output_type.mode() == Mode::Const { if output_type.mode() == Mode::Const {
@ -267,8 +281,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Type check the finalize block. // Type check the finalize block.
self.visit_block(&finalize.block); self.visit_block(&finalize.block);
// Check that the return type is defined.
self.assert_type_is_defined(&finalize.output_type, finalize.span);
// Check that the return type is valid. // Check that the return type is valid.
self.assert_type_is_valid(finalize.span, &finalize.output_type); self.assert_valid_declaration_or_parameter_type(&finalize.output_type, finalize.span);
// If the function has a return type, then check that it has a return. // If the function has a return type, then check that it has a return.
if finalize.output_type != Type::Unit && !self.has_return { if finalize.output_type != Type::Unit && !self.has_return {

View File

@ -59,12 +59,6 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
_ => {} _ => {}
} }
match &var.type_ {
Type::Unit => self.emit_err(TypeCheckerError::assign_unit_expression_to_variable(input.span)),
Type::Tuple(tuple) if tuple.len() == 1 => self.emit_err(TypeCheckerError::singleton_tuple(input.span)),
_ => (), // Do nothing
}
Some(var.type_.clone()) Some(var.type_.clone())
} else { } else {
self.emit_err(TypeCheckerError::unknown_sym("variable", var_name.name, var_name.span)); self.emit_err(TypeCheckerError::unknown_sym("variable", var_name.name, var_name.span));
@ -192,17 +186,16 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
VariableType::Mut VariableType::Mut
}; };
// 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. // Check that the type of the definition is valid.
self.assert_type_is_valid(input.span, &input.type_); self.assert_valid_declaration_or_parameter_type(&input.type_, input.span);
match &input.type_ {
Type::Unit => self.emit_err(TypeCheckerError::assign_unit_expression_to_variable(input.span)),
Type::Tuple(tuple) if tuple.len() == 1 => self.emit_err(TypeCheckerError::singleton_tuple(input.span)),
_ => (), // Do nothing
}
// Check the expression on the left-hand side.
self.visit_expression(&input.value, &Some(input.type_.clone())); 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( if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
input.variable_name.name, input.variable_name.name,
VariableSymbol { VariableSymbol {

View File

@ -387,8 +387,8 @@ impl<'a> TypeChecker<'a> {
} }
} }
/// Emits an error if the type is not valid. /// Emits an error if the type or its constituent types are not defined.
pub(crate) fn assert_type_is_valid(&self, span: Span, type_: &Type) { pub(crate) fn assert_type_is_defined(&self, type_: &Type, span: Span) {
match type_ { match type_ {
// String types are temporarily disabled. // String types are temporarily disabled.
Type::String => { Type::String => {
@ -401,13 +401,13 @@ impl<'a> TypeChecker<'a> {
// Check that the constituent types of the tuple are valid. // Check that the constituent types of the tuple are valid.
Type::Tuple(tuple_type) => { Type::Tuple(tuple_type) => {
for type_ in tuple_type.iter() { for type_ in tuple_type.iter() {
self.assert_type_is_valid(span, type_) self.assert_type_is_defined(type_, span)
} }
} }
// Check that the constituent types of mapping are valid. // Check that the constituent types of mapping are valid.
Type::Mapping(mapping_type) => { Type::Mapping(mapping_type) => {
self.assert_type_is_valid(span, &mapping_type.key); self.assert_type_is_defined(&mapping_type.key, span);
self.assert_type_is_valid(span, &mapping_type.value); self.assert_type_is_defined(&mapping_type.value, span);
} }
_ => {} // Do nothing. _ => {} // Do nothing.
} }
@ -422,6 +422,22 @@ impl<'a> TypeChecker<'a> {
span, 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 { fn types_to_string(types: &[Type]) -> String {

View File

@ -453,19 +453,19 @@ create_messages!(
help: None, help: None,
} }
// TODO: Eventually update to warnings. // TODO: Better error messages for each tuple case. e.g tuple in function parameter, tuple in assignment, tuple in return, etc.
@formatted @formatted
singleton_tuple { singleton_tuple {
args: (), args: (),
msg: format!("Tuples of a single element are not allowed."), msg: format!("Singleton tuple expressions and tuple types are not allowed."),
help: None, help: None,
} }
// TODO: Eventually update to warnings.
@formatted @formatted
unit_tuple { unit_tuple {
args: (), args: (),
msg: format!("Tuples of a zero elements are not allowed."), msg: format!("Empty tuple expressions and tuple types are not allowed."),
help: None, help: None,
} }
); );