Some cleanup

This commit is contained in:
d0cd 2022-10-21 17:16:52 -07:00
parent 678f8b02a1
commit af08c4f2e5
8 changed files with 28 additions and 18 deletions

View File

@ -117,7 +117,9 @@ impl ParserContext<'_> {
fn parse_return_statement(&mut self) -> Result<ReturnStatement> {
let start = self.expect(&Token::Return)?;
let expression = match self.token.token {
// If the next token is a semicolon, implicitly return a unit expression, `()`.
Token::Semicolon => Expression::Unit(UnitExpression { span: self.token.span }),
// Otherwise, attempt to parse an expression.
_ => self.parse_expression()?,
};
self.expect(&Token::Semicolon)?;

View File

@ -318,6 +318,6 @@ impl<'a> CodeGenerator<'a> {
}
fn visit_unit(&mut self, _input: &'a UnitExpression) -> (String, String) {
unreachable!("`UnitExpression`s should not exist in the AST at this phase of compilation.")
unreachable!("`UnitExpression`s should not be visited during code generation.")
}
}

View File

@ -712,4 +712,12 @@ 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()));
}
Some(Type::Unit)
}
}

View File

@ -73,11 +73,10 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
for Member { identifier, type_ } in input.members.iter() {
// Check that the member type is not a tuple.
if matches!(type_, Type::Tuple(_)) {
self.emit_err(if input.is_record {
TypeCheckerError::record_cannot_contain_tuple(identifier.span)
} else {
TypeCheckerError::struct_cannot_contain_tuple(identifier.span)
});
self.emit_err(TypeCheckerError::composite_data_type_cannot_contain_tuple(
if input.is_record { "record" } else { "struct" },
identifier.span,
));
}
// Ensure that there are no record members.
self.assert_member_is_not_record(identifier.span, input.identifier.name, type_);

View File

@ -378,6 +378,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
false => f.output_type.clone(),
});
// Set the `has_return` flag.
self.has_return = true;
// Check that the return expression is not a nested tuple.
@ -389,6 +390,11 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
}
}
// Set the `is_return` flag.
self.is_return = true;
// Type check the associated expression.
self.visit_expression(&input.expression, return_type);
// Unset the `is_return` flag.
self.is_return = false;
}
}

View File

@ -41,6 +41,8 @@ pub struct TypeChecker<'a> {
pub(crate) is_finalize: bool,
/// Whether or not we are currently traversing an imported program.
pub(crate) is_imported: bool,
/// Whether or not we are currently traversing a return statement.
pub(crate) is_return: bool,
}
const BOOLEAN_TYPE: Type = Type::Boolean;
@ -98,6 +100,7 @@ impl<'a> TypeChecker<'a> {
has_finalize: false,
is_finalize: false,
is_imported: false,
is_return: false,
}
}

View File

@ -412,8 +412,7 @@ fn analyze_source_file(src: &str, source_file_start_pos: BytePos) -> (Vec<BytePo
let src_bytes = src.as_bytes();
while i < src.len() {
let i_usize = i;
let byte = src_bytes[i_usize];
let byte = src_bytes[i];
// How much to advance to get to the next UTF-8 char in the string.
let mut char_len = 1;

View File

@ -479,16 +479,9 @@ create_messages!(
}
@formatted
struct_cannot_contain_tuple {
args: (),
msg: format!("A struct cannot contain a tuple."),
help: None,
}
@formatted
record_cannot_contain_tuple {
args: (),
msg: format!("A record cannot contain a tuple."),
composite_data_type_cannot_contain_tuple {
args: (data_type: impl Display),
msg: format!("A {data_type} cannot contain a tuple."),
help: None,
}