[PATCH] libstdc++: Add allocate_at_least (P0401) [PR118030]
Jonathan Wakely
jwakely.gcc@gmail.com
Mon Mar 9 10:01:13 GMT 2026
On Sun, 8 Mar 2026, 23:39 Nathan Myers, <ncm@cantrip.org> wrote:
> Have you thought about how std::allocator<>::allocate_at_least
> might usefully discover whether the ::op new (it is obliged by the
> Standard to use) is the one in libstdc++, and will supply extra
> details about memory allocated, not one the user has supplied and
> the linker has substituted in its place? Ideally it would all be
> decided at link time, and not conditionally in every call, but
> linker magic is fragile even without LTO. And users can call ::op
> new directly, with no #include to declare it.
>
> Maybe pass an extra argument to ::op new that the user's version
> won't notice, and that ours may discover is present by looking at
> the stack frame? The extra argument might be a pointer to a place
> to scribble extra details. (A hint argument might be placed there,
> besides.) But stack frame conventions vary too.
>
We don't need to do that. This feature is a customisation point for
non-standard allocators (and program-defined specializations of
std::allocator<User type>), we don't need to make std::allocator use
it. P0901 proposed changes to operator new which would have been useful
here, but that got abandoned.
I did post a prototype in https://gcc.gnu.org/PR106477 which can detect
whether operator new has been replaced, but I don't think we would need
that here.
We could just have a thread_local size_t* which is initially null and which
std::allocator would set to a local size_t, and operator new could check
for non-null and conditionally write the allocated size to it. But that
assumes that malloc provides a way to get the size. If a user interposes
their own malloc but doesn't work replace malloc_usable_size then you'd
have UB.
A safer and more conservative approach would be for
std::allocator::allocate_at_least to just round up to a multiple of
alignof(max_align_t) (as long as the total number of bytes being allocated
is already at least alignof(max_align_t)). That should be a separate change
though.
> -N
>
> On 3/8/26 7:01 AM, Jonathan Wakely wrote:
> > On Sat, 7 Mar 2026, 12:38 Nathan Myers, <ncm@cantrip.org
> > + * @return Memory of suitable size and alignment for @a n or
> > more
> > + * contiguous objects of type @c value_type .
> >
> >
> > Please use markdown for new doxygen comments instead of doxygen's @c or
> > html like <tt>. In general only back ticks are needed, and they're more
> > readable then the alternatives.
> Good.
>
> > + *
> > + * Returns <tt> a.allocate_at_least(n) </tt> if that
> expression
> > + * is well-formed, else <tt> { a.allocate(n), n } </tt>.
> When an
> > + * allocator is obliged to reserve more space than required
> for
> > + * the cited @c n objects, it may deliver the extra space to
> the
> > + * caller.
> > + */
> > + [[nodiscard]] static constexpr auto
> > + allocate_at_least(_Alloc& __a, size_type __n)
> > + -> allocation_result<pointer, size_type>
> > + {
> > + static_assert(requires { sizeof(value_type); },
> > + "allocated object type must be complete");
> >
> >
> > Does using a requires-expression make a difference here, rather than
> > just sizeof directly?
>
> Just that it writes out the error message attached.
>
Ah yes, the requires-expression means we get a failed static_assert:
/home/jwakely/gcc/16/include/c++/16.0.1/bits/new_allocator.h:131:23:
error: static
assertion failed: cannot allocate incomplete types
131 | static_assert(requires { sizeof(_Tp); }, "cannot allocate
incomplete types");
| ^~~~~~~~~~~~~~~~~~~~~~~~~
* 'false' evaluates to false
instead of an invalid one that doesn't even get as far as testing the
boolean expression:
/home/jwakely/gcc/16/include/c++/16.0.1/bits/new_allocator.h:131:23:
error: invalid
application of 'sizeof' to incomplete type 'S'
131 | static_assert(sizeof(_Tp) != 0, "cannot allocate incomplete
types");
| ^~~~~~~~~~~
But then it would be better to improve the existing checks in
allocator::allocate instead of adding another one here. That can be done
separately, so for this patch please don't add a new static_assert, we
already get a diagnostic from the allocate(n) call.
>
> > + if constexpr (requires { __a.allocate_at_least(__n); })
> > + return __a.allocate_at_least(__n);
> > + else
> > + return { __a.allocate(__n), __n };
> > + }
> > +#endif
> > +
> > /**
> > * @brief Deallocate memory.
> > * @param __a An allocator.
> > @@ -635,6 +662,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > #endif
> > }
> >
> > +#ifdef __glibcxx_allocate_at_least // C++23
> > + /**
> > + * @brief Allocate memory, generously.
> > + * @param __a An allocator.
> > + * @param __n The minimum number of objects to allocate
> > space for.
> > + * @return Memory of suitable size and alignment for @a n or
> > more
> > + * contiguous objects of type @c value_type .
> > + *
> > + * Returns <tt> a.allocate_at_least(n) </tt>.
> > + */
> > + [[nodiscard]] static constexpr auto
> > + allocate_at_least(allocator_type __a, size_type __n)
> > + -> allocation_result<pointer, size_type>
> > + {
> > + static_assert(requires { sizeof(value_type); },
> > + "allocated object type must be complete");
> >
> >
> > I think this static_assert is redundant, because
> > std::allocator::allocate_at_least calls std::allocator::allocate which
> > checks the same condition.
>
> But, does it?
Yes
Maybe it should. I have it here only because the Standard
> says "Mandates:", which I have (mistakenly?) interpreted as requiring
> a call-site diagnostic.
>
The standard only requires it too be ill-formed, which requires a
diagnostic, nothing about where it happens.
> > --- a/libstdc++-v3/include/bits/allocator.h
> > +++ b/libstdc++-v3/include/bits/allocator.h
> > @@ -61,6 +61,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > * @{
> > */
> >
> > +#ifdef __glibcxx_allocate_at_least // C++23
> > + template <typename _Pointer, typename _Size = size_t>
> > + struct allocation_result
> >
> >
> > Defining this type here means it won't be declared in bits/
> > alloc_traits.h for freestanding, because of:
> >
> > # if _GLIBCXX_HOSTED
> > # include <bits/allocator.h>
> > # endif
> >
> > I think it would be better in bits/memoryfwd.h
>
> Good catch.
>
> > + {
> > + _Pointer ptr;
> > + _Size count;
> > + };
> > +#endif
> > +
> >
> ... > +#ifdef __glibcxx_allocate_at_least // C++23
> > + [[nodiscard]] constexpr allocation_result<_Tp*, size_t>
> > + allocate_at_least(size_t __n)
> > + { return { this->allocate(__n), __n }; }
> > +#endif
>
> See, no static_assert here.
>
It's inside the call to allocate(__n).
prog.cc:5:39: required from here
.../include/c++/13.2.0/bits/allocator.h:193:45: error: invalid application
of 'sizeof' to incomplete type 'S'
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260309/40496673/attachment-0001.htm>
More information about the Libstdc++
mailing list