Преглед изворни кода

Add handling for return types. (#2596)

This starts handling return types on functions, and comparing types with `return` statements.

Note, errors remain poor because the type literal is currently associated with a builtin, losing the parse_node that specified it. This means we don't have the original source location to associate with, even though it may be helpful to point at the type in source. We could point at the signature overall, but my leaning is that we wouldn't want that long-term, so TODOs for now and may want to change a little about how the parse node is tracked once things are a little further along.
Jon Ross-Perkins пре 3 година
родитељ
комит
2adaeee2ba

+ 4 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -102,6 +102,10 @@ CARBON_DIAGNOSTIC_KIND(PreviousDefinition)
 CARBON_DIAGNOSTIC_KIND(NoMatchingCall)
 CARBON_DIAGNOSTIC_KIND(CallArgCountMismatch)
 CARBON_DIAGNOSTIC_KIND(CallArgTypeMismatch)
+CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpression)
+CARBON_DIAGNOSTIC_KIND(ReturnStatementImplicitNote)
+CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpression)
+CARBON_DIAGNOSTIC_KIND(ReturnStatementTypeMismatch)
 
 // ============================================================================
 // Other diagnostics

+ 1 - 0
toolchain/semantics/BUILD

@@ -80,6 +80,7 @@ cc_library(
         "//common:check",
         "//common:ostream",
         "//common:vlog",
+        "//toolchain/diagnostics:diagnostic_kind",
         "//toolchain/lexer:numeric_literal",
         "//toolchain/lexer:token_kind",
         "//toolchain/lexer:tokenized_buffer",

+ 25 - 12
toolchain/semantics/semantics_ir.h

@@ -31,14 +31,20 @@ struct SemanticsCall {
 // A callable object.
 struct SemanticsCallable {
   auto Print(llvm::raw_ostream& out) const -> void {
-    out << "{param_ir: " << param_ir_id << ", param_refs: " << param_refs_id
-        << "}";
+    out << "{param_ir: " << param_ir_id << ", param_refs: " << param_refs_id;
+    if (return_type_id.is_valid()) {
+      out << ", return_type: " << return_type_id;
+    }
+    out << "}";
   }
 
   // The full IR for parameters.
   SemanticsNodeBlockId param_ir_id;
   // A block containing a single reference node per parameter.
   SemanticsNodeBlockId param_refs_id;
+  // The return type. This will be invalid if the return type wasn't specified.
+  // The IR corresponding to the return type will be in a node block.
+  SemanticsNodeId return_type_id;
 };
 
 struct SemanticsRealLiteral {
@@ -83,28 +89,25 @@ class SemanticsIR {
     node_blocks_.resize(1);
   }
 
-  // Returns the requested node.
-  auto GetNode(SemanticsNodeId node_id) const -> const SemanticsNode& {
-    return nodes_[node_id.index];
-  }
-
-  // Returns the type of the requested node.
-  auto GetType(SemanticsNodeId node_id) -> SemanticsNodeId {
-    return GetNode(node_id).type();
-  }
-
+  // Adds a call, returning an ID to reference it.
   auto AddCall(SemanticsCall call) -> SemanticsCallId {
     SemanticsCallId id(calls_.size());
     calls_.push_back(call);
     return id;
   }
 
+  // Adds a callable, returning an ID to reference it.
   auto AddCallable(SemanticsCallable callable) -> SemanticsCallableId {
     SemanticsCallableId id(callables_.size());
     callables_.push_back(callable);
     return id;
   }
 
+  // Returns the requested callable.
+  auto GetCallable(SemanticsCallableId callable_id) -> SemanticsCallable {
+    return callables_[callable_id.index];
+  }
+
   // Adds an integer literal, returning an ID to reference it.
   auto AddIntegerLiteral(llvm::APInt integer_literal)
       -> SemanticsIntegerLiteralId {
@@ -122,6 +125,16 @@ class SemanticsIR {
     return node_id;
   }
 
+  // Returns the requested node.
+  auto GetNode(SemanticsNodeId node_id) const -> const SemanticsNode& {
+    return nodes_[node_id.index];
+  }
+
+  // Returns the type of the requested node.
+  auto GetType(SemanticsNodeId node_id) -> SemanticsNodeId {
+    return GetNode(node_id).type();
+  }
+
   // Adds an empty new node block, returning an ID to reference it and add
   // items.
   auto AddNodeBlock() -> SemanticsNodeBlockId {

+ 60 - 6
toolchain/semantics/semantics_parse_tree_handler.cpp

@@ -9,6 +9,7 @@
 
 #include "common/vlog.h"
 #include "llvm/Support/PrettyStackTrace.h"
+#include "toolchain/diagnostics/diagnostic_kind.h"
 #include "toolchain/lexer/token_kind.h"
 #include "toolchain/lexer/tokenized_buffer.h"
 #include "toolchain/parser/parse_node_kind.h"
@@ -320,7 +321,7 @@ auto SemanticsParseTreeHandler::HandleCallExpression(ParseTree::Node parse_node)
     }
 
     auto callable_id = name_node.GetAsFunctionDeclaration();
-    auto callable = semantics_->callables_[callable_id.index];
+    auto callable = semantics_->GetCallable(callable_id);
 
     if (!TryTypeConversionOnArgs(call_expr_parse_node, ir_id, refs_id,
                                  name_node.parse_node(),
@@ -474,6 +475,7 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinition(
   auto decl_id =
       node_stack_.PopForNodeId(ParseNodeKind::FunctionDefinitionStart);
 
+  return_scope_stack_.pop_back();
   PopScope();
   auto block_id = node_block_stack_.Pop();
   AddNode(SemanticsNode::MakeFunctionDefinition(parse_node, decl_id, block_id));
@@ -484,14 +486,21 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinition(
 
 auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
     ParseTree::Node parse_node) -> bool {
+  SemanticsNodeId return_type_id = SemanticsNodeId::Invalid;
+  if (parse_tree_->node_kind(node_stack_.PeekParseNode()) ==
+      ParseNodeKind::ReturnType) {
+    return_type_id = node_stack_.PopForNodeId(ParseNodeKind::ReturnType);
+  }
   node_stack_.PopForSoloParseNode(ParseNodeKind::ParameterList);
   auto [param_ir_id, param_refs_id] = finished_params_stack_.pop_back_val();
   auto name_node = node_stack_.PopForSoloParseNode(ParseNodeKind::DeclaredName);
   auto fn_node =
       node_stack_.PopForSoloParseNode(ParseNodeKind::FunctionIntroducer);
 
-  auto callable_id = semantics_->AddCallable(
-      {.param_ir_id = param_ir_id, .param_refs_id = param_refs_id});
+  auto callable_id =
+      semantics_->AddCallable({.param_ir_id = param_ir_id,
+                               .param_refs_id = param_refs_id,
+                               .return_type_id = return_type_id});
   auto decl_id =
       AddNode(SemanticsNode::MakeFunctionDeclaration(fn_node, callable_id));
   // TODO: Propagate the type of the function.
@@ -499,6 +508,7 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
 
   node_block_stack_.Push();
   PushScope();
+  return_scope_stack_.push_back(decl_id);
   node_stack_.Push(parse_node, decl_id);
 
   return true;
@@ -772,14 +782,57 @@ auto SemanticsParseTreeHandler::HandlePrefixOperator(ParseTree::Node parse_node)
 
 auto SemanticsParseTreeHandler::HandleReturnStatement(
     ParseTree::Node parse_node) -> bool {
+  CARBON_CHECK(!return_scope_stack_.empty());
+  const auto& fn_node = semantics_->GetNode(return_scope_stack_.back());
+  const auto callable =
+      semantics_->GetCallable(fn_node.GetAsFunctionDeclaration());
+
   if (parse_tree_->node_kind(node_stack_.PeekParseNode()) ==
       ParseNodeKind::ReturnStatementStart) {
     node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart);
+
+    if (callable.return_type_id.is_valid()) {
+      // TODO: Stringify types, add a note pointing at the return
+      // type's parse node.
+      CARBON_DIAGNOSTIC(ReturnStatementMissingExpression, Error,
+                        "Must return a {0}.", SemanticsNodeId);
+      emitter_
+          ->Build(parse_node, ReturnStatementMissingExpression,
+                  callable.return_type_id)
+          .Emit();
+    }
+
     AddNodeAndPush(parse_node, SemanticsNode::MakeReturn(parse_node));
   } else {
-    auto arg = node_stack_.PopForNodeId();
+    const auto arg = node_stack_.PopForNodeId();
     auto arg_type = semantics_->GetType(arg);
     node_stack_.PopAndDiscardSoloParseNode(ParseNodeKind::ReturnStatementStart);
+
+    if (!callable.return_type_id.is_valid()) {
+      CARBON_DIAGNOSTIC(
+          ReturnStatementDisallowExpression, Error,
+          "No return expression should be provided in this context.");
+      CARBON_DIAGNOSTIC(ReturnStatementImplicitNote, Note,
+                        "There was no return type provided.");
+      emitter_->Build(parse_node, ReturnStatementDisallowExpression)
+          .Note(fn_node.parse_node(), ReturnStatementImplicitNote)
+          .Emit();
+    } else {
+      const auto new_type = CanTypeConvert(arg_type, callable.return_type_id);
+      if (!new_type.is_valid()) {
+        // TODO: Stringify types, add a note pointing at the return
+        // type's parse node.
+        CARBON_DIAGNOSTIC(ReturnStatementTypeMismatch, Error,
+                          "Cannot convert {0} to {1}.", SemanticsNodeId,
+                          SemanticsNodeId);
+        emitter_
+            ->Build(parse_node, ReturnStatementTypeMismatch, arg_type,
+                    callable.return_type_id)
+            .Emit();
+      }
+      arg_type = new_type;
+    }
+
     AddNodeAndPush(parse_node, SemanticsNode::MakeReturnExpression(
                                    parse_node, arg_type, arg));
   }
@@ -795,8 +848,9 @@ auto SemanticsParseTreeHandler::HandleReturnStatementStart(
 
 auto SemanticsParseTreeHandler::HandleReturnType(ParseTree::Node parse_node)
     -> bool {
-  emitter_->Emit(parse_node, SemanticsTodo, "HandleReturnType");
-  return false;
+  // Propagate the type expression.
+  node_stack_.Push(parse_node, node_stack_.PopForNodeId());
+  return true;
 }
 
 auto SemanticsParseTreeHandler::HandleSelfDeducedParameter(

+ 7 - 0
toolchain/semantics/semantics_parse_tree_handler.h

@@ -168,9 +168,16 @@ class SemanticsParseTreeHandler {
   // node in blocks here.
   SemanticsNodeBlockStack params_or_args_stack_;
 
+  // Completed parameters that are held temporarily on a side-channel for a
+  // function. This can't use node_stack_ because it has space for only one
+  // value, whereas parameters return two values.
   llvm::SmallVector<std::pair<SemanticsNodeBlockId, SemanticsNodeBlockId>>
       finished_params_stack_;
 
+  // A stack of return scopes; i.e., targets for `return`. Inside a function,
+  // this will be a FunctionDeclaration.
+  llvm::SmallVector<SemanticsNodeId> return_scope_stack_;
+
   // A stack for scope context.
   llvm::SmallVector<ScopeStackEntry> scope_stack_;
 

+ 2 - 2
toolchain/semantics/testdata/operators/binary_op.carbon

@@ -8,7 +8,7 @@
 // CHECK:STDOUT: calls: [
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: callables: [
-// CHECK:STDOUT:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0, return_type: node2},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: integer_literals: [
 // CHECK:STDOUT:   12,
@@ -51,6 +51,6 @@
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 
-fn Main() {
+fn Main() -> i32 {
   return 12 + 34;
 }

+ 2 - 2
toolchain/semantics/testdata/operators/fail_type_mismatch.carbon

@@ -8,7 +8,7 @@
 // CHECK:STDOUT: calls: [
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: callables: [
-// CHECK:STDOUT:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0, return_type: node2},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: integer_literals: [
 // CHECK:STDOUT:   12,
@@ -51,7 +51,7 @@
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 
-fn Main() {
+fn Main() -> i32 {
   // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch.carbon:[[@LINE+1]]:13: Type mismatch: lhs is node2, rhs is node3
   return 12 + 3.4;
 }

+ 2 - 2
toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon

@@ -8,7 +8,7 @@
 // CHECK:STDOUT: calls: [
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: callables: [
-// CHECK:STDOUT:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0, return_type: node2},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: integer_literals: [
 // CHECK:STDOUT:   12,
@@ -56,7 +56,7 @@
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 
-fn Main() {
+fn Main() -> i32 {
   // The following line has two mismatches, but after the first, it shouldn't
   // keep erroring.
   // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/operators/fail_type_mismatch_once.carbon:[[@LINE+1]]:13: Type mismatch: lhs is node2, rhs is node3

+ 52 - 0
toolchain/semantics/testdata/return/fail_type_mismatch.carbon

@@ -0,0 +1,52 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{not} %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: calls: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: callables: [
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0, return_type: node2},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT:   {mantissa: 10, exponent: -1, is_decimal: 1},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node0, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node1, type: node1},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node2, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node3, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node4, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node5, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node6, type: node5},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: callable0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node7},
+// CHECK:STDOUT:   {kind: RealLiteral, arg0: real0, type: node3},
+// CHECK:STDOUT:   {kind: ReturnExpression, arg0: node9},
+// CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node7, arg1: block2},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node7,
+// CHECK:STDOUT:     node8,
+// CHECK:STDOUT:     node11,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node9,
+// CHECK:STDOUT:     node10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+fn Main() -> i32 {
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/return/fail_type_mismatch.carbon:[[@LINE+1]]:13: Cannot convert node3 to node2.
+  return 1.0;
+}

+ 53 - 0
toolchain/semantics/testdata/return/fail_value_disallowed.carbon

@@ -0,0 +1,53 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{not} %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: calls: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: callables: [
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT:   0,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node0, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node1, type: node1},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node2, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node3, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node4, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node5, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node6, type: node5},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: callable0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node7},
+// CHECK:STDOUT:   {kind: IntegerLiteral, arg0: int0, type: node2},
+// CHECK:STDOUT:   {kind: ReturnExpression, arg0: node9, type: node2},
+// CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node7, arg1: block2},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node7,
+// CHECK:STDOUT:     node8,
+// CHECK:STDOUT:     node11,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node9,
+// CHECK:STDOUT:     node10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+fn Main() {
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/return/fail_value_disallowed.carbon:[[@LINE+2]]:11: No return expression should be provided in this context.
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/return/fail_value_disallowed.carbon:[[@LINE-2]]:1: There was no return type provided.
+  return 0;
+}

+ 49 - 0
toolchain/semantics/testdata/return/fail_value_missing.carbon

@@ -0,0 +1,49 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{not} %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs_size: 1
+// CHECK:STDOUT: calls: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: callables: [
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0, return_type: node2},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: integer_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: real_literals: [
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: strings: [
+// CHECK:STDOUT:   Main,
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: nodes: [
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node0, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node1, type: node1},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node2, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node3, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node4, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node5, type: node0},
+// CHECK:STDOUT:   {kind: CrossReference, arg0: ir0, arg1: node6, type: node5},
+// CHECK:STDOUT:   {kind: FunctionDeclaration, arg0: callable0},
+// CHECK:STDOUT:   {kind: BindName, arg0: str0, arg1: node7},
+// CHECK:STDOUT:   {kind: Return},
+// CHECK:STDOUT:   {kind: FunctionDefinition, arg0: node7, arg1: block2},
+// CHECK:STDOUT: ]
+// CHECK:STDOUT: node_blocks: [
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node7,
+// CHECK:STDOUT:     node8,
+// CHECK:STDOUT:     node10,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT:   [
+// CHECK:STDOUT:     node9,
+// CHECK:STDOUT:   ],
+// CHECK:STDOUT: ]
+
+fn Main() -> i32 {
+  // CHECK:STDERR: {{.*}}/toolchain/semantics/testdata/return/fail_value_missing.carbon:[[@LINE+1]]:9: Must return a node2.
+  return;
+}

+ 0 - 0
toolchain/semantics/testdata/return/trivial.carbon → toolchain/semantics/testdata/return/no_value.carbon


+ 2 - 2
toolchain/semantics/testdata/return/literal.carbon → toolchain/semantics/testdata/return/value.carbon

@@ -8,7 +8,7 @@
 // CHECK:STDOUT: calls: [
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: callables: [
-// CHECK:STDOUT:   {param_ir: block0, param_refs: block0},
+// CHECK:STDOUT:   {param_ir: block0, param_refs: block0, return_type: node2},
 // CHECK:STDOUT: ]
 // CHECK:STDOUT: integer_literals: [
 // CHECK:STDOUT:   0,
@@ -46,6 +46,6 @@
 // CHECK:STDOUT:   ],
 // CHECK:STDOUT: ]
 
-fn Main() {
+fn Main() -> i32 {
   return 0;
 }