Add support for return; (#678)

This creates a ReturnExpression. A separate change should enforce that an implicit return of `()` is only allowed in functions that have an implicit return type of `()`, and I think the structure taken here should ease that.
This commit is contained in:
Jon Meow
2021-08-02 16:30:08 -07:00
committed by GitHub
parent b1993a6cd0
commit 6ae9cc3cf8
16 changed files with 114 additions and 33 deletions
+8 -3
View File
@@ -100,12 +100,17 @@ auto Expression::MakeContinuationTypeLiteral(int line_num)
return type;
}
auto Expression::MakeFunctionTypeLiteral(int line_num, const Expression* param,
const Expression* ret)
auto Expression::MakeFunctionTypeLiteral(int line_num,
const Expression* parameter,
const Expression* return_type,
bool is_omitted_return_type)
-> const Expression* {
auto* t = global_arena->New<Expression>();
t->line_num = line_num;
t->value = FunctionTypeLiteral({.parameter = param, .return_type = ret});
t->value =
FunctionTypeLiteral({.parameter = parameter,
.return_type = return_type,
.is_omitted_return_type = is_omitted_return_type});
return t;
}
+4 -2
View File
@@ -124,6 +124,7 @@ struct FunctionTypeLiteral {
static constexpr ExpressionKind Kind = ExpressionKind::FunctionTypeLiteral;
const Expression* parameter;
const Expression* return_type;
bool is_omitted_return_type;
};
struct BoolTypeLiteral {
@@ -162,8 +163,9 @@ struct Expression {
static auto MakeTypeTypeLiteral(int line_num) -> const Expression*;
static auto MakeIntTypeLiteral(int line_num) -> const Expression*;
static auto MakeBoolTypeLiteral(int line_num) -> const Expression*;
static auto MakeFunctionTypeLiteral(int line_num, const Expression* param,
const Expression* ret)
static auto MakeFunctionTypeLiteral(int line_num, const Expression* parameter,
const Expression* return_type,
bool is_omitted_return_type)
-> const Expression*;
static auto MakeContinuationTypeLiteral(int line_num) -> const Expression*;
@@ -21,7 +21,10 @@ void FunctionDefinition::PrintDepth(int depth, llvm::raw_ostream& out) const {
}
out << "]";
}
out << *param_pattern << " -> " << *return_type;
out << *param_pattern;
if (!is_omitted_return_type) {
out << " -> " << *return_type;
}
if (body) {
out << " {\n";
body->PrintDepth(depth, out);
@@ -25,12 +25,14 @@ struct FunctionDefinition {
FunctionDefinition(int line_num, std::string name,
std::vector<GenericBinding> deduced_params,
const TuplePattern* param_pattern,
const Pattern* return_type, const Statement* body)
const Pattern* return_type, bool is_omitted_return_type,
const Statement* body)
: line_num(line_num),
name(std::move(name)),
deduced_parameters(deduced_params),
param_pattern(param_pattern),
return_type(return_type),
is_omitted_return_type(is_omitted_return_type),
body(body) {}
void Print(llvm::raw_ostream& out) const { PrintDepth(-1, out); }
@@ -42,6 +44,7 @@ struct FunctionDefinition {
std::vector<GenericBinding> deduced_parameters;
const TuplePattern* param_pattern;
const Pattern* return_type;
bool is_omitted_return_type;
const Statement* body;
};
+12 -4
View File
@@ -117,11 +117,15 @@ auto Statement::MakeContinue(int line_num) -> const Statement* {
return s;
}
auto Statement::MakeReturn(int line_num, const Expression* e)
-> const Statement* {
auto Statement::MakeReturn(int line_num, const Expression* exp,
bool is_omitted_exp) -> const Statement* {
auto* s = global_arena->New<Statement>();
s->line_num = line_num;
s->value = Return({.exp = e});
if (exp == nullptr) {
CHECK(is_omitted_exp);
exp = Expression::MakeTupleLiteral(line_num, {});
}
s->value = Return({.exp = exp, .is_omitted_exp = is_omitted_exp});
return s;
}
@@ -230,7 +234,11 @@ void Statement::PrintDepth(int depth, llvm::raw_ostream& out) const {
}
break;
case StatementKind::Return:
out << "return " << *GetReturn().exp << ";";
if (GetReturn().is_omitted_exp) {
out << "return;";
} else {
out << "return " << *GetReturn().exp << ";";
}
break;
case StatementKind::Sequence:
GetSequence().stmt->PrintDepth(depth, out);
+3 -1
View File
@@ -60,6 +60,7 @@ struct If {
struct Return {
static constexpr StatementKind Kind = StatementKind::Return;
const Expression* exp;
bool is_omitted_exp;
};
struct Sequence {
@@ -120,7 +121,8 @@ struct Statement {
static auto MakeIf(int line_num, const Expression* cond,
const Statement* then_stmt, const Statement* else_stmt)
-> const Statement*;
static auto MakeReturn(int line_num, const Expression* e) -> const Statement*;
static auto MakeReturn(int line_num, const Expression* exp,
bool is_omitted_exp) -> const Statement*;
static auto MakeSequence(int line_num, const Statement* s1,
const Statement* s2) -> const Statement*;
static auto MakeBlock(int line_num, const Statement* s) -> const Statement*;
@@ -1093,8 +1093,9 @@ void StepStmt() {
Stack<Scope*> scopes;
scopes.Push(scope);
Stack<Action*> todo;
todo.Push(Action::MakeStatementAction(Statement::MakeReturn(
stmt->line_num, Expression::MakeTupleLiteral(stmt->line_num, {}))));
todo.Push(Action::MakeStatementAction(
Statement::MakeReturn(stmt->line_num, nullptr,
/*is_omitted_exp=*/true)));
todo.Push(Action::MakeStatementAction(stmt->GetContinuation().body));
Frame* continuation_frame =
global_arena->New<Frame>("__continuation", scopes, todo);
+12 -8
View File
@@ -56,7 +56,8 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* {
case ValKind::FunctionType:
return Expression::MakeFunctionTypeLiteral(
0, ReifyType(t->GetFunctionType().param, line_num),
ReifyType(t->GetFunctionType().ret, line_num));
ReifyType(t->GetFunctionType().ret, line_num),
/*is_omitted_return_type=*/false);
case ValKind::TupleValue: {
std::vector<FieldInitializer> args;
for (const TupleElement& field : t->GetTupleValue().elements) {
@@ -454,7 +455,8 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values)
auto pt = InterpExp(values, e->GetFunctionTypeLiteral().parameter);
auto rt = InterpExp(values, e->GetFunctionTypeLiteral().return_type);
auto new_e = Expression::MakeFunctionTypeLiteral(
e->line_num, ReifyType(pt, e->line_num), ReifyType(rt, e->line_num));
e->line_num, ReifyType(pt, e->line_num), ReifyType(rt, e->line_num),
/*is_omitted_return_type=*/false);
return TCExpression(new_e, Value::MakeTypeType(), types);
}
case ExpressionKind::IntTypeLiteral:
@@ -710,7 +712,9 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
} else {
ExpectType(s->line_num, "return", ret_type, res.type);
}
return TCStatement(Statement::MakeReturn(s->line_num, res.exp), types);
return TCStatement(Statement::MakeReturn(s->line_num, res.exp,
s->GetReturn().is_omitted_exp),
types);
}
case StatementKind::Continuation: {
TCStatement body_result =
@@ -742,8 +746,8 @@ auto CheckOrEnsureReturn(const Statement* stmt, bool void_return, int line_num)
-> const Statement* {
if (!stmt) {
if (void_return) {
return Statement::MakeReturn(line_num,
Expression::MakeTupleLiteral(line_num, {}));
return Statement::MakeReturn(line_num, nullptr,
/*is_omitted_exp=*/true);
} else {
FATAL_COMPILATION_ERROR(line_num)
<< "control-flow reaches end of non-void function without a return";
@@ -798,8 +802,8 @@ auto CheckOrEnsureReturn(const Statement* stmt, bool void_return, int line_num)
if (void_return) {
return Statement::MakeSequence(
stmt->line_num, stmt,
Statement::MakeReturn(stmt->line_num, Expression::MakeTupleLiteral(
stmt->line_num, {})));
Statement::MakeReturn(line_num, nullptr,
/*is_omitted_exp=*/true));
} else {
FATAL_COMPILATION_ERROR(stmt->line_num)
<< "control-flow reaches end of non-void function without a return";
@@ -835,7 +839,7 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values)
return global_arena->New<FunctionDefinition>(
f->line_num, f->name, f->deduced_parameters, f->param_pattern,
global_arena->New<ExpressionPattern>(ReifyType(return_type, f->line_num)),
body);
/*is_omitted_return_type=*/false, body);
}
auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def)
+23 -11
View File
@@ -92,6 +92,7 @@ void yy::parser::error(const location_type&, const std::string& message) {
%type <const Carbon::Statement*> statement
%type <const Carbon::Statement*> if_statement
%type <const Carbon::Statement*> optional_else
%type <std::pair<const Carbon::Expression*, bool>> return_expression
%type <const Carbon::Statement*> block
%type <const Carbon::Statement*> statement_list
%type <const Carbon::Expression*> expression
@@ -100,7 +101,7 @@ void yy::parser::error(const location_type&, const std::string& message) {
%type <std::vector<Carbon::GenericBinding>> deduced_param_list
%type <const Carbon::Pattern*> pattern
%type <const Carbon::Pattern*> non_expression_pattern
%type <const Carbon::Expression*> return_type
%type <std::pair<const Carbon::Expression*, bool>> return_type
%type <const Carbon::Expression*> paren_expression
%type <const Carbon::Expression*> tuple
%type <std::optional<std::string>> binding_lhs
@@ -264,7 +265,8 @@ expression:
{ $$ = Carbon::Expression::MakePrimitiveOperatorExpression(
yylineno, Carbon::Operator::Ptr, {$1}); }
| FNTY tuple return_type
{ $$ = Carbon::Expression::MakeFunctionTypeLiteral(yylineno, $2, $3); }
{ $$ = Carbon::Expression::MakeFunctionTypeLiteral(
yylineno, $2, $3.first, $3.second); }
;
designator: "." identifier { $$ = $2; }
;
@@ -415,8 +417,8 @@ statement:
{ $$ = Carbon::Statement::MakeBreak(yylineno); }
| CONTINUE ";"
{ $$ = Carbon::Statement::MakeContinue(yylineno); }
| RETURN expression ";"
{ $$ = Carbon::Statement::MakeReturn(yylineno, $2); }
| RETURN return_expression ";"
{ $$ = Carbon::Statement::MakeReturn(yylineno, $2.first, $2.second); }
| block
{ $$ = $1; }
| MATCH "(" expression ")" "{" clause_list "}"
@@ -440,6 +442,12 @@ optional_else:
| ELSE block
{ $$ = $2; }
;
return_expression:
// Empty
{ $$ = {Carbon::Expression::MakeTupleLiteral(yylineno, {}), true}; }
| expression
{ $$ = {$1, false}; }
;
statement_list:
// Empty
{ $$ = 0; }
@@ -452,9 +460,9 @@ block:
;
return_type:
// Empty
{ $$ = Carbon::Expression::MakeTupleLiteral(yylineno, {}); }
{ $$ = {Carbon::Expression::MakeTupleLiteral(yylineno, {}), true}; }
| ARROW expression %prec FNARROW
{ $$ = $2; }
{ $$ = {$2, false}; }
;
generic_binding:
identifier ":!" expression
@@ -486,21 +494,25 @@ function_definition:
FN identifier deduced_params maybe_empty_tuple_pattern return_type block
{
$$ = Carbon::FunctionDefinition(
yylineno, $2, $3, $4, Carbon::global_arena->New<Carbon::ExpressionPattern>($5), $6);
yylineno, $2, $3, $4,
Carbon::global_arena->New<Carbon::ExpressionPattern>($5.first),
$5.second, $6);
}
| FN identifier deduced_params maybe_empty_tuple_pattern DBLARROW expression ";"
{
$$ = Carbon::FunctionDefinition(
yylineno, $2, $3, $4,
Carbon::global_arena->New<Carbon::AutoPattern>(yylineno),
Carbon::Statement::MakeReturn(yylineno, $6));
yylineno, $2, $3, $4,
Carbon::global_arena->New<Carbon::AutoPattern>(yylineno), true,
Carbon::Statement::MakeReturn(yylineno, $6, false));
}
;
function_declaration:
FN identifier deduced_params maybe_empty_tuple_pattern return_type ";"
{
$$ = Carbon::FunctionDefinition(
yylineno, $2, $3, $4, Carbon::global_arena->New<Carbon::ExpressionPattern>($5), 0); }
yylineno, $2, $3, $4,
Carbon::global_arena->New<Carbon::ExpressionPattern>($5.first),
$5.second, nullptr); }
;
variable_declaration: identifier ":" pattern
{ $$ = Carbon::global_arena->New<Carbon::BindingPattern>(yylineno, $1, $3); }
+3
View File
@@ -66,6 +66,9 @@ TEST_LIST = [
"pattern_variable_fail",
"placeholder_variable",
"record1",
"return_empty_explicit",
"return_empty_implicit1",
"return_empty_implicit2",
"star",
"struct1",
"struct2",
@@ -0,0 +1,12 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
fn F() -> () {
return ();
}
fn main() -> Int {
F();
return 0;
}
@@ -0,0 +1 @@
result: 0
@@ -0,0 +1,11 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
fn F() {
}
fn main() -> Int {
F();
return 0;
}
@@ -0,0 +1 @@
result: 0
@@ -0,0 +1,12 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
fn F() {
return;
}
fn main() -> Int {
F();
return 0;
}
@@ -0,0 +1 @@
result: 0