Skip to content

[M2-PART-01 fix] Explicit integer-to-Scalar conversion for the macOS P0 toolchain - #82

Merged
offdev merged 2 commits into
masterfrom
fix/m2-part-01-appleclang-scalar-cast
Oct 7, 2026
Merged

offdev merged 2 commits into
masterfrom
fix/m2-part-01-appleclang-scalar-cast

Conversation

@offdev

@offdev offdev commented Oct 7, 2026

Copy link
Copy Markdown
Owner

M2-PART-01 fix · AppleClang (macOS-14 P0) rejects the fpx16_16 aggregate cast

Root cause. The macOS Intel lane (macos-14, AppleClang 15) of the M2-PART-01 merge run (37640282584) failed at Build:

particles.h:518: error: no matching conversion for static_cast from
'const std::uint32_t' to 'Scalar' (aka 'laige::fpx16_16')
particles_tests.cpp:244: error: no matching conversion for static_cast
from 'const std::uint32_t' to 'typename M::Scalar' (aka 'laige::fpx16_16')

static_cast<Scalar>(u) with Scalar = fpx16_16 compiles because of the C++17 single-member-aggregate parenthesized-initialization corner: the functional cast behaves like fpx16_16(u), aggregate-initializing raw from the u32 → int32_t conversion. g++ 16, Linux clang 22, MSVC 2022, and AppleClang 16 (macos-15) all accept the corner; AppleClang 15 rejects it. The run's split — macOS arm64 green, macOS Intel red — confirms the compiler-version boundary (not a code-path difference).

Fix. Replace the aggregate cast with an explicit per-backend construction — the values are identical, so the determinism contract is untouched:

  • particles.h — new private ParticleSystem::fromInt(u32): the fpx branch uses the aggregate brace init (the house idiom — fpx16_16.h itself constructs fpx16_16{raw} throughout), the fp32 branch a plain int32 → Scalar static_cast. sampleScalar now builds both operands via fromInt.
  • particles_tests.cpp — the oracle mirror sampleFromInt<Backend> (the oracle must follow the engine's sample path exactly).

Exactness: u < 2^24 on both backends — fpx raw units exact; the fp32 24-bit mantissa represents every integer below 2^24 exactly.

Verification.

  • KAT unchanged: the particles-determinism lines are bit-identical before/after (fpx16_16 … fnv1a=0x2f87b731a5186d05, fp32_pinned … fnv1a=0xd37fb69c51a30681) — the sample path's values did not move.
  • All six local trees warning-clean + full ctest green: build 115/115, build-clang 115/115, build-release 104/104, build-shared 115/115, build-asan 112/112, build-tsan 112/112.
  • laige-api.json regenerated (private member only — unchanged: 1410 symbols / 41 headers); api-real-tree/api-check-fresh, tools/laige-include-lint, tools/laige-determinism-lint green.
  • ci:macos label applied so this PR's lane runs the macOS lanes and proves the fix before merge.

Scope. Two files, the exact failing construct only; no public API change, no behavior change, no doc change (the header comment records the toolchain constraint at the site).

…P0 toolchain

The macOS Intel (macos-14, AppleClang 15) lane of the M2-PART-01
merge run (37640282584) failed to build particles_tests.cpp:

  no matching conversion for static_cast from 'const std::uint32_t'
  to 'laige::fpx16_16'

Root cause: static_cast<Scalar>(u32) with Scalar = fpx16_16 relies on
the C++17 single-member-aggregate parenthesized-initialization
corner (the cast behaves like fpx16_16(u32), aggregate-initializing
raw from the int32 conversion). g++ 16, Linux clang 22, MSVC 2022,
and AppleClang 16 (macos-15) all accept it; AppleClang 15 (the
macOS-14 P0 toolchain) rejects it. The macOS arm64 lane passing in
the same run while Intel failed confirms the compiler-version split.

Fix: replace the aggregate cast with an explicit per-backend
construction (a private ParticleSystem::fromInt and the test's
oracle mirror sampleFromInt): the fpx branch uses the aggregate
brace init (the house idiom, e.g. fpx16_16.h itself), the fp32
branch a plain int32->Scalar static_cast. The values are identical
(exact on both backends: u < 2^24), so the determinism contract and
the pinned KAT hashes are unchanged (verified: the
particles-determinism fnv1a lines are bit-identical before/after).

No public API change (private member only — laige-api.json
regenerated and unchanged: 1410 symbols / 41 headers; api-real-tree
green). All six local trees warning-clean + full ctest green
(115/115, 115/115, 104/104, 115/115, 112/112, 112/112); lints green.
@offdev offdev added the ci:macos label Oct 7, 2026
The first fix left the fp32 branch of fromInt/sampleFromInt as a
plain statement AFTER the if-constexpr (no else), so it was still
instantiated for the fpx backend — and it carries the same
int32->fpx16_16 cast AppleClang 15 rejects (the PR-lane macOS Intel
job 112876659688 failed on exactly that line). With the else the
discarded branch is not instantiated: the fpx backend only ever
instantiates the aggregate brace init, the fp32 backend only the
int32->Scalar standard conversion.

KAT unchanged (bit-identical particles-determinism lines); all six
local trees warning-clean + full ctest green (115/115, 115/115,
104/104, 115/115, 112/112, 112/112); lints green; laige-api.json
unchanged (private member only).
@offdev
offdev merged commit 6f017dd into master Oct 7, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant