Factor out AST node for function return types. (#912)

This enables us to stop treating the return type as a Pattern (which is really isn't), treat return types more consistently with other static types in the typechecker, and drop ReturnTypeContext.

Additional changes:
- Merge TypeCheckFunDef with TypeOfFunDef.
- Handle implicit conversions in `return` statements.
- Require function type literals to have an explicit `->`.
- Move consistency check for omitted returns from TypeChecker to ResolveControlFlow.
This commit is contained in:
Geoff Romer
2021-11-12 11:30:31 -08:00
committed by GitHub
parent a4aff26821
commit d854fb93cb
11 changed files with 255 additions and 229 deletions
@@ -13,22 +13,51 @@ using llvm::cast;
namespace Carbon {
// Resolves control-flow edges in the AST rooted at `statement`. `return`
// statements will resolve to `*function`, and `break` and `continue`
// statements will resolve to `*loop`. If either parameter is nullopt, that
// indicates a context where the corresponding statements are not permitted.
static void ResolveControlFlow(
Nonnull<Statement*> statement,
std::optional<Nonnull<const FunctionDeclaration*>> function,
std::optional<Nonnull<const Statement*>> loop) {
// Aggregate information about a function being analyzed.
struct FunctionData {
// The function declaration.
Nonnull<FunctionDeclaration*> declaration;
// True if the function has a deduced return type, and we've already seen
// a `return` statement in its body.
bool saw_return_in_auto = false;
};
// Resolves control-flow edges such as `Return::function()` and `Break::loop()`
// in the AST rooted at `statement`. `loop` is the innermost loop that
// statically encloses `statement`, or nullopt if there is no such loop.
// `function` carries information about the function body that `statement`
// belongs to, and that information may be updated by this call. `function`
// can be nullopt if `statement` does not belong to a function body, for
// example if it is part of a continuation body instead.
static void ResolveControlFlow(Nonnull<Statement*> statement,
std::optional<Nonnull<const Statement*>> loop,
std::optional<Nonnull<FunctionData*>> function) {
switch (statement->kind()) {
case Statement::Kind::Return:
case Statement::Kind::Return: {
if (!function.has_value()) {
FATAL_COMPILATION_ERROR(statement->source_loc())
<< "return is not within a function body";
}
cast<Return>(*statement).set_function(*function);
const ReturnTerm& function_return =
(*function)->declaration->return_term();
if (function_return.is_auto()) {
if ((*function)->saw_return_in_auto) {
FATAL_COMPILATION_ERROR(statement->source_loc())
<< "Only one return is allowed in a function with an `auto` "
"return type.";
}
(*function)->saw_return_in_auto = true;
}
auto& ret = cast<Return>(*statement);
ret.set_function((*function)->declaration);
if (ret.is_omitted_expression() != function_return.is_omitted()) {
FATAL_COMPILATION_ERROR(ret.source_loc())
<< ret << " should" << (function_return.is_omitted() ? " not" : "")
<< " provide a return value, to match the function's signature.";
}
return;
}
case Statement::Kind::Break:
if (!loop.has_value()) {
FATAL_COMPILATION_ERROR(statement->source_loc())
@@ -45,26 +74,26 @@ static void ResolveControlFlow(
return;
case Statement::Kind::If: {
auto& if_stmt = cast<If>(*statement);
ResolveControlFlow(&if_stmt.then_block(), function, loop);
ResolveControlFlow(&if_stmt.then_block(), loop, function);
if (if_stmt.else_block().has_value()) {
ResolveControlFlow(*if_stmt.else_block(), function, loop);
ResolveControlFlow(*if_stmt.else_block(), loop, function);
}
return;
}
case Statement::Kind::Block: {
auto& block = cast<Block>(*statement);
for (auto* block_statement : block.statements()) {
ResolveControlFlow(block_statement, function, loop);
ResolveControlFlow(block_statement, loop, function);
}
return;
}
case Statement::Kind::While:
ResolveControlFlow(&cast<While>(*statement).body(), function, statement);
ResolveControlFlow(&cast<While>(*statement).body(), statement, function);
return;
case Statement::Kind::Match: {
auto& match = cast<Match>(*statement);
for (Match::Clause& clause : match.clauses()) {
ResolveControlFlow(&clause.statement(), function, loop);
ResolveControlFlow(&clause.statement(), loop, function);
}
return;
}
@@ -88,7 +117,8 @@ void ResolveControlFlow(AST& ast) {
}
auto& function = cast<FunctionDeclaration>(*declaration);
if (function.body().has_value()) {
ResolveControlFlow(*function.body(), &function, std::nullopt);
FunctionData data = {.declaration = &function};
ResolveControlFlow(*function.body(), std::nullopt, &data);
}
}
}