[PATCH v2 3/4] libstdc++: static_assert that static sized range size is less than inplace_vector capacity
Jonathan Wakely
jwakely@redhat.com
Tue May 26 09:18:30 GMT 2026
On Fri, 24 Apr 2026 at 08:41 +0200, Tomasz Kamiński wrote:
>Resolves LWG 4396. Improve inplace_vector(from_range_t, R&& rg)
>
>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
"dereference"
Commit logs should really be wrapped at about 72 chars, so that they
are still below 80 columns when indented in the output of 'git log'
https://cbea.ms/git-commit/
>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/std/inplace_vector (inplace_vector(std::from_range, __Rg&&)):
> Add static_asserts checking range size.
> * testsuite/23_containers/inplace_vector/cons/from_iota_neg.cc: New test.
New line after the colon please.
> * testsuite/23_containers/inplace_vector/cons/from_range_neg.cc: New test.
And here.
>---
>v2 removes expands test to cover integer-class types as size. Concept change was
>extracted to newly added first patch.
>
>Tested on x86_64-linux. OK for trunk?
OK with tweaks as noted above.
> libstdc++-v3/include/std/inplace_vector | 16 +++-
> .../inplace_vector/cons/from_iota_neg.cc | 46 +++++++++++
> .../inplace_vector/cons/from_range_neg.cc | 78 +++++++++++++++++++
> 3 files changed, 138 insertions(+), 2 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_iota_neg.cc
> create mode 100644 libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range_neg.cc
>
>diff --git a/libstdc++-v3/include/std/inplace_vector b/libstdc++-v3/include/std/inplace_vector
>index 0096bd83a17..caf9c203245 100644
>--- a/libstdc++-v3/include/std/inplace_vector
>+++ b/libstdc++-v3/include/std/inplace_vector
>@@ -41,7 +41,7 @@
> #include <optional>
> #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>
>@@ -120,7 +120,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)
>@@ -934,6 +941,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_iota_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_iota_neg.cc
>new file mode 100644
>index 00000000000..ae0b96175b7
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_iota_neg.cc
>@@ -0,0 +1,46 @@
>+// { dg-do compile { target c++26 } }
>+// { dg-require-effective-target int128 }
>+
>+#include <array>
>+#include <ranges>
>+#include <span>
>+#include <inplace_vector>
>+
>+template<typename Int, Int min, Int max = std::numeric_limits<Int>::max()>
>+struct StaticIota : std::ranges::iota_view<Int, Int>
>+{
>+ using Base = std::ranges::iota_view<Int, Int>;
>+ using size_type = std::ranges::range_size_t<Base>;
>+
>+ constexpr StaticIota() noexcept : Base(min, max) {}
>+
>+ constexpr static size_type
>+ size()
>+ { return size_type(max) - size_type(min); }
>+};
>+
>+template<typename Range>
>+constexpr std::ranges::ref_view<Range>
>+ref_view(Range& rg)
>+{ return std::ranges::ref_view<Range>(rg); }
>+
>+void
>+test_all()
>+{
>+ StaticIota<__int128, 0, 12> m12;
>+
>+ std::inplace_vector<int, 15> tm1(std::from_range, m12);
>+ std::inplace_vector<int, 15> tr1(std::from_range, ref_view(m12));
>+
>+ std::inplace_vector<int, 10> tm2(std::from_range, m12); // { dg-error "(from here|expansion of)" }
>+ // ref_view is not statically sized due pointer dereference
>+ std::inplace_vector<int, 10> tr2(std::from_range, ref_view(m12));
>+
>+ StaticIota<__int128, 0> mm;
>+
>+ std::inplace_vector<int, 10> tm3(std::from_range, mm); // { dg-error "(from here|expansion of)" }
>+ // ref_view is not statically sized due pointer dereference
>+ std::inplace_vector<int, 10> tr3(std::from_range, ref_view(mm));
>+}
>+
>+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
>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..48d5b4c56f5
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range_neg.cc
>@@ -0,0 +1,78 @@
>+// { 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);
>+}
>+
>+template<typename Range>
>+constexpr std::ranges::ref_view<Range>
>+ref_view(Range& rg)
>+{ return std::ranges::ref_view<Range>(rg); }
>+
>+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(ref_view(a0));
>+ test_zero(std::views::empty<int>);
>+ test_zero(s5 | std::views::adjacent<7> | std::views::elements<0>);
>+ test_zero(a5 | std::views::adjacent<7> | std::views::elements<0>);
>+
>+ test_one(a1); // { dg-error "from here" }
>+ test_one(s1); // { dg-error "from here" }
>+ // ref_view is not statically sized due pointer dereference
>+ test_one(ref_view(a1));
>+ test_one(a5 | std::views::adjacent<7> | std::views::elements<0>);
>+ test_one(s5 | std::views::adjacent<5> | std::views::elements<0>); // { dg-error "from here" }
>+
>+ test_five(a5); // { dg-error "from here" }
>+ test_five(s5); // { dg-error "from here" }
>+ // ref_view is not statically sized due pointer dereference
>+ test_five(ref_view(a5));
>+ test_five(a7 | std::views::adjacent<3> | std::views::elements<0>);
>+ test_five(s7 | std::views::adjacent<3> | std::views::elements<0>); // { dg-error "from here" }
>+}
>+
>+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
>--
>2.53.0
>
>
More information about the Libstdc++
mailing list