diff --git a/include/proxy/v4/detail/core.h b/include/proxy/v4/detail/core.h index 6ea3d73..4262904 100644 --- a/include/proxy/v4/detail/core.h +++ b/include/proxy/v4/detail/core.h @@ -41,7 +41,7 @@ template struct basic_facade_traits; template -struct meta_storage; +struct proxy_meta; } // namespace detail @@ -246,15 +246,14 @@ struct proxy_helper { proxy& p_; }; - template - static const meta_storage& get_meta(const proxy& p) noexcept { - assert(p.has_value()); - return p.meta_; + template + static const M& get_meta(const proxy& p) noexcept { + assert(p.meta_.has_value()); + return *p.meta_; } - template - static const meta_storage& - get_meta(const proxy_indirect_accessor& p) noexcept { - return get_meta(as_proxy(p)); + template + static const M& get_meta(const proxy_indirect_accessor& p) noexcept { + return get_meta(as_proxy(p)); } template static void* get_ptr(proxy& p) noexcept { @@ -659,6 +658,122 @@ struct specialization_traits, TT> : applicable_traits {}; template class TT> concept specialization_of = specialization_traits::applicable; +template +struct first_containing_reduction : std::type_identity {}; +template +struct first_containing_reduction + : std::conditional, I, + void> {}; + +template +struct most_containing_reduction + : std::conditional, I, + O> {}; +template +struct sfinae_unique_types_traits : std::type_identity {}; +template +struct sfinae_unique_types_traits< + std::enable_if_t<(std::is_nothrow_convertible_v || + ...)>, + std::tuple, std::tuple> + : sfinae_unique_types_traits, std::tuple> {}; +template +struct sfinae_unique_types_traits< + std::enable_if_t || + ...)>, + std::tuple, std::tuple> + : sfinae_unique_types_traits< + void, + std::tuple, U, Us...>>, + std::tuple> {}; +template +using unique_types_t = sfinae_unique_types_traits, T>::type; + +template +concept nullable = requires(T v, const T cv) { + { v.reset() } noexcept; + { cv.has_value() } noexcept -> std::same_as; +}; + +struct sentinel_meta { + sentinel_meta() = default; + template + constexpr explicit sentinel_meta(std::in_place_type_t

) noexcept : v_(1) {} + void reset() noexcept { v_ = 0; } + bool has_value() const noexcept { return v_; } + +private: + std::ptrdiff_t v_; +}; + +template +struct PRO4D_ENFORCE_EBO composite_meta : Ms... { + composite_meta() = default; + template + constexpr explicit composite_meta(std::in_place_type_t

) + : Ms(std::in_place_type

)... {} +}; + +template +struct proxy_meta_base_impl { + constexpr proxy_meta_base_impl() noexcept {} + template + constexpr explicit proxy_meta_base_impl(std::in_place_type_t

) + : value_(std::in_place_type

) {} + proxy_meta_base_impl(const proxy_meta_base_impl& rhs) noexcept + : proxy_meta_base_impl() { + assign(rhs); + } + proxy_meta_base_impl& operator=(const proxy_meta_base_impl& rhs) noexcept { + assign(rhs); + return *this; + } + + template + requires(std::is_nothrow_convertible_v || + (std::is_nothrow_convertible_v || ...)) + constexpr operator const T&() const noexcept { + return static_cast, void, First, Rest...>&>( + value_); + } + + bool has_value() const noexcept { + return static_cast(value_).has_value(); + } + void reset() noexcept { static_cast(value_).reset(); } + +private: + void assign(const proxy_meta_base_impl& rhs) noexcept { + if (rhs.has_value()) { + value_ = rhs.value_; + } else { + reset(); + } + } + + composite_meta value_; +}; +template + requires(std::is_trivially_copyable_v) +struct proxy_meta_base_impl : First { + using First::First; +}; + +template +struct proxy_meta_base_traits + : specialization_type_traits>, + sentinel_meta> {}; +template +struct proxy_meta_base_traits + : specialization_type_traits>> {}; +template +using proxy_meta_base_t = typename proxy_meta_base_traits::type; + template consteval void diagnose_proxiable_size_too_large() { static_assert(ActualSize <= MaxSize, "not proxiable due to size too large"); @@ -803,8 +918,8 @@ struct facade_traits : specialization_t, specialization_t { - using meta_storage_base = specialization_t< - compact_facade_meta_traits::storage, + using meta_base = specialization_t< + proxy_meta_base_t, composite_t, lifetime_meta_t, @@ -849,8 +964,8 @@ struct facade_traits : specialization_t -struct meta_storage : facade_traits::meta_storage_base { - using base = facade_traits::meta_storage_base; +struct proxy_meta : facade_traits::meta_base { + using base = facade_traits::meta_base; using base::base; }; @@ -889,7 +1004,7 @@ template ret_t invoke_impl(P&& p, Args&&... args) { using Ctx = erased_context; Ctx ctx{proxy_helper::get_ptr(p)}; - const auto& inv = proxy_helper::get_meta(p).template get>(); + const auto& inv = proxy_helper::get_meta>(p); if constexpr (overload_traits::this_qualifier == qualifier_type::rv) { proxy_helper::meta_resetting_guard guard{p}; return inv(ctx, std::forward(args)...); @@ -935,8 +1050,7 @@ class proxy_indirect_accessor } template friend const R& reflect(const proxy_indirect_accessor& p) noexcept { - return detail::proxy_helper::get_meta(p) - .template get>() + return detail::proxy_helper::get_meta>(p) .reflector; } }; @@ -1153,8 +1267,7 @@ class proxy : public detail::facade_traits::direct_accessor, } template friend const R& reflect(const proxy& p) noexcept { - return detail::proxy_helper::get_meta(p) - .template get>() + return detail::proxy_helper::get_meta>(p) .reflector; } @@ -1206,7 +1319,7 @@ class proxy : public detail::facade_traits::direct_accessor, P& result = *std::construct_at(reinterpret_cast(ptr_), std::forward(args)...); if constexpr (proxiable) { - meta_ = detail::meta_storage{std::in_place_type

}; + meta_ = decltype(meta_){std::in_place_type

}; } else { detail::facade_traits::template diagnose_proxiable_noreturn

(); } @@ -1234,7 +1347,8 @@ class proxy : public detail::facade_traits::direct_accessor, }) alignas(F::max_align) std::byte ptr_[F::max_size]; - detail::meta_storage meta_; + typename compact_facade_meta_traits::template storage> + meta_; }; template diff --git a/include/proxy/v4/detail/facade_meta_traits.h b/include/proxy/v4/detail/facade_meta_traits.h index 56d71b6..8a6298e 100644 --- a/include/proxy/v4/detail/facade_meta_traits.h +++ b/include/proxy/v4/detail/facade_meta_traits.h @@ -78,6 +78,11 @@ class meta_ptr { schema()); return *this; } + meta_ptr& operator=(const T* p) noexcept { + p_ = ptrauth_sign_unauthenticated(p, ptrauth_key_cxx_vtable_pointer, + schema()); + return *this; + } meta_ptr& operator=(std::nullptr_t) noexcept { p_ = nullptr; return *this; @@ -102,12 +107,6 @@ template using meta_ptr = const T*; #endif // PRO4D_HAS_PAC -template -concept nullable = requires(T v, const T cv) { - { v.reset() } noexcept; - { cv.has_value() } noexcept -> std::same_as; -}; - template struct invoker_base { invoker_base() = default; @@ -141,112 +140,73 @@ struct invoker; PRO4D_DEF_OVERLOAD_SPECIALIZATIONS(PRO4D_DEF_INVOKER) #undef PRO4D_DEF_INVOKER -struct sentinel_meta { - sentinel_meta() = default; - template - explicit sentinel_meta(std::in_place_type_t

) noexcept : v_(1) {} - void reset() noexcept { v_ = 0; } - bool has_value() const noexcept { return v_; } - -private: - std::ptrdiff_t v_; -}; - -template -struct PRO4D_ENFORCE_EBO inline_meta_storage : First, Rest... { - using First::has_value; - using First::reset; - - constexpr inline_meta_storage() noexcept {} - template - constexpr explicit inline_meta_storage(std::in_place_type_t

) - : First(std::in_place_type

), Rest(std::in_place_type

)... {} - inline_meta_storage(const inline_meta_storage& rhs) noexcept - : inline_meta_storage() { - if (static_cast(rhs).has_value()) { - static_cast(*this) = static_cast(rhs); - ((static_cast(*this) = static_cast(rhs)), ...); - } else { - static_cast(*this).reset(); - } - } - inline_meta_storage& operator=(const inline_meta_storage& rhs) noexcept { - if (static_cast(rhs).has_value()) { - static_cast(*this) = static_cast(rhs); - ((static_cast(*this) = static_cast(rhs)), ...); - } else { - static_cast(*this).reset(); - } +template +struct PRO4D_ENFORCE_EBO inplace_meta_storage : M { + using M::M; + + inplace_meta_storage() = default; + inplace_meta_storage(const inplace_meta_storage&) = default; + template + requires(std::is_nothrow_convertible_v) + inplace_meta_storage(const inplace_meta_storage& rhs) noexcept + : M(static_cast(*rhs)) {} + inplace_meta_storage& operator=(const inplace_meta_storage&) = default; + template + requires(std::is_nothrow_convertible_v) + inplace_meta_storage& + operator=(const inplace_meta_storage& rhs) noexcept { + static_cast(*this) = static_cast(*rhs); return *this; } - template - const M& get() const noexcept { - return static_cast(*this); - } -}; -template -struct inline_meta_storage : First { - using First::First; - template - const M& get() const noexcept { - return static_cast(*this); - } + const M& operator*() const noexcept { return *this; } }; -template +template struct static_meta_storage { static_meta_storage() = default; + template + requires(std::is_nothrow_convertible_v) + static_meta_storage(const static_meta_storage& rhs) noexcept + : ptr_(std::addressof(static_cast(*rhs))) {} + template + requires(std::is_nothrow_convertible_v) + static_meta_storage& operator=(const static_meta_storage& rhs) noexcept { + ptr_ = std::addressof(static_cast(*rhs)); + return *this; + } template explicit static_meta_storage(std::in_place_type_t

) : ptr_(std::addressof(storage

)) {} bool has_value() const noexcept { return ptr_ != nullptr; } void reset() noexcept { ptr_ = nullptr; } - template - const M& get() const noexcept { - return (*ptr_).template get(); - } + const M& operator*() const noexcept { return *ptr_; } private: - meta_ptr, void (*)(Ms...)> ptr_; + meta_ptr ptr_; template - static inline const inline_meta_storage storage{std::in_place_type

}; + static inline const M storage{std::in_place_type

}; }; -template -struct compact_meta_storage_traits - : std::type_identity> {}; -template -struct compact_meta_storage_traits - : std::type_identity> {}; -template <> -struct compact_meta_storage_traits<> - : std::type_identity> {}; - -template -struct flat_meta_storage_traits - : std::type_identity> {}; -template -struct flat_meta_storage_traits - : std::type_identity> {}; - } // namespace detail struct compact_facade_meta_traits { template using invoker = detail::invoker; - template - using storage = detail::compact_meta_storage_traits::type; + template + using storage = std::conditional_t, + detail::static_meta_storage>; }; struct flat_facade_meta_traits { template using invoker = detail::invoker; - template - using storage = detail::flat_meta_storage_traits::type; + template + using storage = detail::inplace_meta_storage; }; } // namespace pro::inline v4 diff --git a/tests/proxy_detail_tests.cpp b/tests/proxy_detail_tests.cpp index 760f9ce..893f988 100644 --- a/tests/proxy_detail_tests.cpp +++ b/tests/proxy_detail_tests.cpp @@ -27,4 +27,80 @@ static_assert(pro::detail::explicitly_convertible); static_assert(!pro::detail::explicitly_convertible); static_assert(!pro::detail::explicitly_convertible); +template +struct NullableMeta { + NullableMeta() = default; + template + constexpr explicit NullableMeta(std::in_place_type_t

) noexcept + : v(I + 1) {} + void reset() noexcept { v = 0; } + bool has_value() const noexcept { return v != 0; } + + int v = 0; +}; +template +struct PlainMeta { + PlainMeta() = default; + template + constexpr explicit PlainMeta(std::in_place_type_t

) noexcept {} +}; + +static_assert(pro::detail::nullable>); +static_assert(!pro::detail::nullable>); + +using M0 = NullableMeta<0>; +using M1 = NullableMeta<1>; +using M2 = NullableMeta<2>; +using M01 = pro::detail::proxy_meta_base_t; +using M02 = pro::detail::proxy_meta_base_t; + +static_assert(std::is_same_v< + pro::detail::proxy_meta_base_t<>, + pro::detail::proxy_meta_base_impl>); +static_assert(std::is_same_v>, + pro::detail::proxy_meta_base_impl< + pro::detail::sentinel_meta, PlainMeta<0>>>); +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); + +static_assert(std::is_base_of_v>); +static_assert(!std::is_base_of_v); + +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); +static_assert(std::is_same_v, + pro::detail::proxy_meta_base_impl>); +static_assert( + std::is_same_v>, + pro::detail::proxy_meta_base_impl< + pro::detail::proxy_meta_base_t>>); + +static_assert(std::is_nothrow_convertible_v); +static_assert(std::is_nothrow_convertible_v); +static_assert(!std::is_nothrow_convertible_v); +static_assert(!std::is_nothrow_convertible_v); + +inline constexpr pro::detail::proxy_meta_base_t kReordered{ + std::in_place_type}; +inline constexpr pro::detail::proxy_meta_base_t kDiamond{ + std::in_place_type}; + +static_assert(static_cast(kReordered).v == 1); +static_assert(static_cast(kReordered).v == 2); +static_assert( + std::addressof(static_cast(kDiamond)) == + std::addressof(static_cast(static_cast(kDiamond)))); +static_assert( + std::addressof(static_cast(kDiamond)) == + std::addressof(static_cast(static_cast(kDiamond)))); + } // namespace proxy_detail_tests_detail diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 16521bd..d4670a3 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -218,8 +218,8 @@ TEST(ProxyInvocationTests, TestMultipleDispatches_Duplicated) { ::add_convention)> // ::build {}; - static_assert(sizeof(pro::detail::meta_storage) == - sizeof(pro::detail::meta_storage>)); + static_assert(sizeof(pro::detail::proxy_meta) == + sizeof(pro::detail::proxy_meta>)); std::list l = {1, 2, 3}; pro::proxy p = &l; ASSERT_EQ(Size(*p), std::size_t{3}); diff --git a/tests/proxy_pac_tests.cpp b/tests/proxy_pac_tests.cpp index 53bf205..e3c56f5 100644 --- a/tests/proxy_pac_tests.cpp +++ b/tests/proxy_pac_tests.cpp @@ -18,8 +18,8 @@ namespace proxy_pac_tests_detail { template constexpr bool IsInlineMetaPreferred = pro::detail::specialization_of< - typename pro::detail::facade_traits::meta_storage_base, - pro::detail::inline_meta_storage>; + pro::compact_facade_meta_traits::storage>, + pro::detail::inplace_meta_storage>; template auto GetRawBytes(const T& v) noexcept { @@ -30,10 +30,13 @@ auto GetRawBytes(const T& v) noexcept { template void CorruptMeta(pro::proxy& p) noexcept { - const pro::detail::meta_storage& meta = - pro::detail::proxy_helper::get_meta(p); - std::byte* target = reinterpret_cast( - const_cast*>(std::addressof(meta))); + // meta_ is the second of the two slots of proxy, behind the storage of + // the contained value. + using Storage = + pro::compact_facade_meta_traits::storage>; + static_assert(sizeof(pro::proxy) == sizeof(Storage) + F::max_size); + std::byte* target = + reinterpret_cast(std::addressof(p)) + F::max_size; std::uintptr_t word; std::memcpy(&word, target, sizeof(word)); word ^= std::uintptr_t{1} << 54u; // Within the PAC bits for any VA size