Bläddra i källkod

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.
Jon Meow 4 år sedan
förälder
incheckning
6ae9cc3cf8

+ 8 - 3
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<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
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*;
 

+ 4 - 1
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);

+ 4 - 1
executable_semantics/ast/function_definition.h

@@ -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
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<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
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*;

+ 3 - 2
executable_semantics/interpreter/interpreter.cpp

@@ -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
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<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
executable_semantics/syntax/parser.ypp

@@ -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 - 0
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",

+ 12 - 0
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;
+}

+ 1 - 0
executable_semantics/testdata/return_empty_explicit.golden

@@ -0,0 +1 @@
+result: 0

+ 11 - 0
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;
+}

+ 1 - 0
executable_semantics/testdata/return_empty_implicit1.golden

@@ -0,0 +1 @@
+result: 0

+ 12 - 0
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;
+}

+ 1 - 0
executable_semantics/testdata/return_empty_implicit2.golden

@@ -0,0 +1 @@
+result: 0