mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-03 22:02:51 +01:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user