From 29717fefb8e951561bd51fdcea54ec62af3fd7dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 14 Oct 2026 15:30:56 -0700 Subject: [PATCH] start code: flatten and improve missing pub fn main message better error message when the programmer omits `main` or fails to make it public. closes #37113 --- lib/std/start.zig | 76 +++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index d77c093d23792f1422d4cfdc2e8d97a2af181b7d..2ed247f591505f8dc4db264e6f6018f0f5bab1b1 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -29,49 +29,54 @@ comptime { } } } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { - if (builtin.link_libc and @hasDecl(root, "main")) { + if (builtin.link_libc and (@hasDecl(root, "main") or std.os.requiresLibC())) { if (is_wasm) { @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" }); } else if (!@typeInfo(@TypeOf(root.main)).@"fn".attrs.@"callconv".eql(.c)) { @export(&main, .{ .name = "main" }); } - } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { - if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".attrs.@"callconv".eql(.c)) { - @export(&wWinMain, .{ .name = "wWinMain" }); - } - } else if (native_os == .windows) { - if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and - !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) - { - @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); - } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and - !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) - { - @compileError("WinMain not supported; declare wWinMain or main instead"); - } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and - !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) - { - @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); - } - } else if (native_os == .uefi) { - if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); - } else if (native_os == .wasi) { - const wasm_start_sym = switch (builtin.wasi_exec_model) { - .reactor => "_initialize", - .command => "_start", - }; - if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { + } else switch (native_os) { + .windows => { + if (builtin.link_libc and @hasDecl(root, "wWinMain")) { + if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".attrs.@"callconv".eql(.c)) { + @export(&wWinMain, .{ .name = "wWinMain" }); + } + } else if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and + !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) + { + @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); + } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and + !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) + { + @compileError("WinMain not supported; declare wWinMain or main instead"); + } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and + !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) + { + @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); + } + }, + + .uefi => { + if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); + }, + .wasi => { + const wasm_start_sym = switch (builtin.wasi_exec_model) { + .reactor => "_initialize", + .command => "_start", + }; + if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { + // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which + // case it's not required to provide an entrypoint such as main. + @export(&startWasi, .{ .name = wasm_start_sym }); + } + }, + .freestanding => if (is_wasm) { // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which // case it's not required to provide an entrypoint such as main. - @export(&startWasi, .{ .name = wasm_start_sym }); - } - } else if (is_wasm and native_os == .freestanding) { - // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which - // case it's not required to provide an entrypoint such as main. - if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name }); - } else switch (native_os) { + if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) + @export(&wasm_freestanding_start, .{ .name = start_sym_name }); + }, .other, - .freestanding, .vulkan, .opengl, .opencl, @@ -85,6 +90,7 @@ comptime { .psp, .vita, => {}, + else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), } } -- 2.54.0