mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-09-20 10:18:35 +03:00
More tyc
This commit is contained in:
parent
6d82f83c37
commit
605f675ff6
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user