On Fri, Aug 29, 2025 at 10:56:45AM +0200, Tomasz Kaminski wrote:
> Yes, that is even better. C++ made me biased towards not using
> preprocessing,
> so I miss simple solutions.

Anyway, went with your requires case + added tests for starting
lifetime on static const variable.

2025-08-29  Jakub Jelinek  <[email protected]>

        * include/bits/version.def: Implement C++23 P2590R2 - Explicit
        lifetime management.
        (start_lifetime_as): New.
        * include/bits/version.h: Regenerate.
        * include/std/memory (std::start_lifetime_as,
        std::start_lifetime_as_array): New function templates.
        * src/c++23/std.cc.in (std::start_lifetime_as,
        std::start_lifetime_as_array): Export.
        * testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc: New test.

--- libstdc++-v3/include/bits/version.def.jj    2025-08-28 17:06:44.190031458 
+0200
+++ libstdc++-v3/include/bits/version.def       2025-08-28 18:44:00.947678928 
+0200
@@ -2100,6 +2100,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = start_lifetime_as;
+  values = {
+    v =  202207;
+    cxxmin = 23;
+  };
+};
+
 // Standard test specifications.
 stds[97] = ">= 199711L";
 stds[03] = ">= 199711L";
--- libstdc++-v3/include/bits/version.h.jj      2025-08-28 17:06:49.160883915 
+0200
+++ libstdc++-v3/include/bits/version.h 2025-08-28 18:44:08.431550790 +0200
@@ -2353,4 +2353,14 @@
 #endif /* !defined(__cpp_lib_is_implicit_lifetime) && 
defined(__glibcxx_want_is_implicit_lifetime) */
 #undef __glibcxx_want_is_implicit_lifetime
 
+#if !defined(__cpp_lib_start_lifetime_as)
+# if (__cplusplus >= 202100L)
+#  define __glibcxx_start_lifetime_as 202207L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_start_lifetime_as)
+#   define __cpp_lib_start_lifetime_as 202207L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_start_lifetime_as) && 
defined(__glibcxx_want_start_lifetime_as) */
+#undef __glibcxx_want_start_lifetime_as
+
 #undef __glibcxx_want_all
--- libstdc++-v3/include/std/memory.jj  2025-08-23 15:00:05.208775420 +0200
+++ libstdc++-v3/include/std/memory     2025-08-29 09:21:59.402103595 +0200
@@ -120,6 +120,7 @@
 #define __glibcxx_want_shared_ptr_arrays
 #define __glibcxx_want_shared_ptr_weak_type
 #define __glibcxx_want_smart_ptr_for_overwrite
+#define __glibcxx_want_start_lifetime_as
 #define __glibcxx_want_to_address
 #define __glibcxx_want_transparent_operators
 #define __glibcxx_want_smart_ptr_owner_equality
@@ -172,6 +173,134 @@ _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 #endif // C++11 to C++20
 
+#if __cpp_lib_start_lifetime_as >= 202207L // C++ >= 23
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline _Tp*
+    start_lifetime_as(void* __p) noexcept
+    {
+#if __cpp_lib_is_implicit_lifetime >= 202302L
+      static_assert(is_implicit_lifetime_v<_Tp>);
+#endif
+      auto __q = reinterpret_cast<_Tp*>(__p);
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__q)
+                          : "0" (__q), "m" (*__q));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline const _Tp*
+    start_lifetime_as(const void* __p) noexcept
+    {
+#if __cpp_lib_is_implicit_lifetime >= 202302L
+      static_assert(is_implicit_lifetime_v<_Tp>);
+#endif
+      auto __q = reinterpret_cast<const _Tp*>(__p);
+      auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
+                          : "0" (__q), "m" (*__q));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline volatile _Tp*
+    start_lifetime_as(volatile void* __p) noexcept
+    {
+#if __cpp_lib_is_implicit_lifetime >= 202302L
+      static_assert(is_implicit_lifetime_v<_Tp>);
+#endif
+      auto __q = reinterpret_cast<volatile _Tp*>(__p);
+      auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
+                          : "0" (__q), "m" (*__q));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline const volatile _Tp*
+    start_lifetime_as(const volatile void* __p) noexcept
+    {
+#if __cpp_lib_is_implicit_lifetime >= 202302L
+      static_assert(is_implicit_lifetime_v<_Tp>);
+#endif
+      auto __q = reinterpret_cast<const volatile _Tp*>(__p);
+      auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
+                          : "0" (__q), "m" (*__q));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline _Tp*
+    start_lifetime_as_array(void* __p, size_t __n) noexcept
+    {
+      auto __q = reinterpret_cast<_Tp*>(__p);
+      if (!__n)
+       return __q;
+      auto __r = (__extension__ reinterpret_cast<_Tp(*)[__n]>(__p));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
+                          : "0" (__q), "m" (*__r));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline const _Tp*
+    start_lifetime_as_array(const void* __p, size_t __n) noexcept
+    {
+      auto __q = reinterpret_cast<const _Tp*>(__p);
+      if (!__n)
+       return __q;
+      auto __r = (__extension__ reinterpret_cast<const _Tp(*)[__n]>(__p));
+      auto __s = (__extension__
+                 reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p)));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__s)
+                          : "0" (__q), "m" (*__r));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline volatile _Tp*
+    start_lifetime_as_array(volatile void* __p, size_t __n) noexcept
+    {
+      auto __q = reinterpret_cast<volatile _Tp*>(__p);
+      if (!__n)
+       return __q;
+      auto __r = (__extension__ reinterpret_cast<volatile _Tp(*)[__n]>(__p));
+      auto __s = (__extension__
+                 reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p)));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__s)
+                          : "0" (__q), "m" (*__r));
+      return __q;
+    }
+
+  template<typename _Tp>
+    [[__gnu__::__always_inline__]]
+    inline const volatile _Tp*
+    start_lifetime_as_array(const volatile void* __p, size_t __n) noexcept
+    {
+      auto __q = reinterpret_cast<const volatile _Tp*>(__p);
+      if (!__n)
+       return __q;
+      auto __r = (__extension__ reinterpret_cast<const volatile 
_Tp(*)[__n]>(__p));
+      auto __s = (__extension__
+                 reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p)));
+      __asm__ __volatile__("" : "=g" (__q), "=m" (*__s)
+                          : "0" (__q), "m" (*__r));
+      return __q;
+    }
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif
+
 #ifdef __cpp_lib_parallel_algorithm // C++ >= 17 && HOSTED
 // Parallel STL algorithms
 # if _PSTL_EXECUTION_POLICIES_DEFINED
