From cb3895bb68c20f27062bf93b924135b8fe61e926 Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Tue, 15 Jul 2025 19:44:28 +0800
Subject: [PATCH 1/2] Support trivially relocatability

---
 benchmarks/proxy_management_benchmark.cpp     | 13 +++
 docs/spec/ProFacade.md                        | 22 +++++
 docs/spec/basic_facade_builder/build.md       |  2 +-
 .../support_relocation.md                     |  2 +-
 include/proxy/v4/proxy.h                      | 84 +++++++++++++++----
 tests/proxy_traits_tests.cpp                  | 24 ++++--
 6 files changed, 125 insertions(+), 22 deletions(-)

diff --git a/benchmarks/proxy_management_benchmark.cpp b/benchmarks/proxy_management_benchmark.cpp
index 93a4ba5b..6862efec 100644
--- a/benchmarks/proxy_management_benchmark.cpp
+++ b/benchmarks/proxy_management_benchmark.cpp
@@ -13,6 +13,19 @@
 
 namespace {
 
+struct SmallObject3;
+
+} // namespace
+
+namespace pro::inline v4::details {
+
+template <>
+struct tr_override_traits<SmallObject3> : applicable_traits {};
+
+} // namespace pro::inline v4::details
+
+namespace {
+
 constexpr int TestManagedObjectCount = 600000;
 constexpr int TypeSeriesCount = 3;
 
diff --git a/docs/spec/ProFacade.md b/docs/spec/ProFacade.md
index d62a743d..7b73146e 100644
--- a/docs/spec/ProFacade.md
+++ b/docs/spec/ProFacade.md
@@ -12,6 +12,28 @@ A type `F` meets the *ProFacade* requirements of a type `P` if `F` meets the [*P
 | `F::relocatability`            | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required relocatability of `P`. |
 | `F::destructibility`           | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required destructibility of `P`. |
 
+## Notes
+
+Relocatability is defined as *move-construct an object and then destroy the original instance*. Specifically, the value of `F::relocatability` maps to the following requirements on `P`:
+
+| Value                          | Requirement on `P`                                           |
+| ------------------------------ | ------------------------------------------------------------ |
+| `constraint_level::none`       | None                                                         |
+| `constraint_level::nontrivial` | `std::is_move_constructible_v<P> && std::is_destructible_v<P>` |
+| `constraint_level::nothrow`    | `std::is_nothrow_move_constructible_v<P> && std::is_nothrow_destructible_v<P>` |
+| `constraint_level::trivial`    | *trivially relocatable* (see below)                          |
+
+C++26 introduces the type trait `std::is_trivially_relocatable_v` ([P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html)). The library supports C++20/23 and C++26 with a three-stage evaluation:
+
+1. If `std::is_trivially_relocatable_v<P>` is available **and evaluates to `true`**, the requirement is satisfied.
+2. Otherwise, a conservative fallback is applied: `std::is_trivially_move_constructible_v<P> && std::is_trivially_destructible_v<P>`.
+3. If the fallback is `false`, the library consults an explicit curated allow-list of types that are known to be safely relocatable:
+  - `std::unique_ptr<T, D>` when `D` is itself trivially relocatable
+  - `std::shared_ptr<T>`
+  - `std::weak_ptr<T>`
+
+This sequence reduces the risk of false negatives while compiler support for C++26 is still being rolled out, without compromising safety.
+
 ## See Also
 
 - [concept `proxiable`](proxiable.md)
diff --git a/docs/spec/basic_facade_builder/build.md b/docs/spec/basic_facade_builder/build.md
index 5a551830..68d36a1f 100644
--- a/docs/spec/basic_facade_builder/build.md
+++ b/docs/spec/basic_facade_builder/build.md
@@ -20,7 +20,7 @@ Specifies a [facade](../facade.md) type deduced from the template parameters of
 | `max_size` [static] [constexpr]        | `MaxSize == default-size ? sizeof(void*) * 2u : MaxSize`     |
 | `max_align` [static] [constexpr]       | `MaxAlign == default-size ? alignof(void*) : MaxAlign`       |
 | `copyability` [static] [constexpr]     | `Copyability == default-cl ? constraint_level::none : Copyability` |
-| `relocatability` [static] [constexpr]  | `Relocatability == default-cl ? constraint_level::nothrow : Relocatability` |
+| `relocatability` [static] [constexpr]  | `Relocatability == default-cl ? constraint_level::trivial : Relocatability` |
 | `destructibility` [static] [constexpr] | `Destructibility == default-cl ? constraint_level::nothrow : Destructibility` |
 
 ## Notes
diff --git a/docs/spec/basic_facade_builder/support_relocation.md b/docs/spec/basic_facade_builder/support_relocation.md
index 714cbaf3..78e9b150 100644
--- a/docs/spec/basic_facade_builder/support_relocation.md
+++ b/docs/spec/basic_facade_builder/support_relocation.md
@@ -9,7 +9,7 @@ The alias template `support_relocation` of `basic_facade_builder<Cs, Rs, MaxSize
 
 ## Notes
 
-If no relocatability support is applied before specifying [`build`](build.md), the default value of `build::relocatability` is `pro::constraint_level::nothrow`.
+If no relocatability support is applied before specifying [`build`](build.md), the default value of `build::relocatability` is `pro::constraint_level::trivial`. Please refer to [the *ProFacade* requirements](../ProFacade.md) for more details.
 
 ## Example
 
diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h
index 08c20d69..41216250 100644
--- a/include/proxy/v4/proxy.h
+++ b/include/proxy/v4/proxy.h
@@ -40,6 +40,12 @@
 #error Proxy requires C++20 attribute no_unique_address.
 #endif
 
+#if __cpp_trivial_relocatability >= 202502L
+#define PROD_TR_IF_ELIGIBLE trivially_relocatable_if_eligible
+#else
+#define PROD_TR_IF_ELIGIBLE
+#endif // __cpp_trivial_relocatability >= 202502L
+
 namespace pro::inline v4 {
 
 namespace details {
@@ -138,6 +144,9 @@ using add_qualifier_t = typename add_qualifier_traits<T, Q>::type;
 template <class T, qualifier_type Q>
 using add_qualifier_ptr_t = std::remove_reference_t<add_qualifier_t<T, Q>>*;
 
+template <class T>
+struct tr_override_traits : inapplicable_traits {};
+
 template <class T>
 consteval bool has_copyability(constraint_level level) {
   switch (level) {
@@ -164,8 +173,17 @@ consteval bool has_relocatability(constraint_level level) {
     return std::is_nothrow_move_constructible_v<T> &&
            std::is_nothrow_destructible_v<T>;
   case constraint_level::trivial:
-    return std::is_trivially_move_constructible_v<T> &&
-           std::is_trivially_destructible_v<T>;
+#if __cpp_lib_trivially_relocatable >= 202502L
+    if constexpr (std::is_trivially_relocatable_v<T>) {
+      return true;
+    }
+#endif // __cpp_lib_trivially_relocatable >= 202502L
+    if constexpr (tr_override_traits<T>::applicable) {
+      return true;
+    } else {
+      return std::is_trivially_move_constructible_v<T> &&
+             std::is_trivially_destructible_v<T>;
+    }
   default:
     return false;
   }
@@ -587,6 +605,20 @@ template <class A1, class A2>
 using merged_composite_accessor =
     typename composite_accessor_merge_traits<A1, A2>::type;
 
+struct tr_blocker {
+  tr_blocker() = default;
+  tr_blocker(const tr_blocker&) noexcept {}
+  tr_blocker& operator=(const tr_blocker&) noexcept { return *this; }
+};
+template <class A, bool TR>
+struct conditional_tr_accessor_traits : std::type_identity<A> {};
+template <class... As>
+struct conditional_tr_accessor_traits<composite_accessor_impl<As...>, false>
+    : std::type_identity<composite_accessor_impl<tr_blocker, As...>> {};
+template <class A, bool TR>
+using conditional_tr_accessor_t =
+    typename conditional_tr_accessor_traits<A, TR>::type;
+
 template <class P>
 struct ptr_traits : inapplicable_traits {};
 template <class P>
@@ -729,9 +761,10 @@ struct facade_traits<F>
   using indirect_accessor =
       merged_composite_accessor<typename facade_traits::conv_indirect_accessor,
                                 typename facade_traits::refl_indirect_accessor>;
-  using direct_accessor =
+  using direct_accessor = conditional_tr_accessor_t<
       merged_composite_accessor<typename facade_traits::conv_direct_accessor,
-                                typename facade_traits::refl_direct_accessor>;
+                                typename facade_traits::refl_direct_accessor>,
+      F::relocatability == constraint_level::trivial>;
 
   template <class P>
   static consteval void diagnose_proxiable() {
@@ -925,8 +958,9 @@ const R& proxy_reflect(const proxy<F>& p) noexcept {
 }
 
 template <facade F>
-class proxy : public details::facade_traits<F>::direct_accessor,
-              public details::inplace_ptr<proxy_indirect_accessor<F>> {
+class proxy PROD_TR_IF_ELIGIBLE
+    : public details::facade_traits<F>::direct_accessor,
+      public details::inplace_ptr<proxy_indirect_accessor<F>> {
   friend struct details::proxy_helper<F>;
   static_assert(details::facade_traits<F>::applicable);
 
@@ -945,7 +979,7 @@ class proxy : public details::facade_traits<F>::direct_accessor,
             proxy_indirect_accessor<F>>() /* Make GCC happy */ {
     initialize(rhs);
   }
-  proxy(proxy&& rhs) noexcept(F::relocatability == constraint_level::nothrow)
+  proxy(proxy&& rhs) noexcept(F::relocatability >= constraint_level::nothrow)
     requires(F::relocatability >= constraint_level::nontrivial &&
              F::copyability != constraint_level::trivial)
   {
@@ -1254,8 +1288,9 @@ class indirect_ptr {
 };
 
 template <class T, class Alloc>
-class PRO4D_ENFORCE_EBO allocated_ptr : private alloc_aware<Alloc>,
-                                        public indirect_ptr<inplace_ptr<T>> {
+class PRO4D_ENFORCE_EBO allocated_ptr PROD_TR_IF_ELIGIBLE
+    : private alloc_aware<Alloc>,
+      public indirect_ptr<inplace_ptr<T>> {
 public:
   template <class... Args>
   allocated_ptr(const Alloc& alloc, Args&&... args)
@@ -1289,7 +1324,8 @@ struct PRO4D_ENFORCE_EBO compact_ptr_storage : alloc_aware<Alloc>,
         inplace_ptr<T>(std::in_place, std::forward<Args>(args)...) {}
 };
 template <class T, class Alloc>
-class compact_ptr : public indirect_ptr<compact_ptr_storage<T, Alloc>> {
+class compact_ptr PROD_TR_IF_ELIGIBLE
+    : public indirect_ptr<compact_ptr_storage<T, Alloc>> {
   using Storage = compact_ptr_storage<T, Alloc>;
 
 public:
@@ -1326,7 +1362,7 @@ struct PRO4D_ENFORCE_EBO shared_compact_ptr_storage
         inplace_ptr<T>(std::in_place, std::forward<Args>(args)...) {}
 };
 template <class T, class Alloc>
-class shared_compact_ptr
+class shared_compact_ptr PROD_TR_IF_ELIGIBLE
     : public indirect_ptr<shared_compact_ptr_storage<T, Alloc>> {
   using Storage = shared_compact_ptr_storage<T, Alloc>;
 
@@ -1369,7 +1405,7 @@ struct strong_weak_compact_ptr_storage : strong_weak_compact_ptr_storage_base,
 template <class T, class Alloc>
 class weak_compact_ptr;
 template <class T, class Alloc>
-class strong_compact_ptr
+class strong_compact_ptr PROD_TR_IF_ELIGIBLE
     : public indirect_ptr<strong_weak_compact_ptr_storage<T, Alloc>> {
   friend class weak_compact_ptr<T, Alloc>;
   using Storage = strong_weak_compact_ptr_storage<T, Alloc>;
@@ -1416,7 +1452,7 @@ class strong_compact_ptr
       : indirect_ptr<Storage>(ptr) {}
 };
 template <class T, class Alloc>
-class weak_compact_ptr {
+class weak_compact_ptr PROD_TR_IF_ELIGIBLE {
 public:
   weak_compact_ptr(const strong_compact_ptr<T, Alloc>& rhs) noexcept
       : ptr_(rhs.ptr_) {
@@ -1452,6 +1488,25 @@ class weak_compact_ptr {
   strong_weak_compact_ptr_storage<T, Alloc>* ptr_;
 };
 
+template <class T, class D>
+  requires(has_relocatability<D>(constraint_level::trivial))
+struct tr_override_traits<std::unique_ptr<T, D>> : applicable_traits {};
+template <class T>
+struct tr_override_traits<std::shared_ptr<T>> : applicable_traits {};
+template <class T>
+struct tr_override_traits<std::weak_ptr<T>> : applicable_traits {};
+template <class T, class Alloc>
+  requires(has_relocatability<Alloc>(constraint_level::trivial))
+struct tr_override_traits<allocated_ptr<T, Alloc>> : applicable_traits {};
+template <class T, class Alloc>
+struct tr_override_traits<compact_ptr<T, Alloc>> : applicable_traits {};
+template <class T, class Alloc>
+struct tr_override_traits<shared_compact_ptr<T, Alloc>> : applicable_traits {};
+template <class T, class Alloc>
+struct tr_override_traits<strong_compact_ptr<T, Alloc>> : applicable_traits {};
+template <class T, class Alloc>
+struct tr_override_traits<weak_compact_ptr<T, Alloc>> : applicable_traits {};
+
 struct weak_conversion_dispatch;
 template <class... Cs>
 struct weak_ownership_support_traits_impl : inapplicable_traits {};
@@ -2053,7 +2108,7 @@ struct basic_facade_builder {
       MaxAlign == details::invalid_size ? alignof(details::ptr_prototype)
                                         : MaxAlign,
       Copyability == details::invalid_cl ? constraint_level::none : Copyability,
-      Relocatability == details::invalid_cl ? constraint_level::nothrow
+      Relocatability == details::invalid_cl ? constraint_level::trivial
                                             : Relocatability,
       Destructibility == details::invalid_cl ? constraint_level::nothrow
                                              : Destructibility>;
@@ -2577,6 +2632,7 @@ struct formatter<pro::v4::proxy_indirect_accessor<F>, CharT> {
 } // namespace std
 #endif // __STDC_HOSTED__ && __has_include(<format>)
 
+#undef PROD_TR_IF_ELIGIBLE
 #undef PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE
 
 #endif // MSFT_PROXY_V4_PROXY_H_
diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp
index dd29d99a..7a85c489 100644
--- a/tests/proxy_traits_tests.cpp
+++ b/tests/proxy_traits_tests.cpp
@@ -41,11 +41,25 @@ using MockCopyableSmallPtr =
 using MockTrivialPtr = MockPtr<true, true, true, sizeof(void*), alignof(void*)>;
 using MockFunctionPtr = void (*)();
 
+} // namespace proxy_traits_tests_details
+
+namespace pro::inline v4::details {
+
+template <bool kNothrowRelocatable, bool kCopyable, bool kTrivial,
+          std::size_t kSize, std::size_t kAlignment>
+struct tr_override_traits<proxy_traits_tests_details::MockPtr<
+    kNothrowRelocatable, kCopyable, kTrivial, kSize, kAlignment>>
+    : applicable_traits {};
+
+} // namespace pro::inline v4::details
+
+namespace proxy_traits_tests_details {
+
 struct DefaultFacade : pro::facade_builder::build {};
 static_assert(
     std::is_same_v<pro::proxy<DefaultFacade>::facade_type, DefaultFacade>);
 static_assert(DefaultFacade::copyability == pro::constraint_level::none);
-static_assert(DefaultFacade::relocatability == pro::constraint_level::nothrow);
+static_assert(DefaultFacade::relocatability == pro::constraint_level::trivial);
 static_assert(DefaultFacade::destructibility == pro::constraint_level::nothrow);
 static_assert(DefaultFacade::max_size >= 2 * sizeof(void*));
 static_assert(DefaultFacade::max_align >= sizeof(void*));
@@ -75,7 +89,7 @@ static_assert(std::is_nothrow_constructible_v<
               pro::proxy<DefaultFacade>, std::in_place_type_t<MockFunctionPtr>,
               MockFunctionPtr>);
 static_assert(sizeof(pro::proxy<DefaultFacade>) ==
-              4 * sizeof(void*)); // VTABLE should be embeded
+              3 * sizeof(void*)); // VTABLE should be embeded
 
 static_assert(!std::is_copy_constructible_v<pro::proxy<DefaultFacade>>);
 static_assert(!std::is_copy_assignable_v<pro::proxy<DefaultFacade>>);
@@ -115,8 +129,6 @@ static_assert(std::is_nothrow_constructible_v<pro::proxy<DefaultFacade>,
                                               MockFunctionPtr>);
 static_assert(
     std::is_nothrow_assignable_v<pro::proxy<DefaultFacade>, MockFunctionPtr>);
-static_assert(sizeof(pro::proxy<DefaultFacade>) ==
-              4 * sizeof(void*)); // VTABLE should be embeded
 
 struct CopyableFacade : pro::facade_builder                               //
                         ::support_copy<pro::constraint_level::nontrivial> //
@@ -158,7 +170,7 @@ static_assert(std::is_nothrow_constructible_v<pro::proxy<CopyableFacade>,
 static_assert(
     std::is_nothrow_assignable_v<pro::proxy<CopyableFacade>, MockFunctionPtr>);
 static_assert(sizeof(pro::proxy<CopyableFacade>) ==
-              3 * sizeof(void*)); // VTABLE should not be embeded
+              4 * sizeof(void*)); // VTABLE should be embeded
 
 struct CopyableSmallFacade
     : pro::facade_builder                               //
@@ -191,7 +203,7 @@ static_assert(
 static_assert(
     std::is_assignable_v<pro::proxy<CopyableSmallFacade>, MockFunctionPtr>);
 static_assert(sizeof(pro::proxy<CopyableSmallFacade>) ==
-              2 * sizeof(void*)); // VTABLE should not be embeded
+              3 * sizeof(void*)); // VTABLE should be embeded
 
 struct TrivialFacade : pro::facade_builder                                   //
                        ::restrict_layout<sizeof(void*), alignof(void*)>      //

From 798b20b0c6ce69f55ea1f184f75248a598cb965a Mon Sep 17 00:00:00 2001
From: Mingxin Wang <mingxwa@microsoft.com>
Date: Wed, 16 Jul 2025 11:12:40 +0800
Subject: [PATCH 2/2] Resolve comments

---
 docs/spec/ProFacade.md   | 21 ++++++++++++++-------
 include/proxy/v4/proxy.h | 13 +++++++------
 2 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/docs/spec/ProFacade.md b/docs/spec/ProFacade.md
index 7b73146e..0a61c4bf 100644
--- a/docs/spec/ProFacade.md
+++ b/docs/spec/ProFacade.md
@@ -23,16 +23,23 @@ Relocatability is defined as *move-construct an object and then destroy the orig
 | `constraint_level::nothrow`    | `std::is_nothrow_move_constructible_v<P> && std::is_nothrow_destructible_v<P>` |
 | `constraint_level::trivial`    | *trivially relocatable* (see below)                          |
 
-C++26 introduces the type trait `std::is_trivially_relocatable_v` ([P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html)). The library supports C++20/23 and C++26 with a three-stage evaluation:
+C++26 introduces the type trait `std::is_trivially_relocatable_v` ([P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html)). The library evaluates *trivial relocatability* as follows:
 
-1. If `std::is_trivially_relocatable_v<P>` is available **and evaluates to `true`**, the requirement is satisfied.
-2. Otherwise, a conservative fallback is applied: `std::is_trivially_move_constructible_v<P> && std::is_trivially_destructible_v<P>`.
-3. If the fallback is `false`, the library consults an explicit curated allow-list of types that are known to be safely relocatable:
-  - `std::unique_ptr<T, D>` when `D` is itself trivially relocatable
-  - `std::shared_ptr<T>`
+- When `std::is_trivially_relocatable_v<P>` is available
+  – If the trait evaluates to `true`, the requirement is met.
+  – If it evaluates to `false`, the library falls back to the **allow-list** (see below).
+
+- When the trait is **not** available
+  – A conservative check is used: `std::is_trivially_move_constructible_v<P> && std::is_trivially_destructible_v<P>`
+  – If that check is `false`, the allow-list is consulted.
+
+The allow-list contains types that are known to be safely relocatable even when the compiler cannot confirm it:
+
+  - `std::unique_ptr<T, D>` when `D` is trivially relocatable  
+  - `std::shared_ptr<T>`  
   - `std::weak_ptr<T>`
 
-This sequence reduces the risk of false negatives while compiler support for C++26 is still being rolled out, without compromising safety.
+This strategy avoids false negatives caused by gaps in current compiler implementations of the C++26 feature set while maintaining safety.
 
 ## See Also
 
diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h
index 41216250..b00e1c03 100644
--- a/include/proxy/v4/proxy.h
+++ b/include/proxy/v4/proxy.h
@@ -173,16 +173,17 @@ consteval bool has_relocatability(constraint_level level) {
     return std::is_nothrow_move_constructible_v<T> &&
            std::is_nothrow_destructible_v<T>;
   case constraint_level::trivial:
+    if constexpr (
 #if __cpp_lib_trivially_relocatable >= 202502L
-    if constexpr (std::is_trivially_relocatable_v<T>) {
-      return true;
-    }
+        std::is_trivially_relocatable_v<T>
+#else
+        std::is_trivially_move_constructible_v<T> &&
+        std::is_trivially_destructible_v<T>
 #endif // __cpp_lib_trivially_relocatable >= 202502L
-    if constexpr (tr_override_traits<T>::applicable) {
+    ) {
       return true;
     } else {
-      return std::is_trivially_move_constructible_v<T> &&
-             std::is_trivially_destructible_v<T>;
+      return tr_override_traits<T>::applicable;
     }
   default:
     return false;
