Skip to content

Commit

Permalink
diagnose application of many traits to incomplete types
Browse files Browse the repository at this point in the history
Summary:
Traits which in any way evaluate class/struct/union body or enum underlying type should take care to forbid and diagnose application to any incomplete non-function type.

For quicker results, we may diagnose application to any incomplete or function type via the `sizeof` trick, where `sizeof` is inapplicable to incomplete and function types.

Reviewed By: bcardosolopes

Differential Revision: D51500675

fbshipit-source-id: 26168da60222eac8bf9de596fcbd1f25b7e509f9
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Nov 30, 2023
1 parent a0ccd7f commit 5cc658b
Show file tree
Hide file tree
Showing 13 changed files with 64 additions and 44 deletions.
17 changes: 13 additions & 4 deletions folly/Traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ struct is_constexpr_default_constructible_ {
static std::true_type sfinae(T*);
static std::false_type sfinae(void*);
template <typename T>
static constexpr bool apply =
static constexpr bool apply = sizeof(T) &&
decltype(sfinae(static_cast<T*>(nullptr)))::value;
};

Expand Down Expand Up @@ -368,6 +368,8 @@ struct detected_<void_t<T<A...>>, D, T, A...> {
// alias value_t as std::false_type and has member type alias type as D.
//
// mimic: std::experimental::detected_or, Library Fundamentals TS v2
//
// Note: not resilient agaist incomplete types; may violate ODR.
template <typename D, template <typename...> class T, typename... A>
using detected_or = detail::detected_<void, D, T, A...>;

Expand All @@ -379,6 +381,8 @@ using detected_or = detail::detected_<void, D, T, A...>;
// Equivalent to detected_or<D, T, A...>::type.
//
// mimic: std::experimental::detected_or_t, Library Fundamentals TS v2
//
// Note: not resilient agaist incomplete types; may violate ODR.
template <typename D, template <typename...> class T, typename... A>
using detected_or_t = typename detected_or<D, T, A...>::type;

Expand All @@ -390,6 +394,8 @@ using detected_or_t = typename detected_or<D, T, A...>::type;
// Equivalent to detected_or_t<nonesuch, T, A...>.
//
// mimic: std::experimental::detected_t, Library Fundamentals TS v2
//
// Note: not resilient agaist incomplete types; may violate ODR.
template <template <typename...> class T, typename... A>
using detected_t = detected_or_t<nonesuch, T, A...>;

Expand All @@ -407,6 +413,8 @@ using detected_t = detected_or_t<nonesuch, T, A...>;
// mimic: std::experimental::is_detected, std::experimental::is_detected_v,
// Library Fundamentals TS v2
//
// Note: not resilient agaist incomplete types; may violate ODR.
//
// Note: the trait type is_detected differs here by being deferred.
template <template <typename...> class T, typename... A>
FOLLY_INLINE_VARIABLE constexpr bool is_detected_v =
Expand Down Expand Up @@ -591,7 +599,7 @@ struct IsNothrowSwappable
template <class T>
struct IsRelocatable
: std::conditional<
is_detected_v<traits_detail::detect_IsRelocatable, T>,
sizeof(T) && is_detected_v<traits_detail::detect_IsRelocatable, T>,
traits_detail::has_true_IsRelocatable<T>,
// TODO add this line (and some tests for it) when we
// upgrade to gcc 4.7
Expand All @@ -601,7 +609,8 @@ struct IsRelocatable
template <class T>
struct IsZeroInitializable
: std::conditional<
is_detected_v<traits_detail::detect_IsZeroInitializable, T>,
sizeof(T) &&
is_detected_v<traits_detail::detect_IsZeroInitializable, T>,
traits_detail::has_true_IsZeroInitializable<T>,
bool_constant< //
!std::is_class<T>::value && //
Expand Down Expand Up @@ -689,7 +698,7 @@ struct is_transparent : bool_constant<is_transparent_v<T>> {};
namespace detail {

template <typename T, typename = void>
FOLLY_INLINE_VARIABLE constexpr bool is_allocator_ = false;
FOLLY_INLINE_VARIABLE constexpr bool is_allocator_ = !sizeof(T);
template <typename T>
FOLLY_INLINE_VARIABLE constexpr bool is_allocator_<
T,
Expand Down
6 changes: 3 additions & 3 deletions folly/container/Foreach-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ template <typename T>
using EnableIfMemberGetFound =
void_t<decltype(std::declval<T>().template get<0>())>;
template <typename, typename T>
struct IsMemberGetFound : std::false_type {};
struct IsMemberGetFound : bool_constant<!sizeof(T)> {};
template <typename T>
struct IsMemberGetFound<EnableIfMemberGetFound<T>, T> : std::true_type {};

Expand Down Expand Up @@ -91,7 +91,7 @@ using EnableIfTuple = void_t<
decltype(get_impl<0>(std::declval<T>())),
decltype(std::tuple_size<T>::value)>;
template <typename, typename T>
struct IsTuple : std::false_type {};
struct IsTuple : bool_constant<!sizeof(T)> {};
template <typename T>
struct IsTuple<EnableIfTuple<T>, T> : std::true_type {};

Expand All @@ -103,7 +103,7 @@ using EnableIfRange = void_t<
decltype(access::begin(std::declval<T&>())),
decltype(access::end(std::declval<T&>()))>;
template <typename, typename T>
struct IsRange : std::false_type {};
struct IsRange : bool_constant<!sizeof(T)> {};
template <typename T>
struct IsRange<EnableIfRange<T>, T> : std::true_type {};

Expand Down
6 changes: 4 additions & 2 deletions folly/container/Iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ namespace folly {
// Whether std::distance over a pair of iterators is reasonably known to give
// the distance without advancing the iterators or copies of them.
template <typename Iter, typename SentinelIter>
FOLLY_INLINE_VARIABLE constexpr bool iterator_has_known_distance_v = false;
FOLLY_INLINE_VARIABLE constexpr bool iterator_has_known_distance_v =
!sizeof(Iter) && !sizeof(SentinelIter);
template <typename Iter>
FOLLY_INLINE_VARIABLE constexpr bool iterator_has_known_distance_v<Iter, Iter> =
std::is_base_of<
Expand Down Expand Up @@ -83,7 +84,8 @@ using iterator_category_t =
namespace detail {

template <typename Iter, typename Category, typename = void>
FOLLY_INLINE_VARIABLE constexpr bool iterator_category_matches_v_ = false;
FOLLY_INLINE_VARIABLE constexpr bool iterator_category_matches_v_ =
!sizeof(Iter) && !sizeof(Category);
template <typename Iter, typename Category>
FOLLY_INLINE_VARIABLE constexpr bool iterator_category_matches_v_<
Iter,
Expand Down
2 changes: 1 addition & 1 deletion folly/container/detail/F14Policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ struct FOLLY_MSVC_DECLSPEC(empty_bases) BasePolicy
// Detection for folly_assume_32bit_hash

template <typename Hasher, typename Void = void>
struct ShouldAssume32BitHash : std::false_type {};
struct ShouldAssume32BitHash : bool_constant<!sizeof(Hasher)> {};

template <typename Hasher>
struct ShouldAssume32BitHash<
Expand Down
2 changes: 1 addition & 1 deletion folly/container/detail/F14Table.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ struct StdNodeReplica {
#else

template <typename H>
struct StdIsFastHash : std::true_type {};
struct StdIsFastHash : bool_constant<!!sizeof(H)> {};
template <>
struct StdIsFastHash<std::hash<long double>> : std::false_type {};
template <typename... Args>
Expand Down
4 changes: 2 additions & 2 deletions folly/detail/PolyDetail.h
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ struct Sig<R(A&, As...)> : SigImpl<R, A&, As...> {
};

template <class T, class I, class = void>
struct ModelsInterface2_ : std::false_type {};
struct ModelsInterface2_ : bool_constant<!sizeof(T) || !sizeof(I)> {};

template <class T, class I>
struct ModelsInterface2_<
Expand All @@ -937,7 +937,7 @@ struct ModelsInterface2_<
MembersOf<std::decay_t<I>, std::decay_t<T>>>> : std::true_type {};

template <class T, class I, class = void>
struct ModelsInterface_ : std::false_type {};
struct ModelsInterface_ : bool_constant<!sizeof(T) || !sizeof(I)> {};

template <class T, class I>
struct ModelsInterface_<
Expand Down
19 changes: 15 additions & 4 deletions folly/experimental/coro/Traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ inline constexpr bool is_coroutine_handle_v =
/// implementations have particular requirements on the promise (eg. the
/// stack-aware awaiters may require the .getAsyncFrame() method)
template <typename T, typename = void>
struct is_awaiter : std::false_type {};
struct is_awaiter : bool_constant<!sizeof(T)> {};

template <typename T>
struct is_awaiter<T, std::enable_if_t<std::is_void_v<T>>> : std::false_type {};

template <typename T>
struct is_awaiter<
Expand All @@ -85,7 +88,11 @@ constexpr bool is_awaiter_v = is_awaiter<T>::value;
namespace detail {

template <typename Awaitable, typename = void>
struct _has_member_operator_co_await : std::false_type {};
struct _has_member_operator_co_await : bool_constant<!sizeof(Awaitable)> {};

template <typename T>
struct _has_member_operator_co_await<T, std::enable_if_t<std::is_void_v<T>>>
: std::false_type {};

template <typename Awaitable>
struct _has_member_operator_co_await<
Expand All @@ -94,7 +101,11 @@ struct _has_member_operator_co_await<
: is_awaiter<decltype(std::declval<Awaitable>().operator co_await())> {};

template <typename Awaitable, typename = void>
struct _has_free_operator_co_await : std::false_type {};
struct _has_free_operator_co_await : bool_constant<!sizeof(Awaitable)> {};

template <typename T>
struct _has_free_operator_co_await<T, std::enable_if_t<std::is_void_v<T>>>
: std::false_type {};

template <typename Awaitable>
struct _has_free_operator_co_await<
Expand Down Expand Up @@ -200,7 +211,7 @@ using await_result_t = typename await_result<Awaitable>::type;
namespace detail {

template <typename Promise, typename = void>
constexpr bool promiseHasAsyncFrame_v = false;
constexpr bool promiseHasAsyncFrame_v = !sizeof(Promise);

template <typename Promise>
constexpr bool promiseHasAsyncFrame_v<
Expand Down
13 changes: 11 additions & 2 deletions folly/experimental/coro/ViaIfAsync.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,12 @@ class ViaIfAsyncAwaitable {
namespace detail {

template <typename SemiAwaitable, typename = void>
struct HasViaIfAsyncMethod : std::false_type {};
struct HasViaIfAsyncMethod : bool_constant<!sizeof(SemiAwaitable)> {};

template <typename SemiAwaitable>
struct HasViaIfAsyncMethod<
SemiAwaitable,
std::enable_if_t<std::is_void_v<SemiAwaitable>>> : std::false_type {};

template <typename SemiAwaitable>
struct HasViaIfAsyncMethod<
Expand Down Expand Up @@ -496,7 +501,11 @@ struct ViaIfAsyncFunction {
FOLLY_DEFINE_CPO(detail::adl::ViaIfAsyncFunction, co_viaIfAsync)

template <typename T, typename = void>
struct is_semi_awaitable : std::false_type {};
struct is_semi_awaitable : bool_constant<!sizeof(T)> {};

template <typename T>
struct is_semi_awaitable<T, std::enable_if_t<std::is_void_v<T>>>
: std::false_type {};

template <typename T>
struct is_semi_awaitable<
Expand Down
3 changes: 2 additions & 1 deletion folly/functional/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ struct match_empty_function_protocol_fn {
decltype(static_cast<bool>(static_cast<T const&>(T(nullptr)) == nullptr));

template <typename T>
static constexpr bool cx_matches_v = is_detected_v<detect_from_eq_nullptr, T>;
static constexpr bool cx_matches_v =
sizeof(T) && is_detected_v<detect_from_eq_nullptr, T>;

public:
template <typename T, std::enable_if_t<!cx_matches_v<T>, int> = 0>
Expand Down
2 changes: 1 addition & 1 deletion folly/hash/Hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ struct IsAvalanchingHasher;

namespace detail {
template <typename Hasher, typename Void = void>
struct IsAvalanchingHasherFromMemberType : std::false_type {};
struct IsAvalanchingHasherFromMemberType : bool_constant<!sizeof(Hasher)> {};

template <typename Hasher>
struct IsAvalanchingHasherFromMemberType<
Expand Down
13 changes: 5 additions & 8 deletions folly/memory/not_null-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,13 @@ namespace folly {

namespace detail {
template <typename T>
struct is_not_null_helper : std::false_type {};
inline constexpr bool is_not_null_v = is_instantiation_of_v<not_null, T>;
template <typename T>
struct is_not_null_helper<not_null<T>> : std::true_type {};
template <typename T>
struct is_not_null
: is_not_null_helper<std::remove_cv_t<std::remove_reference_t<T>>> {};
template <typename T>
inline constexpr bool is_not_null_v = is_not_null<T>::value;
struct is_not_null : bool_constant<is_not_null_v<T>> {};

template <typename T, typename = std::enable_if_t<!is_not_null_v<T>>>
template <
typename T,
typename = std::enable_if_t<!is_not_null_v<remove_cvref_t<T>>>>
auto maybeUnwrap(T&& t) {
return std::forward<T>(t);
}
Expand Down
5 changes: 0 additions & 5 deletions folly/memory/test/not_null_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,6 @@ TEST_F(NotNullTest, is_not_null) {
detail::is_not_null_v<const int* const> == false, "is_not_null failure");
static_assert(
detail::is_not_null_v<not_null<int*>> == true, "is_not_null failure");
static_assert(
detail::is_not_null_v<const not_null<int*>&> == true,
"is_not_null failure");
static_assert(
detail::is_not_null_v<not_null<int*>&&> == true, "is_not_null failure");
}

template <typename To, typename From>
Expand Down
16 changes: 6 additions & 10 deletions folly/sorted_vector_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -862,13 +862,11 @@ inline void swap(
}

template <typename T>
struct is_sorted_vector_set : std::false_type {};

template <typename... T>
struct is_sorted_vector_set<folly::sorted_vector_set<T...>> : std::true_type {};
FOLLY_INLINE_VARIABLE constexpr bool is_sorted_vector_set_v =
detail::is_instantiation_of_v<sorted_vector_set, T>;

template <typename T>
constexpr bool is_sorted_vector_set_v = is_sorted_vector_set<T>::value;
struct is_sorted_vector_set : bool_constant<is_sorted_vector_set_v<T>> {};

#if FOLLY_HAS_MEMORY_RESOURCE

Expand Down Expand Up @@ -1585,13 +1583,11 @@ inline void swap(
}

template <typename T>
struct is_sorted_vector_map : std::false_type {};

template <typename... T>
struct is_sorted_vector_map<folly::sorted_vector_map<T...>> : std::true_type {};
FOLLY_INLINE_VARIABLE constexpr bool is_sorted_vector_map_v =
detail::is_instantiation_of_v<sorted_vector_map, T>;

template <typename T>
constexpr bool is_sorted_vector_map_v = is_sorted_vector_map<T>::value;
struct is_sorted_vector_map : bool_constant<is_sorted_vector_map_v<T>> {};

#if FOLLY_HAS_MEMORY_RESOURCE

Expand Down

0 comments on commit 5cc658b

Please sign in to comment.