--- libstdc++-v3/src/c++23/std.cc.in.jj 2025-08-28 17:10:43.784942152 +0200
+++ libstdc++-v3/src/c++23/std.cc.in    2025-08-29 09:55:46.164636222 +0200
@@ -2004,6 +2004,10 @@ export namespace std
   using std::owner_equal;
   using std::owner_hash;
 #endif
+#if __cpp_lib_start_lifetime_as
+  using std::start_lifetime_as;
+  using std::start_lifetime_as_array;
+#endif
 }
 
 // 20.4 <memory_resource>
--- libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc.jj 
2025-08-28 19:29:12.865001523 +0200
+++ libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc    
2025-08-29 10:53:59.160068890 +0200
@@ -0,0 +1,94 @@
+// { dg-do run { target c++23 } }
+
+#include <bit>
+#include <memory>
+
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+struct S { int a; int b; };
+struct T { long long c; };
+
+template<typename S, typename T>
+void
+test01()
+{
+}
+
+template<typename S, typename T>
+requires (sizeof(S) == sizeof(T))
+void
+test01()
+{
+  union U { unsigned char a[sizeof(S)]; S b; T c; } u;
+  u.a[0] = 1;
+  T v = std::bit_cast<T> (S{1, 2});
+  union V { unsigned char a[3 * sizeof(S)]; S b[3]; T c[3]; } w;
+  T x = std::bit_cast<T> (S{3, 4});
+  T y = std::bit_cast<T> (S{5, 6});
+  S* d = std::start_lifetime_as<S>(reinterpret_cast<void*>(&u.a));
+  d->a = 1;
+  d->b = 2;
+  T* e = std::start_lifetime_as<T>(reinterpret_cast<void*>(d));
+  VERIFY( e->c == v.c );
+  const T* f = std::start_lifetime_as<T>(reinterpret_cast<const void*>(d));
+  VERIFY( f->c == v.c );
+  volatile T* g
+    = std::start_lifetime_as<T>(reinterpret_cast<volatile void*>(d));
+  VERIFY( g->c == v.c );
+  const volatile T* h
+    = std::start_lifetime_as<T>(reinterpret_cast<const volatile void*>(d));
+  VERIFY( h->c == v.c );
+  S* i = std::start_lifetime_as_array<S>(reinterpret_cast<void*>(&w.a), 3);
+  i[0].a = 1;
+  i[0].b = 2;
+  i[1].a = 3;
+  i[1].b = 4;
+  i[2].a = 5;
+  i[2].b = 6;
+  T* j = std::start_lifetime_as_array<T>(reinterpret_cast<void*>(i), 3);
+  VERIFY( j[0].c == v.c && j[1].c == x.c && j[2].c == y.c );
+  const T* k
+    = std::start_lifetime_as_array<T>(reinterpret_cast<const void*>(i), 3);
+  VERIFY( k[0].c == v.c && k[1].c == x.c && k[2].c == y.c );
+  volatile T* l
+    = std::start_lifetime_as_array<T>(reinterpret_cast<volatile void*>(i), 3);
+  VERIFY( l[0].c == v.c && l[1].c == x.c && l[2].c == y.c );
+  const volatile T* m
+    = std::start_lifetime_as_array<T>(reinterpret_cast<const volatile 
void*>(i),
+                                     3);
+  VERIFY( m[0].c == v.c && m[1].c == x.c && m[2].c == y.c );
+  T* n = std::start_lifetime_as_array<T>(static_cast<void*>(nullptr), 0);
+  VERIFY( n == nullptr );
+  const T* o
+    = std::start_lifetime_as_array<T>(static_cast<const void*>(nullptr), 0);
+  VERIFY( o == nullptr );
+  volatile T* p
+    = std::start_lifetime_as_array<T>(static_cast<volatile void*>(nullptr), 0);
+  VERIFY( p == nullptr );
+  const volatile T* q
+    = std::start_lifetime_as_array<T>(static_cast<const volatile 
void*>(nullptr),
+                                     0);
+  VERIFY( q == nullptr );
+  VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<void*>(&w.a), 0)
+         == &w.c[0] );
+  VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const void*>(&w.a), 
0)
+         == static_cast<const T*>(&w.c[0]) );
+  VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<volatile 
void*>(&w.a),
+                                         0)
+         == static_cast<volatile T*>(&w.c[0]) );
+  VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const volatile 
void*>(&w.a),
+                                         0)
+         == static_cast<const volatile T*>(&w.c[0]) );
+  static const S r[] = { { 5, 6 }, { 3, 4 } };
+  const T* s = std::start_lifetime_as<T>(&r[1]);
+  VERIFY( s->c == x.c );
+  const T* t = std::start_lifetime_as_array<T>(&r[0], 2);
+  VERIFY( t[0].c == y.c && t[1].c == x.c );
+}
+
+int
+main()
+{
+  test01<S, T>();
+}


        Jakub

Reply via email to