diff --git a/executable_semantics/ast/expression.cpp b/executable_semantics/ast/expression.cpp index c7c72a190a69..3d64f069f321 100644 --- a/executable_semantics/ast/expression.cpp +++ b/executable_semantics/ast/expression.cpp @@ -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(); 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; } diff --git a/executable_semantics/ast/expression.h b/executable_semantics/ast/expression.h index c5cd788ee352..178a21ca4d15 100644 --- a/executable_semantics/ast/expression.h +++ b/executable_semantics/ast/expression.h @@ -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*; diff --git a/executable_semantics/ast/function_definition.cpp b/executable_semantics/ast/function_definition.cpp index 615e4510e86e..ec69180bb193 100644 --- a/executable_semantics/ast/function_definition.cpp +++ b/executable_semantics/ast/function_definition.cpp @@ -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); diff --git a/executable_semantics/ast/function_definition.h b/executable_semantics/ast/function_definition.h index 9a8e9f59d15c..d2b66f462720 100644 --- a/executable_semantics/ast/function_definition.h +++ b/executable_semantics/ast/function_definition.h @@ -25,12 +25,14 @@ struct FunctionDefinition { FunctionDefinition(int line_num, std::string name, std::vector 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 deduced_parameters; const TuplePattern* param_pattern; const Pattern* return_type; + bool is_omitted_return_type; const Statement* body; }; diff --git a/executable_semantics/ast/statement.cpp b/executable_semantics/ast/statement.cpp index 05cbb0d05573..754d715b0f9a 100644 --- a/executable_semantics/ast/statement.cpp +++ b/executable_semantics/ast/statement.cpp @@ -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(); 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); diff --git a/executable_semantics/ast/statement.h b/executable_semantics/ast/statement.h index e9e2cedf2ae7..7d630a6c314d 100644 --- a/executable_semantics/ast/statement.h +++ b/executable_semantics/ast/statement.h @@ -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*; diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 9ad47b3a8ae3..764aea731b97 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -1093,8 +1093,9 @@ void StepStmt() { Stack scopes; scopes.Push(scope); Stack 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("__continuation", scopes, todo); diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index 006dbff5c2ae..12499ba487ef 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -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 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( f->line_num, f->name, f->deduced_parameters, f->param_pattern, global_arena->New(ReifyType(return_type, f->line_num)), - body); + /*is_omitted_return_type=*/false, body); } auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def) diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 920150c0e89f..905614cd7306 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -92,6 +92,7 @@ void yy::parser::error(const location_type&, const std::string& message) { %type statement %type if_statement %type optional_else +%type > return_expression %type block %type statement_list %type expression @@ -100,7 +101,7 @@ void yy::parser::error(const location_type&, const std::string& message) { %type > deduced_param_list %type pattern %type non_expression_pattern -%type return_type +%type > return_type %type paren_expression %type tuple %type > 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($5), $6); + yylineno, $2, $3, $4, + Carbon::global_arena->New($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(yylineno), - Carbon::Statement::MakeReturn(yylineno, $6)); + yylineno, $2, $3, $4, + Carbon::global_arena->New(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($5), 0); } + yylineno, $2, $3, $4, + Carbon::global_arena->New($5.first), + $5.second, nullptr); } ; variable_declaration: identifier ":" pattern { $$ = Carbon::global_arena->New(yylineno, $1, $3); } diff --git a/executable_semantics/test_list.bzl b/executable_semantics/test_list.bzl index d15a9bc36abf..112e67b2835a 100644 --- a/executable_semantics/test_list.bzl +++ b/executable_semantics/test_list.bzl @@ -66,6 +66,9 @@ TEST_LIST = [ "pattern_variable_fail", "placeholder_variable", "record1", + "return_empty_explicit", + "return_empty_implicit1", + "return_empty_implicit2", "star", "struct1", "struct2", diff --git a/executable_semantics/testdata/return_empty_explicit.carbon b/executable_semantics/testdata/return_empty_explicit.carbon new file mode 100644 index 000000000000..0b6134341813 --- /dev/null +++ b/executable_semantics/testdata/return_empty_explicit.carbon @@ -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; +} diff --git a/executable_semantics/testdata/return_empty_explicit.golden b/executable_semantics/testdata/return_empty_explicit.golden new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/executable_semantics/testdata/return_empty_explicit.golden @@ -0,0 +1 @@ +result: 0 diff --git a/executable_semantics/testdata/return_empty_implicit1.carbon b/executable_semantics/testdata/return_empty_implicit1.carbon new file mode 100644 index 000000000000..f2b772ba61c0 --- /dev/null +++ b/executable_semantics/testdata/return_empty_implicit1.carbon @@ -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; +} diff --git a/executable_semantics/testdata/return_empty_implicit1.golden b/executable_semantics/testdata/return_empty_implicit1.golden new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/executable_semantics/testdata/return_empty_implicit1.golden @@ -0,0 +1 @@ +result: 0 diff --git a/executable_semantics/testdata/return_empty_implicit2.carbon b/executable_semantics/testdata/return_empty_implicit2.carbon new file mode 100644 index 000000000000..433d1bbf97a2 --- /dev/null +++ b/executable_semantics/testdata/return_empty_implicit2.carbon @@ -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; +} diff --git a/executable_semantics/testdata/return_empty_implicit2.golden b/executable_semantics/testdata/return_empty_implicit2.golden new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/executable_semantics/testdata/return_empty_implicit2.golden @@ -0,0 +1 @@ +result: 0