Add GCC 16 to the toolchain list driving the conan, freestanding, and cmake
CI matrices. The existing install step already pulls g++>=15 from
ppa:ubuntu-toolchain-r/test on the ubuntu-24.04 runner, so no other changes
are needed (no ubuntu-26.04 runner image exists yet).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The polyfill.io domain was sold and began serving malware in the June
2024 supply-chain attack; it now redirects users to a registration
landing page instead of serving the polyfill. Drop the reference — it
was part of the old Material for MkDocs MathJax snippet and is not
needed for MathJax v3 on modern browsers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GCC 16 incorrectly treats a disengaged std::optional bound to a constexpr
variable as "not a constant expression", breaking scaling_overflows_non_zero_values
for large integer scaling factors (e.g. Zm->am, km3->pm3) where try_get_value
returns nullopt to signal overflow. Drop the constexpr on the local and use a
runtime `if` (legal since the function is consteval), avoiding materialization
of an empty optional as a constexpr constant. Logic is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The docs deploy used `mike deploy --push`, which appends a full-site commit
to gh-pages on every run. Since the previous manual reinit (Nov 2024) this grew
the branch back to ~178 MB across 187 dev redeploys — GitHub Pages only serves
the branch tip, so that history is pure dead weight.
Now mike deploys to the local branch, history is collapsed to a single
parentless commit, and the result is force-pushed. Versioned subtrees and
versions.json live in the tree (not history) so all releases + HEAD/latest are
preserved. Concurrency is switched to a single serial queue so the force-push
cannot clobber an overlapping deploy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous `195+` is the Conan-side encoding (matching
`_feature_compatibility` in conanfile.py) and is ambiguous in prose —
the digits collide with `_MSC_VER`/cl-version/toolset variants. The
cl-version `19.5+` is what users see from `cl /version`, and what's
already used in src/CMakeLists.txt when this project talks about MSVC
versions. Realign the MSVC column to accommodate the wider cell.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub Actions redirects `windows-2025` to `windows-2025-vs2026` from
2026-06-15. MSVC 14.5 (cl 19.50) is what VS 2026 ships, so the vs2026-
tagged image is the toolset we actually want — pinning explicitly avoids
the redirect notice on every CI run and removes any ambiguity.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two paths shipped the snippet incorrectly:
(1) Conan packaging (`conan create`): `package()` did a blanket
`rmdir(lib/cmake)` whenever `cxx_modules=False` to clear the bundled
config files that conflict with Conan-generated equivalents. That
also deleted the new `import-std-setup.cmake` that `cmake_build_modules`
in `package_info()` points at, so import_std consumers couldn't
resolve the include. Switch to surgical removal of just the three
bundled config files (`mp-unitsConfig.cmake`,
`mp-unitsConfigVersion.cmake`, `mp-unitsTargets*.cmake`) — keep
helper scripts.
(2) CMake "Test Package" CI (no `conan create`): a `find_package` against
the build tree (`mp-units_DIR=<build_dir>`) reads the generated
`mp-unitsConfig.cmake` from `${CMAKE_CURRENT_BINARY_DIR}`, where
`${CMAKE_CURRENT_LIST_DIR}/import-std-setup.cmake` doesn't exist —
only the source-tree copy and the install-tree copy did. Add a
`configure_file(... COPYONLY)` so the snippet sits next to the
generated config in the build dir too, and install the build-dir
copy for consistency.
With both fixes, the four delivery paths converge — Conan package, Conan
generated config + `cmake_build_modules`, CMake `find_package` against
build tree, CMake `find_package` against install tree — all see the same
`import-std-setup.cmake` next to `mp-unitsConfig.cmake`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The import_std + cxx_modules=False scenario was never exercised in CI
due to two interlocked gaps:
- job_matrix.py hard-rejected any (import_std=True, cxx_modules=False)
combination in is_supported, making it invisible to the matrix generator.
- _guarantee_api_coverage always pinned cxx_modules=True when forcing an
import_std job, so even after lifting the gate, MSVC (which has no C++
modules support at all) would still produce zero import_std=True jobs.
Fix both: drop the cxx_modules guard from is_supported, and loop over
cxx_modules in [True, False] in the guarantee so each import_std-capable
toolchain gets a job for each variant. MSVC now gets its first
import_std=True CI job (Release-only, as the Debug+std_format exclusion
still applies).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mp-units' minimum-supported MSVC 195 (VS 2026) has solid `import std;`
support, but enabling `-o import_std=True` against the build hit two
issues that this change fixes.
(1) Module scanning was only enabled when `cxx_modules=True`.
Without `CMAKE_CXX_SCAN_FOR_MODULES=ON`, CMake doesn't link the implicit
std module BMI into non-module translation units that do `import std;`,
producing C2230 "could not find module 'std'". Enable scanning whenever
`import_std=True` as well. Same bug fixed in `test_package/conanfile.py`.
(2) The std module BMI is built with mismatched compile flags.
CMake materializes the implicit std module BMI as `__cmake_cxx_std_NN`,
a separate internal target that does not link mp-units and therefore
does not inherit mp-units' target-level options. On MSVC this caused
C5050 (`_UTF8` defined on consumer but not on module command line) and
on Clang 21 there is a parallel `-Wreserved-module-identifier` issue
already partially handled in `src/CMakeLists.txt` but in a directory
scope that did not actually reach the BMI either.
Consolidate the workarounds into one `src/cmake/import-std-setup.cmake`
snippet that applies the flags project-wide via `add_compile_options`,
self-guarded by `if(NOT CMAKE_CXX_MODULE_STD) return()`. It is included
from every entry point so all delivery paths converge:
- top-level `CMakeLists.txt` for the dev build
- bundled `mp-unitsConfig.cmake` for `find_package` consumers
- via `cmake_build_modules` in `package_info()` for Conan-generated
configs (the `cxx_modules=False` consumer path)
Other pieces:
- `test/static/unit_magnitude_test.cpp` was the only test in the repo
missing the `MP_UNITS_IMPORT_STD` gate around `#include <type_traits>`,
causing duplicate `std::integral_constant` definitions in this
configuration.
- Compiler support table announces MSVC 195+ for `import std;`.
- Installation docs gain a consumer-side setup note covering the
CMake cache variables required, what mp-units handles automatically
via the snippet, and the `add_subdirectory`-vendored caveat.
Verified: `conan build . -pr msvc195 -o '&:cxx_modules=False'
-o '&:import_std=True' -s compiler.cppstd=23 -c
user.mp-units.build:all=True` -> 40/40 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TODO branch for `dimension_symbol_formatting` and `unit_symbol_formatting`
covered both Clang and MSVC: it dropped the deprecated `encoding` member to
avoid an implicit-copy-constructor deprecation warning that pragma push/pop
around the in-struct initialization can't suppress.
That concern still applies to Clang, but on MSVC the warning we actually hit
was C4996 at the in-struct `char_set = encoding;` line — which the existing
pragma machinery can suppress, except `MP_UNITS_DIAGNOSTIC_IGNORE_DEPRECATED`
was an empty no-op on MSVC. Map it to `warning(disable: 4996)` and narrow the
formatting-struct conditional from `MP_UNITS_COMP_CLANG || MP_UNITS_COMP_MSVC`
to just `MP_UNITS_COMP_CLANG`. MSVC now keeps the `[[deprecated]] encoding`
alias like GCC.
Verified by isolated rebuild + full test suite (40/40) under MSVC 195. Clang
behavior is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`__declspec(empty_bases)` is opt-in for an MSVC ABI feature, not a bug
workaround. Its only users (`scaled_unit_impl`, `derived_unit_impl`) are
stateless tag types, instantiated solely as `inline constexpr` compile-time
values, so the extra padding bytes MSVC may add without the attribute have
no behavioural or runtime cost. Removing the macro and its hacks.h block.
Removing it surfaced a latent C2131 in `get_common_unit_in`: the function
parameter `u` was read inside a `constexpr` initializer
(`constexpr auto canonical_u = get_canonical_unit(u);`), which MSVC's
stricter constant-expression evaluator rejects. GCC/Clang accept this
because `get_canonical_unit` ignores the runtime value and only depends on
`U`; with `__declspec(empty_bases)` the type was zero-sized and MSVC was
lenient, but without it the rule trips. Switch to `U{}` and drop the now-
unused parameter name (same shape as the recent `explode()` fix).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MP_UNITS_CONSTRAINED_NTTP_WORKAROUND / MP_UNITS_EXPRESSION_WORKAROUND /
MP_UNITS_IS_VALUE_WORKAROUND macros were gated on `_MSC_VER < 1950`. With the
minimum supported MSVC bumped to 195 in b0da3f7d0, that branch is unreachable
and the macros expand to identity, so remove them and inline the arguments at
every call site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MSVC's Debug constant evaluator can't get through `std::basic_format_string`'s
consteval ctor when the format args carry mp-units's deeply nested template
types — it trips C7595 ("call to immediate function is not a constant
expression"). Release works; switching to {fmt} (`std_format=False`) works.
Only `MSVC + Debug + std_format=True` is broken.
Express that constraint at every layer so a local repro and CI fail uniformly:
* `Configuration.is_supported` (job_matrix.py): rejects the combination, so
the matrix generator skips it.
* `make_msvc_config(...)` consumer (generate-job-matrix.py): extends the
{fmt}-fallback filter to include MSVC Debug, ensuring the matrix still
covers MSVC Debug — just with `std_format=False`.
* `conanfile.validate()`: rejects an install with a clear message pointing
the user at `std_format=False` or Release, so a local repro fails fast
at install time instead of much later during compilation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MSVC's Debug code generator does not dead-code-eliminate after an `if constexpr`
chain whose branches all return, so the fallthrough `return` after the chain is
flagged C4702 (unreachable) on instantiations where one of the branches is
selected. Release builds optimize the dead path out and never complain.
Restructure two places so the fallback lives inside an explicit `else` /
returning helper instead of a fallthrough:
* `enforce_bounds()` (quantity_point.h): wrap the `return q;` fallback in
`else { return q; }` after the existing `if constexpr` chain.
* `compare_quantities()` (quantity.h): hoist the floating-point / already-widest
fallback into a local `fallback_cmp` lambda invoked from both arms of the
`if constexpr` chain. Keeps the existing float-equal suppression.
For `CHECK_THROWS_AS` in constrained_test.cpp, Catch2's macro expands into a
try/catch whose catch arm becomes unreachable when the body calls a `[[noreturn]]`
function; suppress C4702 around that one TEST_CASE since the macro is not ours
to restructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Commit b68e0281e bumped Conan `compiler.version` 194 → 195 (cl.exe 19.50+ /
toolset 14.50+ on the `windows-2025` runner) but left the display label at
"14.4". Update the label to "14.5" so the matrix entry no longer claims a
toolset version one minor behind what the runner actually ships.
The label is purely cosmetic — it only flows into `name=f"MSVC {release}"`
— so this is a CI-display fix, not a behavioural change.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
With the recent `double_width_int` / `safe_int` fixes (commits fc7eb739e,
4ec5b655b, 2a4c34253, 01bf04491), the full hosted test and example suite
now compiles cleanly on MSVC. Drop the MSVC-specific `build:all=False`
branches in `ci-conan.yml` so the same matrix exercises tests and
examples on every supported toolchain — which is what would have caught
issue #795 before the MSVC team had to file it.
`ci-test-package-cmake.yml` keeps `build:all=False` because that workflow
exercises the *consumer* CMake test-package, which never needs the
library's own tests/examples built.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds heterogeneous arithmetic operators (+ - * / %) to `detail::safe_int_binary_ops`,
following the same idiom already used for `==` and `<=>`. The non-template homogeneous
inline-friends inside `safe_int<T>` keep winning same-T calls; the new template friends
in the base unambiguously handle the T != U calls without changing the converting-ctor
semantics. Resolves the ambiguity that triggered C2666 on `safe_int<int> + safe_int<long>`
when `int` and `long` have equal width (Windows).
Also:
* `mul_overflows<T>` now gates on `integer_rep_width_v<int128_t>` so MSVC's synthetic
`double_width_int<int64_t>` counts as a usable wider type for the overflow check.
* Replace the platform-dependent `safe_int<long>` widening test with a portable
`long long` variant — on Windows the original test's RHS
`static_cast<long>(INT_MAX) + 1L` is itself constexpr-invalid 32-bit overflow.
* Add convertibility coverage tests for `safe_int<T> op raw_U` mixing short / int /
long / uint8_t to lock down that the `safe_int op integral` overload wins by exact
match over the converting ctor on every platform.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI builds the MSVC matrix with `user.mp-units.build:all=False` so neither tests nor examples
are compiled there. Building with `all=True` on MSVC reveals a backlog of warnings that get
promoted to errors by `-WX`:
* `unit_magnitude.h(793)` C4245 — `find_first_factor` takes `uintmax_t`; cast `intmax_t N`
to it explicitly before calling.
* `framework/unit_magnitude.h::mag_ratio` (MSVC ICE workaround) — `prime_factorization` only
accepts positive arguments, but the workaround was passing the original `D` directly, so
`mag_ratio<3, -4>` instantiated `prime_factorization<-4>`. Normalize both halves to be
positive and reconstruct the sign from the parity of `(N < 0) != (D < 0)`.
* `safe_int.h::div_overflows` / `neg_overflows` / `int_in_range` C4702 — MSVC's reachability
analysis does not see the `if constexpr (is_signed_v<T>) return …;` branch as
unconditional for signed instantiations, so the trailing fallback return looks unreachable.
Restructure as `if constexpr (…) … else …;` so both branches share an explicit `else`.
* `safe_int.h::operator-` and `constrained.h::operator-` C4146 — unary minus on an unsigned
operand is well-defined in C++ but MSVC flags it. Suppress locally with the new
`MP_UNITS_DIAGNOSTIC_IGNORE_UNARY_MINUS_UNSIGNED` macro added to `bits/hacks.h`
(no-op on GCC/Clang, `#pragma warning(disable : 4146)` on MSVC).
* `framework/quantity.h` C4459 — the erased-handler trampolines used `void* p`, which shadows
`si::unit_symbols::p` (pico). Renamed to `void* self`, which is also the more accurate
name for a `this`-trampoline.
* `bits/format.h` / `bits/text_tools.h` C4459 — short loop variables in trivial
character-copy loops shadow various single-letter unit-symbol aliases brought into scope
by `using namespace si::unit_symbols`. These are genuinely benign; wrap each loop in
`MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW` rather than chasing the next colliding short name.
* `test/static/bounded_quantity_point_test.cpp` C4459 — local `auto t = time_of_day(...)`
shadows `non_si::unit_symbols::t` (tonne). Renamed to `auto tod = …` — the longer name
reads better in any case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits the two tiny utilities that both `double_width_int` and `fixed_point` depend on into
`bits/int_power.h`, so neither has to include the other for them. With that dependency cut,
`fixed_point.h` now includes `double_width_int.h` only when `__SIZEOF_INT128__` is not
defined — i.e. exactly on the platforms where `int128_t` / `uint128_t` are dwint aliases.
On compilers with native `__int128` the dwint definition is no longer parsed by every
translation unit that pulls in `fixed_point.h`.
The `is_signed_v<double_width_int<T>>` specialization moves inside the same conditional so
it is only declared when dwint is actually visible.
`double_width_int` was previously missing a number of operators that generic numerical code
expects from a 128-bit integer. On platforms with native `__int128` this was harmless, but on
MSVC (where `int128_t` / `uint128_t` alias `double_width_int<(u)int64_t>`) the recent
`UsesIntegerScaling` change wired the synthetic dwint into concept checks, and `compare_quantities`
+ magnitude folding into runtime paths, exposing every gap.
This change rounds out the operator set so `double_width_int` truly behaves as an integer type:
* binary `+`, `-`, `*`, `/` between two `double_width_int`s (alongside the existing narrow-rhs
overloads).
* unary `~` and binary `&`, `|`, `^`.
* compound assignment for arithmetic, bitwise, and shift operations.
* pre/post `++` and `--`.
* `static_cast<long double>` (and `double`/`float`) with sign-preserving conversion that avoids
catastrophic cancellation on platforms where `long double == double`.
* `std::numeric_limits<double_width_int<T>>` specialization so generic code probing `::max()`,
`::min()`, `::digits`, `::is_signed`, etc. gets correct answers (needed by
`checked_int_pow`, `compute_base_power`, `safe_int::operator-`).
* fields `hi_` / `lo_` and the `(hi, lo)` ctor are public so cross-instance inline friend
operators can access each other without further friend declarations.
While here, also:
* extract `double_width_int` (and its `std::numeric_limits` specialization) into a dedicated
header `bits/double_width_int.h`; `fixed_point.h` now just includes it and keeps the
`int128_t` aliases, `min_width_uint_t` / `double_width_int_for_t` / `wide_product_of`
helpers, and the `fixed_point` class itself.
* rename a local variable `m` in `wide_product_of` to `mid` to avoid shadowing
`si::unit_symbols::m` (MSVC C4459).
* rewrite the `lo_ > 0 ? -1 : 0` unary-minus body to use `Tl{0} - lo_` instead of `-lo_`,
silencing MSVC C4146 about unary minus on an unsigned operand.
Tests:
* new `test/static/double_width_int_test.cpp` pins down each new operator at compile time
(carry/borrow edges, schoolbook multiplication, narrow/wide division paths, bitwise
identities, numeric_limits values, long-double round-trips).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>