[gcc r17-2852] libstdc++: Reject user-defined specializations for allocator traits.

Tomasz Kaminski via Gcc-cvs <[email protected]> Fri, 31 Jul 2026 13:14:13 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <20260731131413.3976A4B19708__24560.0813346513$1785503694$gmane$org@sourceware.org>
https://gcc.gnu.org/g:55a1efc2e04d7d7ecd5a19db3945b4231dbdc050

commit r17-2852-g55a1efc2e04d7d7ecd5a19db3945b4231dbdc050
Author: Tomasz Kamiński <[email protected]>
Date:   Wed Jul 29 12:14:39 2026 +0200

    libstdc++: Reject user-defined specializations for allocator traits.
    
    Marks allocator_traits primary tempalte with [[_Clang::__no_specializations]]
    attribute in C++23 or later.
    
    This is QoI improvement for C++23 P2652R2, "Disallow User Specialization
    of allocator_traits", that makes such cases ill-formed, but does not
    require diagnostic.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/alloc_traits.h
            (_GLIBCXX_NO_ALLOC_TRAITS_SPECIALIZATIONS): Define locally.
            (std::allocator_traits) [__cplusplus > 202002L]: Add
            clang::no_specializations attribute.
            (std::allocator_traits<allocator<_Tp>>)
            (std::allocator_traits<allocator<void>>): Locally ignore
            -Winvalid-specialization warnings.
            * include/bits/memory_resource.h
            (allocator_traits<pmr::polymorphic_allocator<_Tp>>): Likewise.
            * testsuite/20_util/allocator_traits/requirements/specializations_neg.cc:
            New test.
    
    Reviewed-by: Jonathan Wakely <[email protected]>
    Signed-off-by: Tomasz Kamiński <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/alloc_traits.h              | 15 ++++++++++++++-
 libstdc++-v3/include/bits/memory_resource.h           |  4 ++++
 .../requirements/specializations_neg.cc               | 19 +++++++++++++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 101badff4549..c113f798690d 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -239,6 +239,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       = typename __allocator_traits_base::template __rebind<_Alloc, _Up>::type;
   /// @endcond
 
+#if __cplusplus > 202002L
+# define _GLIBCXX_NO_ALLOC_TRAITS_SPECIALIZATIONS _GLIBCXX_NO_SPECIALIZATIONS
+#else
+# define _GLIBCXX_NO_ALLOC_TRAITS_SPECIALIZATIONS
+#endif
+
   /**
    * @brief  Uniform interface to all allocator types.
    * @headerfile memory
@@ -246,7 +252,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @since C++11
   */
   template<typename _Alloc>
-    struct allocator_traits : __allocator_traits_base
+    struct _GLIBCXX_NO_ALLOC_TRAITS_SPECIALIZATIONS allocator_traits
+    : __allocator_traits_base
     {
       /// The allocator type
       typedef _Alloc allocator_type;
@@ -571,7 +578,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 #pragma GCC diagnostic pop
 
+#undef _GLIBCXX_NO_ALLOC_TRAITS_SPECIALIZATIONS
+
 #if _GLIBCXX_HOSTED
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-specialization"
+ 
   /**
    * @brief  Partial specialization for `std::allocator`
    * @headerfile memory
@@ -876,6 +888,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       select_on_container_copy_construction(const allocator_type& __rhs)
       { return __rhs; }
     };
+#pragma GCC diagnostic pop
 #endif // _GLIBCXX_HOSTED
 
   /// @cond undocumented
diff --git a/libstdc++-v3/include/bits/memory_resource.h b/libstdc++-v3/include/bits/memory_resource.h
index a9db58b76a34..801184c1bbc6 100644
--- a/libstdc++-v3/include/bits/memory_resource.h
+++ b/libstdc++-v3/include/bits/memory_resource.h
@@ -388,6 +388,9 @@ namespace pmr
 
   template<typename _Alloc> struct allocator_traits;
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-specialization"
+
   /// Partial specialization for `std::pmr::polymorphic_allocator`
   /**
    * @ingroup pmr
@@ -534,6 +537,7 @@ namespace pmr
       max_size(const allocator_type&) noexcept
       { return size_t(-1) / sizeof(value_type); }
     };
+#pragma GCC diagnostic pop
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
diff --git a/libstdc++-v3/testsuite/20_util/allocator_traits/requirements/specializations_neg.cc b/libstdc++-v3/testsuite/20_util/allocator_traits/requirements/specializations_neg.cc
new file mode 100644
index 000000000000..1a46d8793f0a
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/allocator_traits/requirements/specializations_neg.cc
@@ -0,0 +1,19 @@
+// { dg-do compile { target c++11 } }
+
+#include <memory>
+
+template<typename T> struct Alloc : std::allocator<T>
+{
+  template<typename U>
+  struct rebind { using other = Alloc<U>; };
+};
+
+template<typename T>
+struct std::allocator_traits<Alloc<T>> // { dg-error "cannot be specialized" "" { target c++23 } }
+{};
+
+template<>
+struct std::allocator_traits<Alloc<void>> // { dg-error "cannot be specialized" "" { target c++23 } }
+{};
+
+// { dg-bogus "cannot be specialized" "" { target c++20_down } 0 }