From fc36273a7ccca009ec9a9a9be6182eca32972305 Mon Sep 17 00:00:00 2001 From: dybucc <149513579+dybucc@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:32:43 +0100 Subject: [PATCH] windows: expose `cfg` for 64-bit `time_t` Use `gnu_time_bits64` on Windows x86 GNU to expose a 64-bit `time_t`. `time_t` is always 64-bits on Windows. The target environment matters not. There's a feature test macro to make it 32-bits wide. In `libc` we expose it as 32-bits in Windows x86 GNU. This is not the same default as in C. This `cfg` exposes the same default as in C. --- build.rs | 7 ++++--- libc-test/build.rs | 19 ++++++++++++++++++- libc-test/tests/windows_time.rs | 6 +++++- src/windows/mod.rs | 14 ++++++++++++-- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/build.rs b/build.rs index 966aefc022354..bb12cd19e019d 100644 --- a/build.rs +++ b/build.rs @@ -24,7 +24,8 @@ const ALLOWED_CFGS: &[&str] = &[ "freebsd15", // Corresponds to `_FILE_OFFSET_BITS=64` in glibc "gnu_file_offset_bits64", - // Corresponds to `_TIME_BITS=64` in glibc + // Corresponds to `_TIME_BITS=64` in glibc. Also used in x86 Windows with + // GNU to expose a 64-bit `time_t`. "gnu_time_bits64", "libc_deny_warnings", // Corresponds to `__USE_TIME_BITS64` in UAPI @@ -144,7 +145,7 @@ fn main() { } if target_env == "gnu" - && target_os == "linux" + && matches!(target_os.as_str(), "linux" | "windows") && target_ptr_width == "32" && target_arch != "riscv32" && target_arch != "x86_64" @@ -173,7 +174,7 @@ fn main() { let (timebits, filebits) = match (tb_env.as_deref(), fb_env.as_deref()) { (Ok(_), Ok(_)) => panic!( "Do not set both libc_unstable_gnu_time_bits and \ - libc_unstable_gnu_file_offset_bits" + libc_unstable_gnu_file_offset_bits" ), (Err(_), Err(_)) => (defaultbits, defaultbits), (Ok(tb), Err(_)) if tb == "64" => (tb, tb), diff --git a/libc-test/build.rs b/libc-test/build.rs index 18d020363e132..b7b97e7c2035f 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -784,6 +784,23 @@ fn test_windows(target: &str) { } cfg.define("_WIN32_WINNT", Some("0x8000")); + let win_gnu_x86_time64 = match env::var("CARGO_CFG_LIBC_UNSTABLE_GNU_TIME_BITS") { + Ok(v) if v == "64" => true, + Ok(v) if v == "32" => false, + Ok(_) => { + panic!("Invalid value for `libc_unstable_gnu_time_bits`. Must be 32, 64 or unset."); + } + Err(_) => false, + }; + + // Needed for the Windows `time_t` test. + println!("cargo::rustc-check-cfg=cfg(gnu_time_bits64)"); + + if i686 && gnu && win_gnu_x86_time64 { + cfg.cfg("gnu_time_bits64", None); + println!("cargo::rustc-cfg=gnu_time_bits64"); + } + headers!( cfg, "direct.h", @@ -841,7 +858,7 @@ fn test_windows(target: &str) { "SSIZE_T" if !gnu => true, "ssize_t" if !gnu => true, // FIXME(windows): The size and alignment of this type are incorrect - "time_t" if gnu && i686 => true, + "time_t" if gnu && i686 && !win_gnu_x86_time64 => true, _ => false, }); diff --git a/libc-test/tests/windows_time.rs b/libc-test/tests/windows_time.rs index 6a674ff678ee9..bf1db469c62e7 100644 --- a/libc-test/tests/windows_time.rs +++ b/libc-test/tests/windows_time.rs @@ -11,7 +11,11 @@ /// functions that expect it as a parameter. #[test] fn test_bitwidth_store() { - if cfg!(all(target_arch = "x86", target_env = "gnu")) { + if cfg!(all( + target_arch = "x86", + target_env = "gnu", + not(gnu_time_bits64), + )) { assert_eq!(size_of::(), 4); } else { assert_eq!(size_of::(), 8); diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 7698d51d7e6fa..e6a77a84b15d4 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -19,7 +19,13 @@ pub type clock_t = i32; pub type errno_t = c_int; cfg_if! { - if #[cfg(all(target_arch = "x86", target_env = "gnu"))] { + // FIXME(1.0): Windows GNU has 64-bit `time_t` by default. In x86 this is + // wrongly bound. + if #[cfg(all( + target_arch = "x86", + target_env = "gnu", + not(gnu_time_bits64) + ))] { pub type time_t = i32; } else { pub type time_t = i64; @@ -395,7 +401,11 @@ extern "C" { // Under Windows x86 with GNU, `time_t` is still 32-bit wide on stable, so // these routines have to link with their 32-bit variants. cfg_if! { - if #[cfg(all(target_arch = "x86", target_env = "gnu"))] { + if #[cfg(all( + target_arch = "x86", + target_env = "gnu", + not(gnu_time_bits64), + ))] { #[link_name = "_ctime32"] pub fn ctime(sourceTime: *const time_t) -> *mut c_char; #[link_name = "_difftime32"]