Summary
main's Slang Tests → Windows leg has been deterministically red since #557 merged:
Compiling melior-macro v0.19.5 (…melior?rev=52fa2611…)
Compiling melior v0.26.9 (…melior?rev=52fa2611…)
error[E0463]: can't find crate for `melior_macro`
--> …\melior\src\ir\attribute\attribute_like.rs:5:5
|
5 | use melior_macro::attribute_check_functions;
error: could not compile `melior` (lib) due to 1 previous error
Windows-only; all other platforms pass. Failing runs: 29910202004 (#557 merge), 29912409528 (0.1.6 bump). This is the load-time sequel to #550's link-time problem: same "CI is one cache-fingerprint change from breaking" structure, and the fingerprint change (a melior rev bump) arrived first here.
Root cause (confirmed by PE import/export analysis, no Windows runner needed)
melior-macro is a proc-macro ([lib] proc-macro = true) that links LLVM C++ via tblgen (features = ["llvm21-0"]). Since the #550 workaround switched Windows test builds to shared libstdc++, the built melior_macro.dll depends on GCC 16's libstdc++-6.dll at load time.
rustc loads proc-macros via libloading::Library::new → LoadLibraryExW(path, 0) → the default Windows DLL search order: application directory (rustc's sysroot bin/) → System32 → cwd → PATH. The chain that fails:
- The
x86_64-pc-windows-gnu rustc dist ships its own libgcc_s_seh-1.dll and libwinpthread-1.dll in the sysroot bin/ next to rustc.exe. The application directory beats PATH, so these stale copies win over MSYS2's /mingw64/bin for every dependency in the proc-macro's load chain.
- MSYS2 GCC 16's
libstdc++-6.dll (mingw-w64-x86_64-gcc-libs-16.1.0-5) imports three 64-bit-time symbols from winpthread — clock_gettime64, nanosleep64, pthread_cond_timedwait64 (added in mingw-w64 v14).
- Rust 1.97.1's bundled
libwinpthread-1.dll exports none of the three → LoadLibraryExW fails → rustc reports the generic E0463.
- The libgcc half is not broken: all 14 symbols GCC-16 libstdc++ imports from
libgcc_s_seh-1.dll are exported by rust's bundled copy.
The melior rev bump (#557, 193e825 → 52fa2611) is the trigger, not the cause — melior-macro is byte-identical across the revs; the bump invalidated the cargo cache and forced the first fresh melior compile (= first proc-macro load) since the #550 workaround landed. Prior green history was the cache riding a melior built when the proc-macro still linked libstdc++ statically. Deterministic: no good melior artifact can be cached while the load fails.
A plain revert of the melior rev is not viable: #557's bytes_attr macro expands to StringAttribute::from_bytes(…), which only exists in the new rev — reverting breaks solx-mlir on every platform and wouldn't durably fix Windows.
Fix
Delete the stale DLL in build-toolchain (PR: #590), next to the existing #550 workaround:
rm -fv "$(rustc --print sysroot)/bin/libwinpthread-1.dll"
Verified safe: nothing in the sysroot bin/ imports winpthread (rustc.exe, rustdoc.exe, rustc_driver-*.dll, std-*.dll all checked); rust-lld.exe has its own copies under rustlib/<triple>/bin/ (rust-lang/rust#128876), untouched. The dependency then falls through to MSYS2's v14 copy via PATH. PATH-based fixes cannot work (app dir always wins), nor can copying into rustlib/.../bin/self-contained/ (not on the DLL search path).
Structural follow-ups
Prior art
Summary
main's Slang Tests → Windows leg has been deterministically red since #557 merged:Windows-only; all other platforms pass. Failing runs: 29910202004 (#557 merge), 29912409528 (0.1.6 bump). This is the load-time sequel to #550's link-time problem: same "CI is one cache-fingerprint change from breaking" structure, and the fingerprint change (a melior rev bump) arrived first here.
Root cause (confirmed by PE import/export analysis, no Windows runner needed)
melior-macrois a proc-macro ([lib] proc-macro = true) that links LLVM C++ viatblgen(features = ["llvm21-0"]). Since the #550 workaround switched Windows test builds to shared libstdc++, the builtmelior_macro.dlldepends on GCC 16'slibstdc++-6.dllat load time.rustc loads proc-macros via
libloading::Library::new→LoadLibraryExW(path, 0)→ the default Windows DLL search order: application directory (rustc's sysrootbin/) → System32 → cwd → PATH. The chain that fails:x86_64-pc-windows-gnurustc dist ships its ownlibgcc_s_seh-1.dllandlibwinpthread-1.dllin the sysrootbin/next torustc.exe. The application directory beats PATH, so these stale copies win over MSYS2's/mingw64/binfor every dependency in the proc-macro's load chain.libstdc++-6.dll(mingw-w64-x86_64-gcc-libs-16.1.0-5) imports three 64-bit-time symbols from winpthread —clock_gettime64,nanosleep64,pthread_cond_timedwait64(added in mingw-w64 v14).libwinpthread-1.dllexports none of the three →LoadLibraryExWfails → rustc reports the genericE0463.libgcc_s_seh-1.dllare exported by rust's bundled copy.The melior rev bump (#557,
193e825→52fa2611) is the trigger, not the cause —melior-macrois byte-identical across the revs; the bump invalidated the cargo cache and forced the first freshmeliorcompile (= first proc-macro load) since the #550 workaround landed. Prior green history was the cache riding ameliorbuilt when the proc-macro still linked libstdc++ statically. Deterministic: no goodmeliorartifact can be cached while the load fails.A plain revert of the melior rev is not viable: #557's
bytes_attrmacro expands toStringAttribute::from_bytes(…), which only exists in the new rev — reverting breakssolx-mliron every platform and wouldn't durably fix Windows.Fix
Delete the stale DLL in
build-toolchain(PR: #590), next to the existing #550 workaround:rm -fv "$(rustc --print sysroot)/bin/libwinpthread-1.dll"Verified safe: nothing in the sysroot
bin/imports winpthread (rustc.exe,rustdoc.exe,rustc_driver-*.dll,std-*.dllall checked);rust-lld.exehas its own copies underrustlib/<triple>/bin/(rust-lang/rust#128876), untouched. The dependency then falls through to MSYS2's v14 copy via PATH. PATH-based fixes cannot work (app dir always wins), nor can copying intorustlib/.../bin/self-contained/(not on the DLL search path).Structural follow-ups
NomicFoundation/meliorfork (pre-generate/vendor the ODS output, or move tobuild.rs+include!— build scripts are separate processes, so the class dies on all platforms). Makes this workaround deletable.bin/winpthread is imported by nothing there and shadows newer runtimes during in-process loads. Precedent for action: Incompatibility between rustc's libgcc_s_dw2-1.dll and MSYS2 mingw-w64-i686-gcc 11.3 rust-lang/rust#99534 (same class withlibgcc_s_dw2-1.dllvs MSYS2 gcc ≥ 11.3) was fixed by a bundled-mingw bump (Upgrade mingw-w64 on CI rust-lang/rust#100178). Even so, the lag window re-opens at every MSYS2 major, hence the CI-local workaround.gcc --versioninto the Windows cache key so toolchain drift underneath the cache is visible.Prior art
libgcc_s_dw2-1.dllbreaking MSYS2 gcc ≥ 11.3; same shadowing, opposite direction.rust-lld.exefor-pc-windows-gnutargets rust-lang/rust#128876 — why the DLLs are shipped (rust-lld); rustup no longer adds sysrootbinto PATH, so today the stale DLLs can only shadow via app-dir precedence during in-process loads — i.e. precisely the C++-linking-proc-macro case.libwinpthread-1.dllddnet/ddnet#10203, ruby-core (JMJT7OIYUAPI22W4PGTLSL772RYD3S7Y) — identical stale-winpthread failures outside Rust, both fixed by removing/replacing the stale DLL.