Skip to content

Add constrained span(initializer_list) constructor#8314

Merged
miscco merged 23 commits intoNVIDIA:mainfrom
rparolin:feature/span-initializer-list-ctor
Apr 8, 2026
Merged

Add constrained span(initializer_list) constructor#8314
miscco merged 23 commits intoNVIDIA:mainfrom
rparolin:feature/span-initializer-list-ctor

Conversation

@rparolin
Copy link
Copy Markdown
Contributor

@rparolin rparolin commented Apr 7, 2026

Summary

  • Re-adds the span(initializer_list) constructor that P4144R1 removed from C++26, using Hana Dusíková's constrained fix
  • Constructor is constrained with same_as<value_type> (prevents narrowing) and is_const_v<element_type> (prevents dangling)
  • Added to both static and dynamic extent specializations
  • No CTAD deduction guide — it causes regressions in existing braced-init-list deduction (e.g., span s{ptr, ptr})

Motivation

This implementation supports a WG21 paper proposing to restore the span(initializer_list) constructor with safety constraints. Having a working implementation in libcu++ strengthens the paper with real-world evidence.

Key design decisions

  • Uses initializer_list<_InitListValueType> instead of Hana's original initializer_list<const _InitListValueType> — the const is redundant because initializer_list<T>::begin() already returns const T*, and omitting it simplifies SFINAE fallback deduction
  • No deduction guide: span(initializer_list<T>) -> span<const T> causes C++ to prefer the initializer_list interpretation for all braced-init-list CTAD, breaking existing span s{ptr, sentinel} deduction

Test plan

  • initializer_list.pass.cpp — dynamic/static extent, named variables, empty lists, value integrity, bool literals, const volatile
  • initializer_list.fail.cpp — non-const element type, narrowing (int→bool, int→short, double→int)
  • Full span test suite: 38/38 pass

🤖 Generated with Claude Code

rparolin and others added 12 commits April 7, 2026 09:16
Re-add the span(initializer_list) constructor that P4144R1 removed from
C++26. Uses Hana Dusikova's fix: constrain with same_as<value_type> to
prevent narrowing and is_const_v<element_type> to prevent dangling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…guide

Move #include <cuda/std/initializer_list> to the top-level header group
(alongside array, cstddef, version) per project include ordering convention.

Add CTAD deduction guide: span(initializer_list<T>) -> span<const T>.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests cover dynamic/static extent construction, explicit initializer_list
variables, empty lists, value integrity, bool literals, const volatile
element types, and CTAD deduction. Both constexpr and runtime paths verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test dynamic/static extent construction, explicit initializer_list
variable, empty lists, value integrity, bool literals, const volatile
element type, and CTAD deduction guide.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify that the constrained span(initializer_list) constructor correctly
rejects non-const element types and type-mismatched initializer lists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The span constructed from a temporary braced-init-list dangles after the
initializer_list temporary is destroyed. Use named variables so the
backing storage outlives element access through the span.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- const volatile: only check size (volatile reads are not constexpr,
  and volatile int has no operator== with int)
- CTAD: use parentheses initialization to avoid brace-init ambiguity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The libcudacxx lit config only supports .pass.cpp, .fail.cpp,
.runfail.cpp, and .sh.cpp suffixes. Rename to .fail.cpp format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The deduction guide span(initializer_list<T>) -> span<const T> causes
C++ to prefer the initializer_list interpretation for braced-init-list
CTAD, breaking existing span s{ptr, ptr} deduction (deduces
span<const int*> instead of span<int>). Remove the guide; the
constrained constructor works fine with explicit types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@copy-pr-bot
Copy link
Copy Markdown
Contributor

copy-pr-bot Bot commented Apr 7, 2026

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@cccl-authenticator-app cccl-authenticator-app Bot moved this from Todo to In Progress in CCCL Apr 7, 2026
rparolin and others added 4 commits April 7, 2026 10:49
- Remove redundant type instantiations (long, double) that test the
  same code path as int
- Remove redundant narrowing case (const short, same as const bool)
- Add span<int* const> positive test (is_const_v is true for const ptr)
- Add span<const int*> negative test (is_const_v is false — the pointer
  itself is not const, only the pointee is)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread libcudacxx/include/cuda/std/span Outdated
Comment thread libcudacxx/include/cuda/std/span Outdated
rparolin and others added 4 commits April 7, 2026 13:16
Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>
…n.cons/initializer_list.pass.cpp

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>
Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>
…n.cons/initializer_list.pass.cpp

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>
@rparolin rparolin marked this pull request as ready for review April 7, 2026 20:18
@rparolin rparolin requested a review from a team as a code owner April 7, 2026 20:18
@rparolin rparolin requested a review from ericniebler April 7, 2026 20:18
@cccl-authenticator-app cccl-authenticator-app Bot moved this from In Progress to In Review in CCCL Apr 7, 2026
@github-actions

