diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 689c5cd898..89acb0df60 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -26,7 +26,8 @@ pub const subsystem: ?SubSystem = blk: { if (is_test) { break :blk SubSystem.Console; } - if (@hasDecl(root, "WinMain") or + if (@hasDecl(root, "main") or + @hasDecl(root, "WinMain") or @hasDecl(root, "wWinMain") or @hasDecl(root, "WinMainCRTStartup") or @hasDecl(root, "wWinMainCRTStartup")) @@ -348,6 +349,21 @@ pub const Endian = enum { Little, }; +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const OutputMode = enum { + Exe, + Lib, + Obj, +}; + +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const LinkMode = enum { + Static, + Dynamic, +}; + /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const Version = struct { diff --git a/lib/std/special/start.zig b/lib/std/special/start.zig index 6d99618d2e..a93b01c290 100644 --- a/lib/std/special/start.zig +++ b/lib/std/special/start.zig @@ -19,21 +19,43 @@ const is_mips = switch (builtin.arch) { }; comptime { - if (builtin.link_libc) { - @export("main", main, .Strong); - } else if (builtin.os == .windows) { - @export("WinMainCRTStartup", WinMainCRTStartup, .Strong); - } else if (is_wasm and builtin.os == .freestanding) { - @export("_start", wasm_freestanding_start, .Strong); - } else if (builtin.os == .uefi) { - @export("EfiMain", EfiMain, .Strong); - } else if (is_mips) { - if (!@hasDecl(root, "__start")) @export("__start", _start, .Strong); - } else { - if (!@hasDecl(root, "_start")) @export("_start", _start, .Strong); + if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { + if (builtin.os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { + @export("_DllMainCRTStartup", _DllMainCRTStartup, .Strong); + } + } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { + if (builtin.link_libc and @hasDecl(root, "main")) { + if (@typeInfo(@typeOf(root.main)).Fn.calling_convention != .C) { + @export("main", main, .Weak); + } + } else if (builtin.os == .windows) { + if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) { + @export("WinMainCRTStartup", WinMainCRTStartup, .Strong); + } + } else if (is_wasm and builtin.os == .freestanding) { + if (!@hasDecl(root, "_start")) @export("_start", wasm_freestanding_start, .Strong); + } else if (builtin.os == .uefi) { + if (!@hasDecl(root, "EfiMain")) @export("EfiMain", EfiMain, .Strong); + } else if (is_mips) { + if (!@hasDecl(root, "__start")) @export("__start", _start, .Strong); + } else { + if (!@hasDecl(root, "_start")) @export("_start", _start, .Strong); + } } } +stdcallcc fn _DllMainCRTStartup( + hinstDLL: std.os.windows.HINSTANCE, + fdwReason: std.os.windows.DWORD, + lpReserved: std.os.windows.LPVOID, +) std.os.windows.BOOL { + if (@hasDecl(root, "DllMain")) { + return root.DllMain(hinstDLL, fdwReason, lpReserved); + } + + return std.os.windows.TRUE; +} + extern fn wasm_freestanding_start() void { // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. @@ -106,7 +128,7 @@ nakedcc fn _start() noreturn { @noInlineCall(posixCallMainAndExit); } -extern fn WinMainCRTStartup() noreturn { +stdcallcc fn WinMainCRTStartup() noreturn { @setAlignStack(16); if (!builtin.single_threaded) { _ = @import("start_windows_tls.zig"); diff --git a/lib/std/special/start_lib.zig b/lib/std/special/start_lib.zig deleted file mode 100644 index 994bc14fd5..0000000000 --- a/lib/std/special/start_lib.zig +++ /dev/null @@ -1,21 +0,0 @@ -// This file is included in the compilation unit when exporting a DLL on windows. - -const root = @import("root"); -const std = @import("std"); -const builtin = @import("builtin"); - -comptime { - @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong); -} - -stdcallcc fn _DllMainCRTStartup( - hinstDLL: std.os.windows.HINSTANCE, - fdwReason: std.os.windows.DWORD, - lpReserved: std.os.windows.LPVOID, -) std.os.windows.BOOL { - if (@hasDecl(root, "DllMain")) { - return root.DllMain(hinstDLL, fdwReason, lpReserved); - } - - return std.os.windows.TRUE; -} diff --git a/src/all_types.hpp b/src/all_types.hpp index 5a248f9cd2..b6310b02fb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2061,7 +2061,6 @@ struct CodeGen { ZigList global_vars; ZigFn *cur_fn; - ZigFn *main_fn; ZigFn *panic_fn; ZigFn *largest_frame_fn; @@ -2081,7 +2080,6 @@ struct CodeGen { uint32_t target_abi_index; uint32_t target_oformat_index; bool is_big_endian; - bool have_pub_main; bool have_c_main; bool have_winmain; bool have_winmain_crt_startup; diff --git a/src/analyze.cpp b/src/analyze.cpp index 7d75bef7d9..d616148596 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3304,17 +3304,6 @@ ZigFn *create_fn(CodeGen *g, AstNode *proto_node) { return fn_entry; } -static bool scope_is_root_decls(Scope *scope) { - while (scope) { - if (scope->id == ScopeIdDecls) { - ScopeDecls *scope_decls = (ScopeDecls *)scope; - return is_top_level_struct(scope_decls->container_type); - } - scope = scope->parent; - } - zig_unreachable(); -} - ZigType *get_test_fn_type(CodeGen *g) { if (g->test_fn_type) return g->test_fn_type; @@ -3353,7 +3342,6 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, G } static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { - ZigType *import = tld_fn->base.import; AstNode *source_node = tld_fn->base.source_node; if (source_node->type == NodeTypeFnProto) { AstNodeFnProto *fn_proto = &source_node->data.fn_proto; @@ -3433,12 +3421,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) { fn_table_entry->inferred_async_node = fn_table_entry->proto_node; } - - if (scope_is_root_decls(tld_fn->base.parent_scope) && import == g->root_import) { - if (g->have_pub_main && buf_eql_str(tld_fn->base.name, "main")) { - g->main_fn = fn_table_entry; - } - } } else if (source_node->type == NodeTypeTestDecl) { ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto); @@ -4813,26 +4795,6 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu ast_print(stderr, root_node, 0); } - if (source_kind == SourceKindRoot) { - // Look for main - for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { - AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); - - if (top_level_decl->type == NodeTypeFnDef) { - AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; - assert(proto_node->type == NodeTypeFnProto); - Buf *proto_name = proto_node->data.fn_proto.name; - - bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub); - if (is_pub) { - if (buf_eql_str(proto_name, "main")) { - g->have_pub_main = true; - } - } - } - } - } - for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl); diff --git a/src/codegen.cpp b/src/codegen.cpp index dc29e8f4b1..324a32485e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8229,9 +8229,9 @@ TargetSubsystem detect_subsystem(CodeGen *g) { if (g->zig_target->os == OsWindows) { if (g->have_dllmain_crt_startup || (g->out_type == OutTypeLib && g->is_dynamic)) return TargetSubsystemAuto; - if (g->have_c_main || g->have_pub_main || g->is_test_build) + if (g->have_c_main || g->is_test_build || g->have_winmain_crt_startup) return TargetSubsystemConsole; - if (g->have_winmain || g->have_winmain_crt_startup) + if (g->have_winmain) return TargetSubsystemWindows; } else if (g->zig_target->os == OsUefi) { return TargetSubsystemEfiApplication; @@ -8375,6 +8375,22 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little"; buf_appendf(contents, "pub const endian = %s;\n", endian_str); } + const char *out_type = nullptr; + switch (g->out_type) { + case OutTypeExe: + out_type = "Exe"; + break; + case OutTypeLib: + out_type = "Lib"; + break; + case OutTypeObj: + case OutTypeUnknown: // This happens when running the `zig builtin` command. + out_type = "Obj"; + break; + } + buf_appendf(contents, "pub const output_mode = OutputMode.%s;\n", out_type); + const char *link_type = g->is_dynamic ? "Dynamic" : "Static"; + buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); @@ -8441,6 +8457,8 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_buf(&cache_hash, compiler_id); cache_int(&cache_hash, g->build_mode); cache_bool(&cache_hash, g->strip_debug_symbols); + cache_int(&cache_hash, g->out_type); + cache_bool(&cache_hash, g->is_dynamic); cache_bool(&cache_hash, g->is_test_build); cache_bool(&cache_hash, g->is_single_threaded); cache_int(&cache_hash, g->zig_target->is_native); @@ -9148,38 +9166,6 @@ static Buf *get_resolved_root_src_path(CodeGen *g) { return resolved_path; } -static bool want_startup_code(CodeGen *g) { - // Test builds get handled separately. - if (g->is_test_build) - return false; - - // WASM freestanding can still have an entry point but other freestanding targets do not. - if (g->zig_target->os == OsFreestanding && !target_is_wasm(g->zig_target)) - return false; - - // Declaring certain export functions means skipping the start code - if (g->have_c_main || g->have_winmain || g->have_winmain_crt_startup) - return false; - - // If there is a pub main in the root source file, that means we need start code. - if (g->have_pub_main) { - return true; - } else { - if (g->zig_target->os == OsUefi) - return false; - } - - if (g->out_type == OutTypeExe) { - // For build-exe, we might add start code even though there is no pub main, so that the - // programmer gets the "no pub main" compile error. However if linking libc and there is - // a C source file, that might have main(). - return g->c_source_files.length == 0 || g->libc_link_lib == nullptr; - } - - // For objects and libraries, and we don't have pub main, no start code. - return false; -} - static void gen_root_source(CodeGen *g) { Buf *resolved_path = get_resolved_root_src_path(g); if (resolved_path == nullptr) @@ -9241,21 +9227,14 @@ static void gen_root_source(CodeGen *g) { assert(g->panic_fn != nullptr); } - if (!g->error_during_imports) { semantic_analyze(g); } report_errors_and_maybe_exit(g); - if (want_startup_code(g)) { + if (!g->is_test_build) { g->start_import = add_special_code(g, create_start_pkg(g, g->root_package), "start.zig"); } - if (g->zig_target->os == OsWindows && !g->have_dllmain_crt_startup && - g->out_type == OutTypeLib && g->is_dynamic) - { - g->start_import = add_special_code(g, create_start_pkg(g, g->root_package), "start_lib.zig"); - } - if (!g->error_during_imports) { semantic_analyze(g); } diff --git a/test/compare_output.zig b/test/compare_output.zig index 03f71d380e..35687b6a6c 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -6,7 +6,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("hello world with libc", \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: [*][*]u8) c_int { + \\pub export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ _ = c.puts("Hello, world!"); \\ return 0; \\} @@ -139,7 +139,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: [*][*]u8) c_int { + \\pub export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -286,7 +286,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ } \\} \\ - \\export fn main() c_int { + \\pub export fn main() c_int { \\ var array = [_]u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); @@ -314,7 +314,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: [*][*]u8) c_int { + \\pub export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 65ff8e4540..d3d439aeaf 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -6,6 +6,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export async fn foo() void {} , "tmp.zig:1:1: error: exported function cannot be async"); + cases.addExe( + "main missing name", + \\pub fn (main) void {} + , + "tmp.zig:1:5: error: missing function name", + ); + cases.addCase(x: { var tc = cases.create("@newStackCall on unsupported target", \\export fn entry() void { diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index a86811b27b..443ed7a0ee 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -5,7 +5,7 @@ pub fn addCases(ctx: *TestContext) !void { // hello world try ctx.testCompareOutputLibC( \\extern fn puts([*]const u8) void; - \\export fn main() c_int { + \\pub export fn main() c_int { \\ puts("Hello, world!"); \\ return 0; \\} @@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void { // function calling another function try ctx.testCompareOutputLibC( \\extern fn puts(s: [*]const u8) void; - \\export fn main() c_int { + \\pub export fn main() c_int { \\ return foo("OK"); \\} \\fn foo(s: [*]const u8) c_int { diff --git a/test/standalone/hello_world/hello_libc.zig b/test/standalone/hello_world/hello_libc.zig index c6f290f70e..5c0a233f1c 100644 --- a/test/standalone/hello_world/hello_libc.zig +++ b/test/standalone/hello_world/hello_libc.zig @@ -7,7 +7,7 @@ const c = @cImport({ const msg = "Hello, world!\n"; -export fn main(argc: c_int, argv: **u8) c_int { +pub export fn main(argc: c_int, argv: **u8) c_int { if (c.printf(msg) != @intCast(c_int, c.strlen(msg))) return -1; return 0; }