From d3e787ad8313544835d40738caabf10d3e491ee5 Mon Sep 17 00:00:00 2001 From: Pascal Severin Date: Thu, 17 Sep 2026 18:03:09 +0200 Subject: [PATCH] [M1-DET-04] Fix macOS + determinism CI: hello-baseline std::min deduction; crash-handler write() under Release -Werror MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS (Intel + arm64) jobs: samples/hello/hello-baseline.cpp:179 — std::min(emitted, baseline_.size()) mixes std::uint64_t with std::size_t. On macOS uint64_t is unsigned long long and size_t is unsigned long, so the template deduction is a hard AppleClang error. On Linux/Windows the two types alias, which is why every other P0 job compiled the same source. Fix: cast the size_t operand to uint64_t so both arguments are one type on every platform. Determinism check job: src/laige-core/logging.cpp:500 — (void)write(...) does not suppress glibc's warn_unused_result (the attribute is attached under fortification, i.e. optimized builds), and the engine's -Werror makes it fatal. That is why only the job's Release g++ tree (build-det-release) failed while Debug g++ and every clang tree compiled the same TU. Fix: consume the result — the crash notice is best-effort and there is no async-signal-safe fallback if the write fails (LOG-007). Verification (local: g++ 16.2.1 / clang++ 22.1.8, x86-64 Linux): - logging.cpp compiles clean under -Wall -Werror in Debug, Release, and Release+_FORTIFY_SOURCE=2 (the CI trigger); hello sample TUs clean under all four compiler/flag sets. - Full ctest: 82/82 (incl. hello_baseline_fpx/fp32 + the mismatch/truncated/malformed/missing fixtures, replay and detcheck suites). - The CI detcheck flow reproduced end to end: four targeted builds (g++ Debug, clang++ Debug, clang++ Debug+ASan, g++ Release), reference-baseline sanity on both backends, synthetic self-check, and pairs A (g++ Debug vs clang++ Debug) and B (ASan Debug vs Release) on both backends — all result=OK ticks=301. - The macOS deduction error cannot be reproduced off-AppleClang (uint64_t == size_t on Linux/Windows); the P0 macOS jobs are the regression guard for it, as for the prior macOS CI fixes (#33, #35, #37, #39). --- samples/hello/hello-baseline.cpp | 6 +++++- src/laige-core/logging.cpp | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/samples/hello/hello-baseline.cpp b/samples/hello/hello-baseline.cpp index a972ee7..fd465ef 100644 --- a/samples/hello/hello-baseline.cpp +++ b/samples/hello/hello-baseline.cpp @@ -176,7 +176,11 @@ std::string BaselineCheck::failText(std::uint64_t emitted) const { "\n run: " + lineB_; } if (emitted != baseline_.size()) { - const std::uint64_t at = std::min(emitted, baseline_.size()); + // Both arguments must be one type for std::min's template deduction: + // on platforms where uint64_t is not size_t (macOS: unsigned long + // long vs unsigned long) the mixed call is a hard compile error. + const std::uint64_t at = + std::min(emitted, static_cast(baseline_.size())); return "baseline stream length mismatch: the baseline has " + std::to_string(baseline_.size()) + " lines, the run produces " + std::to_string(emitted) + " (first missing/extra at tick " + diff --git a/src/laige-core/logging.cpp b/src/laige-core/logging.cpp index ab97043..4f702ff 100644 --- a/src/laige-core/logging.cpp +++ b/src/laige-core/logging.cpp @@ -497,7 +497,15 @@ namespace { // disposition (core dump / debugger / abort) continues unchanged. void crashHandler(int signum, siginfo_t*, void*) { static const char msg[] = "laige: fatal signal received; flushing logs\n"; - (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + // glibc declares write() with warn_unused_result (attached under + // fortification, i.e. optimized builds); a (void) cast does not + // suppress it, and the engine's -Werror makes it fatal. Consume the + // result instead — the notice is best-effort, and there is no + // async-signal-safe fallback if the write fails (LOG-007). + const ssize_t written = write(STDERR_FILENO, msg, sizeof(msg) - 1); + if (written == -1) { + // Intentionally empty: nothing else is async-signal-safe here. + } Logger::instance().crashFlush(); raise(signum); }