Skip to content

Commit

Permalink
Add signature enforcement for Main.Run and give it the symbol name
Browse files Browse the repository at this point in the history
  • Loading branch information
zygoloid committed Oct 5, 2023
1 parent e15b24e commit 40cfcb1
Show file tree
Hide file tree
Showing 28 changed files with 184 additions and 31 deletions.
1 change: 1 addition & 0 deletions toolchain/check/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
16 changes: 16 additions & 0 deletions toolchain/check/handle_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/entry_point.h"

namespace Carbon::Check {

Expand Down Expand Up @@ -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};
}

Expand Down
22 changes: 22 additions & 0 deletions toolchain/check/testdata/basics/fail_bad_run.carbon
Original file line number Diff line number Diff line change
@@ -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 changes: 19 additions & 0 deletions toolchain/check/testdata/basics/fail_bad_run_2.carbon
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions toolchain/check/testdata/basics/run.carbon
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions toolchain/check/testdata/basics/run_i32.carbon
Original file line number Diff line number Diff line change
@@ -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: %.loc10: i32 = int_literal 0
// CHECK:STDOUT: return %.loc10
// CHECK:STDOUT: }
26 changes: 13 additions & 13 deletions toolchain/check/testdata/function/call/fail_param_type.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@
//
// AUTOUPDATE

fn Run(a: i32) {}
fn F(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 G() {
// CHECK:STDERR: fail_param_type.carbon:[[@LINE+6]]:4: ERROR: Cannot implicitly convert from `f64` to `i32`.
// CHECK:STDERR: F(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 F(a: i32) {}
// CHECK:STDERR: ^
Run(1.0);
F(1.0);
}

// CHECK:STDOUT: file "fail_param_type.carbon" {
// CHECK:STDOUT: %Run = fn_decl @Run
// CHECK:STDOUT: %Main = fn_decl @Main
// CHECK:STDOUT: %F = fn_decl @F
// CHECK:STDOUT: %G = fn_decl @G
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Run(%a: i32) {
// CHECK:STDOUT: fn @F(%a: i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Main() {
// CHECK:STDOUT: fn @G() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc16_7: f64 = real_literal 10e-1
// CHECK:STDOUT: %.loc16_6: type = tuple_type ()
// CHECK:STDOUT: %.loc16_5: f64 = real_literal 10e-1
// CHECK:STDOUT: %.loc16_4: type = tuple_type ()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
1 change: 1 addition & 0 deletions toolchain/diagnostics/diagnostic_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions toolchain/lower/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
16 changes: 13 additions & 3 deletions toolchain/lower/file_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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] :
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/array/assign_return_value.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/array/base.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/index/array_element_access.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/index/tuple_element_access.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/struct/empty.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/struct/member_access.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/struct/nested_struct.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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: }
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/struct/one_entry.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/struct/two_entries.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/tuple/empty.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/tuple/nested_tuple.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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: }
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/tuple/one_entry.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/tuple/two_entries.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion toolchain/lower/testdata/var/local.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions toolchain/sem_ir/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
21 changes: 21 additions & 0 deletions toolchain/sem_ir/entry_point.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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 file.GetString(function.name_id) == EntryPointFunction;
}

} // namespace Carbon::SemIR
Loading

0 comments on commit 40cfcb1

Please sign in to comment.