[PATCH 1/2] libstdc++: static_assert that static sized range size is less than inplace_vector capacity
Tomasz Kaminski
tkaminsk@redhat.com
Thu Mar 19 07:38:31 GMT 2026
On Wed, Mar 18, 2026 at 11:17 PM Jonathan Wakely <jwakely.gcc@gmail.com>
wrote:
> On Wed, 18 Mar 2026 at 15:15, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
> >
> > Resolves LWG 4396. Improve inplace_vector(from_range_t, R&& rg)
> >
> > This patch introduces a new __static_sized_range internal concept, for
> checking
> > if is static (independed on the object and know at compile time). The
> > implementation requires validating that ranges::size(__r) can be used as
> > constant template argument. For that check we use non_type_t, that is now
>
> "nontype_t" without the first underscore
>
> > always make available in C++26.
>
> "make" -> "made"
>
> OK with those changes
>
I have tested this with the modules, and this causes a crash during
substitution.
into range::size(__t), if I am reading the line correctly:
+ template<typename _Tp>
+ concept __static_sized_range = sized_range<_Tp>
+ && requires(_Tp&& __r) {
+ typename nontype_t<ranges::size(__r)>;
+ };
This also happens for existing (from_range) tests, so I think this impacts
all inplace_vectors.
usage with modules. SIMD uses the same approach, so I think the same
problem is preventing it.
it with use with module std.
I do not feel comfortable introducing a know regression with using
inplace_vector
via module std this late in GCC16, so I think I will postpone this patches
to 17, as they
are pure additions.
>
> >
> > The patch does not put extra considerations towards support
> integer-class size
> > types, as none of standard statically-sized ranges uses such type as
> > distance_type. If the need arrives we may later want to make
> __max_size_type
> > and __max_diff_type structural.
> >
> > The test case illustrates that views applied to span<T, N> are still
> statically
> > sized, but once applied to array<T, N> are not. This is caused by the
> fact that
> > ref_view stores a pointer to array, and deference of unknown pointers
> does not
> > produce reference to unknown, and simply yield non-constant expressions
> > (unknown pointer is not equivalent to pointer to unknown, as it may be
> null).
> >
> > libstdc++-v3/ChangeLog:
> >
> > * include/bits/ranges_base.h (__static_sized_range): Define.
> > * include/bits/utility.h (std::non_type_t, std::non_type)
> > [__cplusplus > 202302L]: Change include guard.
> > * include/std/inplace_vector (inplace_vector(std::from_range,
> __Rg&&)):
> > Add static_asserts checking range size.
> > * testsuite/23_containers/inplace_vector/cons/from_range_neg.cc:
> New test.
> > ---
> > CC Mathias as he has similar concept in the SIMD patches, and will need
> > to rebase (and provide second one for __static_sized_range<_Rg, N>).
> >
> > Testing on x86_64-linux. The *inplace_vector* test already passed.
> > OK for trunk when all test passes?
> >
> > libstdc++-v3/include/bits/ranges_base.h | 8 +++
> > libstdc++-v3/include/bits/utility.h | 2 +-
> > libstdc++-v3/include/std/inplace_vector | 16 ++++-
> > .../inplace_vector/cons/from_range_neg.cc | 70 +++++++++++++++++++
> > 4 files changed, 93 insertions(+), 3 deletions(-)
> > create mode 100644
> libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range_neg.cc
> >
> > diff --git a/libstdc++-v3/include/bits/ranges_base.h
> b/libstdc++-v3/include/bits/ranges_base.h
> > index 519927f5613..3f4a0c09dbb 100644
> > --- a/libstdc++-v3/include/bits/ranges_base.h
> > +++ b/libstdc++-v3/include/bits/ranges_base.h
> > @@ -558,6 +558,14 @@ namespace ranges
> > template<sized_range _Range>
> > using range_size_t =
> decltype(ranges::size(std::declval<_Range&>()));
> >
> > +#if __cplusplus > 202302L
> > + template<typename _Tp>
> > + concept __static_sized_range = sized_range<_Tp>
> > + && requires(_Tp&& __r) {
> > + typename nontype_t<ranges::size(__r)>;
> > + };
> > +#endif
> > +
> > template<typename _Derived>
> > requires is_class_v<_Derived> && same_as<_Derived,
> remove_cv_t<_Derived>>
> > class view_interface; // defined in <bits/ranges_util.h>
> > diff --git a/libstdc++-v3/include/bits/utility.h
> b/libstdc++-v3/include/bits/utility.h
> > index bd6b18d54dd..c667ce488d6 100644
> > --- a/libstdc++-v3/include/bits/utility.h
> > +++ b/libstdc++-v3/include/bits/utility.h
> > @@ -312,7 +312,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > inline constexpr sorted_equivalent_t sorted_equivalent{};
> > #endif
> >
> > -#if __glibcxx_function_ref // >= C++26
> > +#if __cplusplus > 202302L
> > template<auto>
> > struct nontype_t
> > {
> > diff --git a/libstdc++-v3/include/std/inplace_vector
> b/libstdc++-v3/include/std/inplace_vector
> > index c22a9e1f9b4..bbe3594cd0e 100644
> > --- a/libstdc++-v3/include/std/inplace_vector
> > +++ b/libstdc++-v3/include/std/inplace_vector
> > @@ -40,7 +40,7 @@
> > #include <initializer_list>
> > #include <bits/stdexcept_throw.h>
> > #include <bits/range_access.h>
> > -#include <bits/ranges_base.h> // borrowed_iterator_t,
> __detail::__container_compatible_range
> > +#include <bits/ranges_base.h> // borrowed_iterator_t,
> __detail::__container_compatible_range, __static_sized_range
> > #include <bits/ranges_util.h> // subrange
> > #include <bits/ranges_uninitialized.h>
> > #include <bits/stl_construct.h>
> > @@ -119,7 +119,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> > constexpr
> > inplace_vector(from_range_t, _Rg&& __rg)
> > : inplace_vector()
> > - { append_range(__rg); }
> > + {
> > + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > + // 4396. Improve inplace_vector(from_range_t, R&& rg)
> > + if constexpr (ranges::__static_sized_range<_Rg>)
> > + static_assert(ranges::size(__rg) <= _Nm);
> > +
> > + append_range(__rg);
> > + }
> >
> > constexpr
> > inplace_vector(initializer_list<_Tp> __il)
> > @@ -961,6 +968,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> > constexpr
> > inplace_vector(from_range_t, _Rg&& __rg)
> > {
> > + // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > + // 4396. Improve inplace_vector(from_range_t, R&& rg)
> > + if constexpr (ranges::__static_sized_range<_Rg>)
> > + static_assert(ranges::size(__rg) == 0);
> > +
> > if (ranges::begin(__rg) != ranges::end(__rg))
> > __throw_bad_alloc();
> > }
> > diff --git
> a/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range_neg.cc
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range_neg.cc
> > new file mode 100644
> > index 00000000000..cf87129da51
> > --- /dev/null
> > +++
> b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range_neg.cc
> > @@ -0,0 +1,70 @@
> > +// { dg-do compile { target c++26 } }
> > +
> > +#include <array>
> > +#include <ranges>
> > +#include <span>
> > +#include <inplace_vector>
> > +
> > +template<typename Range0>
> > +void
> > +test_zero(Range0&& r0)
> > +{
> > + std::inplace_vector<int, 0> f0(std::from_range, r0);
> > + std::inplace_vector<int, 1> f1(std::from_range, r0);
> > + std::inplace_vector<int, 5> f5(std::from_range, r0);
> > + std::inplace_vector<int, 7> f7(std::from_range, r0);
> > +}
> > +
> > +template<typename Range1>
> > +void
> > +test_one(Range1&& r1)
> > +{
> > + std::inplace_vector<int, 0> f0(std::from_range, r1); // { dg-error
> "required from" }
> > + std::inplace_vector<int, 1> f1(std::from_range, r1);
> > + std::inplace_vector<int, 5> f5(std::from_range, r1);
> > + std::inplace_vector<int, 7> f7(std::from_range, r1);
> > +}
> > +
> > +template<typename Range5>
> > +void
> > +test_five(Range5&& r5)
> > +{
> > + std::inplace_vector<int, 0> f0(std::from_range, r5); // { dg-error
> "required from" }
> > + std::inplace_vector<int, 1> f1(std::from_range, r5); // { dg-error
> "required from" }
> > + std::inplace_vector<int, 3> f3(std::from_range, r5); // { dg-error
> "required from" }
> > + std::inplace_vector<int, 5> f5(std::from_range, r5);
> > + std::inplace_vector<int, 7> f7(std::from_range, r5);
> > +}
> > +
> > +void
> > +test_all()
> > +{
> > + std::array<int, 0> a0;
> > + std::span<int, 0> s0(a0);
> > + std::array<int, 1> a1;
> > + std::span<int, 1> s1(a1);
> > + std::array<int, 5> a5;
> > + std::span<int, 5> s5(a5);
> > + std::array<int, 7> a7;
> > + std::span<int, 7> s7(a7);
> > +
> > + test_zero(a0);
> > + test_zero(s0);
> > + test_zero(std::views::empty<int>);
> > + test_one(s5 | std::views::adjacent<7> | std::views::elements<0>);
> > + test_one(a5 | std::views::adjacent<7> | std::views::elements<0>);
> > +
> > + test_one(a1); // { dg-error "from here" }
> > + test_one(s1); // { dg-error "from here" }
> > + test_one(s5 | std::views::adjacent<5> | std::views::elements<0>); //
> { dg-error "from here" }
> > + // ref_view is not statically sized due pointer dereference
> > + test_one(a5 | std::views::adjacent<7> | std::views::elements<0>);
> > +
> > + test_five(a5); // { dg-error "from here" }
> > + test_five(s5); // { dg-error "from here" }
> > + test_five(s7 | std::views::adjacent<3> | std::views::elements<0>); //
> { dg-error "from here" }
> > + // ref_view is not statically sized due pointer dereference
> > + test_five(a7 | std::views::adjacent<3> | std::views::elements<0>);
> > +}
> > +
> > +// { dg-error "static assertion failed" "" { target *-*-* } 0 }
> > --
> > 2.53.0
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260319/5fd53f61/attachment.htm>
More information about the Libstdc++
mailing list