From 9919e0b2c9be631872c1bafd3cea94adf09ad331 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 17:59:30 +0300 Subject: [PATCH] cgen: fix makev.bat fails on windows-latest: TCC bootstrap crashes at runtime, Clang 20 fallback fails on vc/v_win.c (fixes #26958) --- makev.bat | 22 +++++++------------ vlib/builtin/builtin_windows.c.v | 7 +++++- vlib/v/gen/c/cheaders.v | 5 +++++ .../gen/c/cheaders_manual_stdlib_decls_test.v | 1 + .../windows_callconv_generated_c.c.must_have | 3 ++- ...ows_clang_bootstrap_regression.c.must_have | 6 ++++- 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/makev.bat b/makev.bat index fa17b498b..b50a2b1ba 100644 --- a/makev.bat +++ b/makev.bat @@ -164,6 +164,7 @@ clang -std=c99 -municode -g -w -o "%V_BOOTSTRAP%" "%V_C_FILE%" -ladvapi32 -lws2_ if %ERRORLEVEL% NEQ 0 ( echo In most cases, compile errors happen because the version of Clang installed is too old clang --version + if [!compiler!] == [] goto :gcc_strap goto :compile_error ) @@ -222,20 +223,13 @@ if exist "%InstallDir%/Common7/Tools/vsdevcmd.bat" ( set ObjFile=.v.c.obj -if not exist "%tcc_exe%" call :download_tcc -if exist "%tcc_exe%" ( - echo ^> Bootstrapping "%V_BOOTSTRAP%" from %V_C_FILE% with "!tcc_exe!" before compiling "%V_EXE%" with MSVC - "!tcc_exe!" -B"%tcc_dir%" -bt10 -g -w -o "%V_BOOTSTRAP%" "%V_C_FILE%" -ladvapi32 -lws2_32 -Wl,-stack=33554432 - if %ERRORLEVEL% NEQ 0 goto :compile_error -) else ( - echo ^> Attempting to build "%V_BOOTSTRAP%" from %V_C_FILE% with MSVC - cl.exe /volatile:ms /Fo%ObjFile% /W0 /MD /D_VBOOTSTRAP /F33554432 "%V_C_FILE%" user32.lib kernel32.lib advapi32.lib shell32.lib ws2_32.lib /link /nologo /out:"%V_BOOTSTRAP%" /incremental:no - if %ERRORLEVEL% NEQ 0 ( - echo In some cases, compile errors happen because of the MSVC compiler version - cl.exe - if exist %ObjFile% del %ObjFile% - goto :compile_error - ) +echo ^> Attempting to build "%V_BOOTSTRAP%" from %V_C_FILE% with MSVC +cl.exe /volatile:ms /Fo%ObjFile% /W0 /MD /D_VBOOTSTRAP /F33554432 "%V_C_FILE%" user32.lib kernel32.lib advapi32.lib shell32.lib ws2_32.lib /link /nologo /out:"%V_BOOTSTRAP%" /incremental:no +if %ERRORLEVEL% NEQ 0 ( + echo In some cases, compile errors happen because of the MSVC compiler version + cl.exe + if exist %ObjFile% del %ObjFile% + goto :compile_error ) echo ^> Compiling "%V_EXE%" with "%V_BOOTSTRAP%" diff --git a/vlib/builtin/builtin_windows.c.v b/vlib/builtin/builtin_windows.c.v index 1cfedc7b4..d5fb5b87f 100644 --- a/vlib/builtin/builtin_windows.c.v +++ b/vlib/builtin/builtin_windows.c.v @@ -7,6 +7,10 @@ module builtin #include #include +// Cast the V callback to the Windows SDK callback type. Clang 20 treats the +// otherwise-compatible struct pointer mismatch as a hard error for v_win.c. +#define v_set_unhandled_exception_filter(handler) SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)(handler)) + // See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types // See https://www.codeproject.com/KB/string/cppstringguide1.aspx pub type C.BOOL = int @@ -166,6 +170,7 @@ pub: type TopLevelExceptionFilter = fn (&ExceptionPointers) C.LONG fn C.SetUnhandledExceptionFilter(TopLevelExceptionFilter) voidptr +fn C.v_set_unhandled_exception_filter(TopLevelExceptionFilter) voidptr @[callconv: stdcall] fn unhandled_exception_handler(e &ExceptionPointers) C.LONG { @@ -188,7 +193,7 @@ fn add_unhandled_exception_handler() { // A vectored handler also sees first-chance exceptions that Windows APIs may // handle internally, which can lead to false-positive "Unhandled Exception" // reports. Register a top-level filter instead. - C.SetUnhandledExceptionFilter(unhandled_exception_handler) + C.v_set_unhandled_exception_filter(unhandled_exception_handler) } fn C.IsDebuggerPresent() bool diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index e53460aee..38ef98bc5 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -533,6 +533,11 @@ char *strchr(const char *str, int c); char *strrchr(const char *str, int c); int fseek(FILE *stream, long offset, int whence); isize getline(char **lineptr, size_t *n, FILE *stream); +#if defined(_WIN32) || defined(_WIN64) +int _fileno(FILE *stream); +FILE *_wfopen(const unsigned short *filename, const unsigned short *mode); +int _wremove(const unsigned short *path); +#endif #ifndef _IOFBF #define _IOFBF 0 #endif diff --git a/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v b/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v index 25f26905a..ff74d8de4 100644 --- a/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v +++ b/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v @@ -23,6 +23,7 @@ fn test_default_c_prelude_uses_manual_stdio_stdlib_string_and_stdarg_decls() { assert generated_c.contains('int vfprintf(FILE *stream, const char *format, va_list ap);'), generated_c assert generated_c.contains('int vsnprintf(char *str, size_t size, const char *format, va_list ap);'), generated_c + assert generated_c.contains('#if defined(_WIN32) || defined(_WIN64)\nint _fileno(FILE *stream);\nFILE *_wfopen(const unsigned short *filename, const unsigned short *mode);\nint _wremove(const unsigned short *path);\n#endif'), generated_c assert generated_c.contains('void perror(const char *str);'), generated_c assert generated_c.contains('int mkstemp(char *stemplate);'), generated_c diff --git a/vlib/v/gen/c/testdata/windows_callconv_generated_c.c.must_have b/vlib/v/gen/c/testdata/windows_callconv_generated_c.c.must_have index 918b6b263..565b406bf 100644 --- a/vlib/v/gen/c/testdata/windows_callconv_generated_c.c.must_have +++ b/vlib/v/gen/c/testdata/windows_callconv_generated_c.c.must_have @@ -1,6 +1,7 @@ #define VCALLCONV(name) __##name #define VCALLCONV(name) __attribute__((name)) #define C__LONG LONG +#define v_set_unhandled_exception_filter(handler) SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)(handler)) typedef C__LONG (VCALLCONV(stdcall) *TopLevelExceptionFilter)(ExceptionPointers*); VV_LOC C__LONG VCALLCONV(stdcall) builtin__unhandled_exception_handler(ExceptionPointers* e); -SetUnhandledExceptionFilter(builtin__unhandled_exception_handler); +v_set_unhandled_exception_filter(builtin__unhandled_exception_handler); diff --git a/vlib/v/gen/c/testdata/windows_clang_bootstrap_regression.c.must_have b/vlib/v/gen/c/testdata/windows_clang_bootstrap_regression.c.must_have index f3afae5e7..d06605b15 100644 --- a/vlib/v/gen/c/testdata/windows_clang_bootstrap_regression.c.must_have +++ b/vlib/v/gen/c/testdata/windows_clang_bootstrap_regression.c.must_have @@ -9,11 +9,15 @@ _INTSIZEOF(t)) - \ _INTSIZEOF(t))) #elif defined(__MINGW32__) || defined(__MINGW64__) || (defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) FILE* __cdecl __acrt_iob_func(unsigned index); +int _fileno(FILE *stream); +FILE *_wfopen(const unsigned short *filename, const unsigned short *mode); +int _wremove(const unsigned short *path); #if defined(_MSC_VER) && !defined(__clang__) #define C__LONG LONG +#define v_set_unhandled_exception_filter(handler) SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)(handler)) typedef C__LONG (VCALLCONV(stdcall) *TopLevelExceptionFilter)(ExceptionPointers*); VV_LOC C__LONG VCALLCONV(stdcall) builtin__unhandled_exception_handler(ExceptionPointers* e); -SetUnhandledExceptionFilter(builtin__unhandled_exception_handler); +v_set_unhandled_exception_filter(builtin__unhandled_exception_handler); u32 res = FormatMessageW( atomic_compare_exchange_weak_ptr(((voidptr)(&ch->adr_written)), ((voidptr)(&nulladr)), ((isize)(-1))) atomic_compare_exchange_strong_ptr(((voidptr)(&ch->write_adr)), ((voidptr)(&wradr)), ((isize)(0))) -- 2.39.5