ソースを参照

Add signature enforcement for `Main.Run` and give it the symbol name `main`. (#3266)

For #2550.
Richard Smith 2 年 前
コミット
bfa5463e5b
28 ファイル変更186 行追加32 行削除
  1. 1 0
      toolchain/check/BUILD
  2. 16 0
      toolchain/check/handle_function.cpp
  3. 22 0
      toolchain/check/testdata/basics/fail_bad_run.carbon
  4. 19 0
      toolchain/check/testdata/basics/fail_bad_run_2.carbon
  5. 16 0
      toolchain/check/testdata/basics/run.carbon
  6. 17 0
      toolchain/check/testdata/basics/run_i32.carbon
  7. 14 14
      toolchain/check/testdata/function/call/fail_param_type.carbon
  8. 1 0
      toolchain/diagnostics/diagnostic_kind.def
  9. 1 0
      toolchain/lower/BUILD
  10. 13 3
      toolchain/lower/file_context.cpp
  11. 1 1
      toolchain/lower/testdata/array/assign_return_value.carbon
  12. 1 1
      toolchain/lower/testdata/array/base.carbon
  13. 1 1
      toolchain/lower/testdata/index/array_element_access.carbon
  14. 1 1
      toolchain/lower/testdata/index/tuple_element_access.carbon
  15. 1 1
      toolchain/lower/testdata/index/tuple_return_value_access.carbon
  16. 1 1
      toolchain/lower/testdata/struct/empty.carbon
  17. 1 1
      toolchain/lower/testdata/struct/member_access.carbon
  18. 1 1
      toolchain/lower/testdata/struct/nested_struct.carbon
  19. 1 1
      toolchain/lower/testdata/struct/one_entry.carbon
  20. 1 1
      toolchain/lower/testdata/struct/two_entries.carbon
  21. 1 1
      toolchain/lower/testdata/tuple/empty.carbon
  22. 1 1
      toolchain/lower/testdata/tuple/nested_tuple.carbon
  23. 1 1
      toolchain/lower/testdata/tuple/one_entry.carbon
  24. 1 1
      toolchain/lower/testdata/tuple/two_entries.carbon
  25. 1 1
      toolchain/lower/testdata/var/local.carbon
  26. 10 0
      toolchain/sem_ir/BUILD
  27. 22 0
      toolchain/sem_ir/entry_point.cpp
  28. 19 0
      toolchain/sem_ir/entry_point.h

+ 1 - 0
toolchain/check/BUILD

@@ -62,6 +62,7 @@ cc_library(
         "//toolchain/parse:tree",
         "//toolchain/parse:tree_node_location_translator",
         "//toolchain/sem_ir:builtin_kind",
+        "//toolchain/sem_ir:entry_point",
         "//toolchain/sem_ir:file",
         "//toolchain/sem_ir:node",
         "//toolchain/sem_ir:node_kind",

+ 16 - 0
toolchain/check/handle_function.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/sem_ir/entry_point.h"
 
 namespace Carbon::Check {
 
@@ -59,6 +60,21 @@ static auto BuildFunctionDeclaration(Context& context)
   auto decl_id = context.AddNode(
       SemIR::Node::FunctionDeclaration::Make(fn_node, function_id));
   context.declaration_name_stack().AddNameToLookup(name_context, decl_id);
+
+  if (SemIR::IsEntryPoint(context.semantics_ir(), function_id)) {
+    // TODO: Update this once valid signatures for the entry point are decided.
+    if (!context.semantics_ir().GetNodeBlock(param_refs_id).empty() ||
+        (return_slot_id.is_valid() &&
+         return_type_id !=
+             context.CanonicalizeType(SemIR::NodeId::BuiltinBoolType) &&
+         return_type_id != context.CanonicalizeTupleType(fn_node, {}))) {
+      CARBON_DIAGNOSTIC(InvalidMainRunSignature, Error,
+                        "Invalid signature for `Main.Run` function. Expected "
+                        "`fn ()` or `fn () -> i32`.");
+      context.emitter().Emit(fn_node, InvalidMainRunSignature);
+    }
+  }
+
   return {function_id, decl_id};
 }
 

+ 22 - 0
toolchain/check/testdata/basics/fail_bad_run.carbon

@@ -0,0 +1,22 @@
+// 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
+
+// CHECK:STDERR: fail_bad_run.carbon:[[@LINE+6]]:1: ERROR: Invalid signature for `Main.Run` function. Expected `fn ()` or `fn () -> i32`.
+// CHECK:STDERR: fn Run() -> String {}
+// CHECK:STDERR: ^
+// CHECK:STDERR: fail_bad_run.carbon:[[@LINE+3]]:21: ERROR: Missing `return` at end of function with declared return type.
+// CHECK:STDERR: fn Run() -> String {}
+// CHECK:STDERR:                     ^
+fn Run() -> String {}
+
+// CHECK:STDOUT: file "fail_bad_run.carbon" {
+// CHECK:STDOUT:   %Run = fn_decl @Run
+// CHECK:STDOUT:   %.loc13: type = tuple_type ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() -> %return: String {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT: }

+ 19 - 0
toolchain/check/testdata/basics/fail_bad_run_2.carbon

@@ -0,0 +1,19 @@
+// 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
+
+// CHECK:STDERR: fail_bad_run_2.carbon:[[@LINE+3]]:1: ERROR: Invalid signature for `Main.Run` function. Expected `fn ()` or `fn () -> i32`.
+// CHECK:STDERR: fn Run(n: i32) {}
+// CHECK:STDERR: ^
+fn Run(n: i32) {}
+
+// CHECK:STDOUT: file "fail_bad_run_2.carbon" {
+// CHECK:STDOUT:   %Run = fn_decl @Run
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run(%n: i32) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 16 - 0
toolchain/check/testdata/basics/run.carbon

@@ -0,0 +1,16 @@
+// 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
+
+fn Run() {}
+
+// CHECK:STDOUT: file "run.carbon" {
+// CHECK:STDOUT:   %Run = fn_decl @Run
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }

+ 17 - 0
toolchain/check/testdata/basics/run_i32.carbon

@@ -0,0 +1,17 @@
+// 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
+
+fn Run() -> i32 { return 0; }
+
+// CHECK:STDOUT: file "run_i32.carbon" {
+// CHECK:STDOUT:   %Run = fn_decl @Run
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Run() -> i32 {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc7: i32 = int_literal 0
+// CHECK:STDOUT:   return %.loc7
+// CHECK:STDOUT: }

+ 14 - 14
toolchain/check/testdata/function/call/fail_param_type.carbon

@@ -4,32 +4,32 @@
 //
 // AUTOUPDATE
 
-fn Run(a: i32) {}
+fn G(a: i32) {}
 
-fn Main() {
-  // CHECK:STDERR: fail_param_type.carbon:[[@LINE+6]]:6: ERROR: Cannot implicitly convert from `f64` to `i32`.
-  // CHECK:STDERR:   Run(1.0);
-  // CHECK:STDERR:      ^
+fn F() {
+  // CHECK:STDERR: fail_param_type.carbon:[[@LINE+6]]:4: ERROR: Cannot implicitly convert from `f64` to `i32`.
+  // CHECK:STDERR:   G(1.0);
+  // CHECK:STDERR:    ^
   // CHECK:STDERR: fail_param_type.carbon:[[@LINE-6]]:1: Initializing parameter 1 of function declared here.
-  // CHECK:STDERR: fn Run(a: i32) {}
+  // CHECK:STDERR: fn G(a: i32) {}
   // CHECK:STDERR: ^
-  Run(1.0);
+  G(1.0);
 }
 
 // CHECK:STDOUT: file "fail_param_type.carbon" {
-// CHECK:STDOUT:   %Run = fn_decl @Run
-// CHECK:STDOUT:   %Main = fn_decl @Main
+// CHECK:STDOUT:   %G = fn_decl @G
+// CHECK:STDOUT:   %F = fn_decl @F
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @Run(%a: i32) {
+// CHECK:STDOUT: fn @G(%a: i32) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: fn @Main() {
+// CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Run.ref = name_reference_untyped "Run", package.%Run
-// CHECK:STDOUT:   %.loc16_7: f64 = real_literal 10e-1
-// CHECK:STDOUT:   %.loc16_6: type = tuple_type ()
+// CHECK:STDOUT:   %G.ref = name_reference_untyped "G", package.%G
+// CHECK:STDOUT:   %.loc16_5: f64 = real_literal 10e-1
+// CHECK:STDOUT:   %.loc16_4: type = tuple_type ()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -125,6 +125,7 @@ CARBON_DIAGNOSTIC_KIND(RepeatedConst)
 CARBON_DIAGNOSTIC_KIND(InvalidArrayExpression)
 CARBON_DIAGNOSTIC_KIND(TypeNotIndexable)
 CARBON_DIAGNOSTIC_KIND(IndexOutOfBounds)
+CARBON_DIAGNOSTIC_KIND(InvalidMainRunSignature)
 CARBON_DIAGNOSTIC_KIND(StructInitElementCountMismatch)
 CARBON_DIAGNOSTIC_KIND(StructInitFieldNameMismatch)
 CARBON_DIAGNOSTIC_KIND(TupleIndexIntegerLiteral)

+ 1 - 0
toolchain/lower/BUILD

@@ -41,6 +41,7 @@ cc_library(
     deps = [
         "//common:check",
         "//common:vlog",
+        "//toolchain/sem_ir:entry_point",
         "//toolchain/sem_ir:file",
         "//toolchain/sem_ir:node",
         "//toolchain/sem_ir:node_kind",

+ 13 - 3
toolchain/lower/file_context.cpp

@@ -8,6 +8,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Sequence.h"
 #include "toolchain/lower/function_context.h"
+#include "toolchain/sem_ir/entry_point.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/node.h"
 #include "toolchain/sem_ir/node_kind.h"
@@ -107,11 +108,20 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id)
           ? GetType(function.return_type_id)
           : llvm::Type::getVoidTy(llvm_context());
 
+  std::string mangled_name;
+  if (SemIR::IsEntryPoint(semantics_ir(), function_id)) {
+    // TODO: Add an implicit `return 0` if `Run` doesn't return `i32`.
+    mangled_name = "main";
+  } else {
+    // TODO: Decide on a name mangling scheme.
+    mangled_name = semantics_ir().GetString(function.name_id);
+  }
+
   llvm::FunctionType* function_type =
       llvm::FunctionType::get(return_type, param_types, /*isVarArg=*/false);
-  auto* llvm_function = llvm::Function::Create(
-      function_type, llvm::Function::ExternalLinkage,
-      semantics_ir().GetString(function.name_id), llvm_module());
+  auto* llvm_function =
+      llvm::Function::Create(function_type, llvm::Function::ExternalLinkage,
+                             mangled_name, llvm_module());
 
   // Set up parameters and the return slot.
   for (auto [node_id, arg] :

+ 1 - 1
toolchain/lower/testdata/array/assign_return_value.carbon

@@ -21,7 +21,7 @@ fn Run() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: define void @Run() {
+// CHECK:STDOUT: define void @main() {
 // CHECK:STDOUT:   %t = alloca [2 x i32], align 4
 // CHECK:STDOUT:   %temp = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   call void @F(ptr %temp)

+ 1 - 1
toolchain/lower/testdata/array/base.carbon

@@ -15,7 +15,7 @@ fn Run() {
 // CHECK:STDOUT: ; ModuleID = 'base.carbon'
 // CHECK:STDOUT: source_filename = "base.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define void @Run() {
+// CHECK:STDOUT: define void @main() {
 // CHECK:STDOUT:   %a = alloca [1 x i32], align 4
 // CHECK:STDOUT:   %array.index = getelementptr inbounds [1 x i32], ptr %a, i32 0, i32 0
 // CHECK:STDOUT:   store i32 1, ptr %array.index, align 4

+ 1 - 1
toolchain/lower/testdata/index/array_element_access.carbon

@@ -33,7 +33,7 @@ fn Run() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: define void @Run() {
+// CHECK:STDOUT: define void @main() {
 // CHECK:STDOUT:   %a = alloca [2 x i32], align 4
 // CHECK:STDOUT:   %temp = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   call void @A(ptr %temp)

+ 1 - 1
toolchain/lower/testdata/index/tuple_element_access.carbon

@@ -14,7 +14,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'tuple_element_access.carbon'
 // CHECK:STDOUT: source_filename = "tuple_element_access.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %a = alloca { i32, i32, i32 }, align 8
 // CHECK:STDOUT:   %tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a, i32 0, i32 0
 // CHECK:STDOUT:   store i32 0, ptr %tuple.elem, align 4

+ 1 - 1
toolchain/lower/testdata/index/tuple_return_value_access.carbon

@@ -21,7 +21,7 @@ fn Run() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: define void @Run() {
+// CHECK:STDOUT: define void @main() {
 // CHECK:STDOUT:   %t = alloca i32, align 4
 // CHECK:STDOUT:   %temp = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   call void @F(ptr %temp)

+ 1 - 1
toolchain/lower/testdata/struct/empty.carbon

@@ -13,7 +13,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'empty.carbon'
 // CHECK:STDOUT: source_filename = "empty.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca {}, align 8
 // CHECK:STDOUT:   %y = alloca {}, align 8
 // CHECK:STDOUT:   ret i32 0

+ 1 - 1
toolchain/lower/testdata/struct/member_access.carbon

@@ -14,7 +14,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'member_access.carbon'
 // CHECK:STDOUT: source_filename = "member_access.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca { double, i32 }, align 8
 // CHECK:STDOUT:   %a = getelementptr inbounds { double, i32 }, ptr %x, i32 0, i32 0
 // CHECK:STDOUT:   store double 0.000000e+00, ptr %a, align 8

+ 1 - 1
toolchain/lower/testdata/struct/nested_struct.carbon

@@ -12,6 +12,6 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'nested_struct.carbon'
 // CHECK:STDOUT: source_filename = "nested_struct.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   ret i32 0
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/lower/testdata/struct/one_entry.carbon

@@ -13,7 +13,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'one_entry.carbon'
 // CHECK:STDOUT: source_filename = "one_entry.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca { i32 }, align 8
 // CHECK:STDOUT:   store { i32 } { i32 4 }, ptr %x, align 4
 // CHECK:STDOUT:   %y = alloca { i32 }, align 8

+ 1 - 1
toolchain/lower/testdata/struct/two_entries.carbon

@@ -13,7 +13,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon'
 // CHECK:STDOUT: source_filename = "two_entries.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %a = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 0
 // CHECK:STDOUT:   store i32 1, ptr %a, align 4

+ 1 - 1
toolchain/lower/testdata/tuple/empty.carbon

@@ -13,7 +13,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'empty.carbon'
 // CHECK:STDOUT: source_filename = "empty.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca {}, align 8
 // CHECK:STDOUT:   %y = alloca {}, align 8
 // CHECK:STDOUT:   ret i32 0

+ 1 - 1
toolchain/lower/testdata/tuple/nested_tuple.carbon

@@ -12,6 +12,6 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'nested_tuple.carbon'
 // CHECK:STDOUT: source_filename = "nested_tuple.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   ret i32 0
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/lower/testdata/tuple/one_entry.carbon

@@ -13,7 +13,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'one_entry.carbon'
 // CHECK:STDOUT: source_filename = "one_entry.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca { i32 }, align 8
 // CHECK:STDOUT:   store { i32 } { i32 1 }, ptr %x, align 4
 // CHECK:STDOUT:   %y = alloca { i32 }, align 8

+ 1 - 1
toolchain/lower/testdata/tuple/two_entries.carbon

@@ -13,7 +13,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon'
 // CHECK:STDOUT: source_filename = "two_entries.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x, i32 0, i32 0
 // CHECK:STDOUT:   store i32 12, ptr %tuple.elem, align 4

+ 1 - 1
toolchain/lower/testdata/var/local.carbon

@@ -12,7 +12,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'local.carbon'
 // CHECK:STDOUT: source_filename = "local.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: define i32 @Run() {
+// CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT:   %x = alloca i32, align 4
 // CHECK:STDOUT:   store i32 1, ptr %x, align 4
 // CHECK:STDOUT:   %1 = load i32, ptr %x, align 4

+ 10 - 0
toolchain/sem_ir/BUILD

@@ -66,6 +66,16 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "entry_point",
+    srcs = ["entry_point.cpp"],
+    hdrs = ["entry_point.h"],
+    deps = [
+        ":file",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_test(
     name = "file_test",
     size = "small",

+ 22 - 0
toolchain/sem_ir/entry_point.cpp

@@ -0,0 +1,22 @@
+// 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
+
+#include "toolchain/sem_ir/entry_point.h"
+
+#include "llvm/ADT/StringRef.h"
+
+namespace Carbon::SemIR {
+
+static constexpr llvm::StringLiteral EntryPointFunction = "Run";
+
+auto IsEntryPoint(const SemIR::File& file, SemIR::FunctionId function_id)
+    -> bool {
+  // TODO: Check if `file` is in the `Main` package.
+  auto& function = file.GetFunction(function_id);
+  // TODO: Check if `function` is in a namespace.
+  return function.name_id.is_valid() &&
+         file.GetString(function.name_id) == EntryPointFunction;
+}
+
+}  // namespace Carbon::SemIR

+ 19 - 0
toolchain/sem_ir/entry_point.h

@@ -0,0 +1,19 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_SEM_IR_ENTRY_POINT_H_
+#define CARBON_TOOLCHAIN_SEM_IR_ENTRY_POINT_H_
+
+#include "toolchain/sem_ir/file.h"
+
+namespace Carbon::SemIR {
+
+// Returns whether the specified function is the entry point function for the
+// program, `Main.Run`.
+auto IsEntryPoint(const SemIR::File& file, SemIR::FunctionId function_id)
+    -> bool;
+
+}  // namespace Carbon::SemIR
+
+#endif  // CARBON_TOOLCHAIN_SEM_IR_ENTRY_POINT_H_