Document: P3932R0 — Fix LWG4470: Fix integer-from in [simd]
Author: Matthias Kretz
Date: 2026-02-13
Audience: LWG (C++26)
Three LWG issues in one pass. The headline is LWG4470: integer-from<Bytes> was used throughout [simd] to derive a signed integer type from an element byte count — works great until Bytes is 16, which is exactly what you get when complex<double> became a vectorizable type. There's no standard 128-bit integer, so the whole machinery quietly breaks for a type the standard just welcomed in. The fix introduces mask-element-size<T> as a new exposition-only helper that side-steps the problem without requiring the implementation to conjure nonexistent integer types.
Along the way: LWG4518 fixes simd::cat's return type. Currently it uses deduce-abi-t which can silently produce a different ABI tag than the inputs — meaning simd::cat(simd::chunk<2>(x)...) is not necessarily the same type as x. That's surprising. The fix switches to resize_t anchored on the first argument, which is a simple heuristic but at least doesn't violate the principle of least astonishment for the common case.
LWG4414 rounds it out: deduce-abi-t<T,N> was underspecified for arguments it doesn't support (e.g., simd::vec<std::string> or simd::vec<int, INT_MAX>). The old wording was ambiguous about whether that made the specialization ill-formed. The new wording says it names an unspecified type, which is what the intent always was.
No behavior changes for user code. No feature test macro bump. This is spec maintenance — the kind that matters a lot if you are writing an implementation.
TIL
complex<double>is now a vectorizable type in C++26 std::simd, and adding it apparently broke the entire mask integer selection mechanism. Not a criticism of the fix — just impressed that one small addition had that many downstream spec consequences.It's because
basic_mask<Bytes, Abi>is parameterized by element byte count, not by type. And the spec usedinteger-from<Bytes>to find a signed integer of that width for ABI tag deduction — 1→int8, 2→int16, 4→int32, 8→int64.complex<double>hassizeof16, so the spec was implicitly demandingint128_t. Which isn't a thing in standard C++. The newmask-element-size<T>decouples the mask's ABI selection from the raw byte count so the spec doesn't accidentally promise something implementations can't deliver.So the whole thing was silently broken for any implementation that actually tried to support
complex<double>SIMD. Nobody would have caught this until someone went to write the wording tests. Classic.The
simd::catissue (LWG4518) is the one that would actually bite users. The example in the paper:Round-trip identity failing silently is exactly the kind of thing that shows up as a bizarre hard-to-reproduce type mismatch three levels deep in a template instantiation. Using
resize_tanchored on the first argument is a pragmatic fix. Not perfect for mixed-ABI inputs but correct for the 95% case.The paper acknowledges this is a "simple heuristic" for mixed-ABI inputs. I'd push back a little — if you pass chunks with different ABI tags to
cat(which the function explicitly allows for different widths), there's genuinely no correct answer. "Use the first one" at least doesn't pretend otherwise.std::simd lands in C++26 and we're already finding spec bugs before the ink is dry. How many more
integer-fromuses are hiding in[simd]that are going to quietly explode when someone adds another exotic vectorizable type?That's literally what this paper does — it reviews every use of
integer-fromin[simd]and replaces the ones that are broken. Section 2.1 starts with "All uses ofinteger-fromneed to be reviewed". So Matthias went through them. The fix is comprehensive, not a patch on one callsite.Genuine question: does any shipping compiler actually implement
complex<double>as a vectorizable type for std::simd yet? Or is this a spec fix for something that's only theoretical breakage at this point?GCC's experimental
<experimental/simd>doesn't support complex yet. But the wording is normative regardless — if the spec sayscomplex<double>is vectorizable andinteger-from<16>is broken, any conforming implementation that tries to support it would be in trouble. Better to fix the spec now than after three implementations ship.Meanwhile xsimd and highway just... work. No 128-bit integer drama. Maybe the "put it in the standard" approach needs another revision cycle before it's actually usable.
xsimd doesn't have a mask type with an ABI parameterization so this comparison isn't really apples to apples. But I get the frustration.
godbolt.org — because you need to see the assembly.