This comment has been minimized.

rparolin and others added 2 commits April 7, 2026 15:10
…lity

MSVC 14.29 eagerly evaluates is_const_v<element_type> during class
instantiation because element_type is a class template parameter. This
causes a hard error instead of SFINAE discard. Adding a defaulted
function template parameter _Ep = element_type makes the condition
depend on the function's own template parameter, deferring evaluation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Comment thread libcudacxx/include/cuda/std/span Outdated
@miscco
Copy link
Copy Markdown
Contributor

miscco commented Apr 8, 2026

Thanks a lot @rparolin for addin the fix

Co-authored-by: Michael Schellenberger Costa <miscco@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

🥳 CI Workflow Results

🟩 Finished in 2h 04m: Pass: 100%/108 | Total: 2d 23h | Max: 2h 03m | Hits: 79%/281680

See results here.

@miscco miscco merged commit f10648a into NVIDIA:main Apr 8, 2026
125 of 127 checks passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in CCCL Apr 8, 2026
jainishmehta pushed a commit to jainishmehta/cccl that referenced this pull request Apr 19, 2026
* feat: add constrained span(initializer_list) constructor

Re-add the span(initializer_list) constructor that P4144R1 removed from
C++26. Uses Hana Dusikova's fix: constrain with same_as<value_type> to
prevent narrowing and is_const_v<element_type> to prevent dangling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move initializer_list include to top-level group, add deduction guide

Move #include <cuda/std/initializer_list> to the top-level header group
(alongside array, cstddef, version) per project include ordering convention.

Add CTAD deduction guide: span(initializer_list<T>) -> span<const T>.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add positive test cases for span(initializer_list) constructor

Tests cover dynamic/static extent construction, explicit initializer_list
variables, empty lists, value integrity, bool literals, const volatile
element types, and CTAD deduction. Both constexpr and runtime paths verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add passing tests for span(initializer_list) constructor

Test dynamic/static extent construction, explicit initializer_list
variable, empty lists, value integrity, bool literals, const volatile
element type, and CTAD deduction guide.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add compile-failure tests for span(initializer_list) constructor

Verify that the constrained span(initializer_list) constructor correctly
rejects non-const element types and type-mismatched initializer lists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply clang-format to initializer_list.verify.cpp

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use named initializer_list variables to avoid dangling in tests

The span constructed from a temporary braced-init-list dangles after the
initializer_list temporary is destroyed. Use named variables so the
backing storage outlives element access through the span.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: fix const volatile and CTAD tests

- const volatile: only check size (volatile reads are not constexpr,
  and volatile int has no operator== with int)
- CTAD: use parentheses initialization to avoid brace-init ambiguity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: replace .verify.cpp with .fail.cpp for negative tests

The libcudacxx lit config only supports .pass.cpp, .fail.cpp,
.runfail.cpp, and .sh.cpp suffixes. Rename to .fail.cpp format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove initializer_list deduction guide to avoid CTAD regression

The deduction guide span(initializer_list<T>) -> span<const T> causes
C++ to prefer the initializer_list interpretation for braced-init-list
CTAD, breaking existing span s{ptr, ptr} deduction (deduces
span<const int*> instead of span<int>). Remove the guide; the
constrained constructor works fine with explicit types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove extra blank line, add dangling comments to size-only tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve deviation comment to explain why const is redundant

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: audit and refine initializer_list tests

- Remove redundant type instantiations (long, double) that test the
  same code path as int
- Remove redundant narrowing case (const short, same as const bool)
- Add span<int* const> positive test (is_const_v is true for const ptr)
- Add span<const int*> negative test (is_const_v is false — the pointer
  itself is not const, only the pointee is)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: replace magic numbers with il.size() in size assertions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: fix remaining magic numbers in initializer_list variable tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply clang-format alignment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update libcudacxx/include/cuda/std/span

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>

* Update libcudacxx/test/libcudacxx/std/containers/views/views.span/span.cons/initializer_list.pass.cpp

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>

* Update libcudacxx/include/cuda/std/span

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>

* Update libcudacxx/test/libcudacxx/std/containers/views/views.span/span.cons/initializer_list.pass.cpp

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>

* fix: add defaulted _Ep template param for MSVC 14.29 SFINAE compatibility

MSVC 14.29 eagerly evaluates is_const_v<element_type> during class
instantiation because element_type is a class template parameter. This
causes a hard error instead of SFINAE discard. Adding a defaulted
function template parameter _Ep = element_type makes the condition
depend on the function's own template parameter, deferring evaluation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply clang-format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update libcudacxx/include/cuda/std/span

Co-authored-by: Michael Schellenberger Costa <miscco@nvidia.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>
Co-authored-by: Michael Schellenberger Costa <miscco@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants