On Sun, 8 Mar 2026, 23:39 Nathan Myers, <[email protected]> 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, <[email protected] > > + * @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'
