Skip to content

Adopt LWG 4397: reject fixed-extent span from a constant-sized range that doesn't fit#22

Open
ssam18 wants to merge 1 commit into
mainfrom
adopt-lwg-4397
Open

Adopt LWG 4397: reject fixed-extent span from a constant-sized range that doesn't fit#22
ssam18 wants to merge 1 commit into
mainfrom
adopt-lwg-4397

Conversation

@ssam18

@ssam18 ssam18 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Implements LWG 4397 (Improve span(R&& r)), which GCC has already shipped and LWG moved to Ready in Brno. Closes #18.

What

The range constructor gains the new Mandate: when the extent is fixed and ranges::size(r) is a constant expression, it must equal the extent. Otherwise, the program is ill-formed. This rejects e.g. span<int, 42>(views::empty<int>) at compile time instead of only tripping the runtime precondition. Ranges whose size isn't a constant expression (std::vector and friends) are unaffected and still checked at run time via the existing assert (the hardened precondition).

How

The check sits in the Extent != dynamic_extent branch of the constructor, guarded by a requires probe that only fires the static_assert when ranges::size(r) is usable as a constant expression:

if constexpr (requires {
                  std::integral_constant<size_type, std::ranges::size(r)>{};
              }) {
    static_assert(std::ranges::size(r) == Extent,
                  "span extent does not match the range's constant size (LWG 4397)");
}

Realizing the Mandate requires P2280R4, the size has to be usable as a constant expression through the constructor parameter. On compilers without it (e.g. GCC 13) the requires is simply never satisfied and the check degrades to the runtime assert, with no false rejections (fixed-extent construction from a std::vector still compiles).

Tests

  • Positive unit tests: matched constant extent from views::empty<int>, and fixed-extent-from-vector staying valid.
  • A guarded negative-compile test: at configure time CMake try_compiles a matching probe (must compile) and a mismatching probe (must not); only when the compiler actually rejects the mismatch is a WILL_FAIL ctest wired up around it. On compilers that don't reject it, the test is skipped with a status message.

Verified locally on GCC 13.3 (test correctly skipped, full suite green) and by simulating the reject path (test enabled and passing).

@ssam18 ssam18 requested a review from SamareshSingh as a code owner July 14, 2026 19:42
@ssam18 ssam18 requested a review from JeffGarland July 14, 2026 19:42
…that doesn't fit

LWG 4397 adds a Mandate to the range constructor: when the extent is fixed and ranges::size(r) is a constant expression, it has to equal the extent, otherwise the program is ill-formed. This catches things like span<int, 42>(views::empty<int>) at compile time instead of only tripping the runtime precondition. Ranges whose size isn't a constant expression (std::vector and friends) are unaffected and still checked at run time.

The compile-time check only kicks in on compilers that implement P2280R4, since the size has to be usable as a constant expression through the constructor parameter. Where that isn't available (e.g. GCC 13) the check quietly falls back to the existing runtime assert with no false rejections.

Because of that, the negative-compile test is guarded: at configure time we probe whether the compiler actually rejects the mismatch, and only then wire up a WILL_FAIL ctest around it. On compilers that don't, the test is skipped.

Fixes the issue #18
@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 100.0%. remained the same — adopt-lwg-4397 into main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adopt LWG 4397

2 participants