Commit 9a85adb6 authored by Daniel Cheng's avatar Daniel Cheng Committed by Commit Bot

Improve readability of base::Bind{Once,Repeating} errors.

Change-Id: If44f3d970b3de9aa2d8bc9d237665cbac9bf5e1a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2490842
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822829}
parent d66a177c
...@@ -69,41 +69,99 @@ struct IsOnceCallback : std::false_type {}; ...@@ -69,41 +69,99 @@ struct IsOnceCallback : std::false_type {};
template <typename Signature> template <typename Signature>
struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {}; struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {};
// Helpers to make error messages slightly more readable.
template <int i>
struct BindArgument {
template <typename ForwardingType>
struct ForwardedAs {
template <typename FunctorParamType>
struct ToParamWithType {
static constexpr bool kCanBeForwardedToBoundFunctor =
std::is_constructible<FunctorParamType, ForwardingType>::value;
// Note that this intentionally drops the const qualifier from
// `ForwardingType`, to test if it *could* have been successfully
// forwarded if `Passed()` had been used.
static constexpr bool kMoveOnlyTypeMustUseBasePassed =
kCanBeForwardedToBoundFunctor ||
!std::is_constructible<FunctorParamType,
std::decay_t<ForwardingType>&&>::value;
};
};
template <typename BoundAsType>
struct BoundAs {
template <typename StorageType>
struct StoredAs {
static constexpr bool kBindArgumentCanBeCaptured =
std::is_constructible<StorageType, BoundAsType>::value;
// Note that this intentionally drops the const qualifier from
// `BoundAsType`, to test if it *could* have been successfully bound if
// `std::move()` had been used.
static constexpr bool kMoveOnlyTypeMustUseStdMove =
kBindArgumentCanBeCaptured ||
!std::is_constructible<StorageType,
std::decay_t<BoundAsType>&&>::value;
};
};
};
// Helper to assert that parameter |i| of type |Arg| can be bound, which means: // Helper to assert that parameter |i| of type |Arg| can be bound, which means:
// - |Arg| can be retained internally as |Storage|. // - |Arg| can be retained internally as |Storage|.
// - |Arg| can be forwarded as |Unwrapped| to |Param|. // - |Arg| can be forwarded as |Unwrapped| to |Param|.
template <size_t i, template <int i,
typename Arg, typename Arg,
typename Storage, typename Storage,
typename Unwrapped, typename Unwrapped,
typename Param> typename Param>
struct AssertConstructible { struct AssertConstructible {
private: private:
static constexpr bool param_is_forwardable = // With `BindRepeating`, there are two decision points for how to handle a
std::is_constructible<Param, Unwrapped>::value; // move-only type:
// Unlike the check for binding into storage below, the check for //
// forwardability drops the const qualifier for repeating callbacks. This is // 1. Whether the move-only argument should be moved into the internal
// to try to catch instances where std::move()--which forwards as a const // `BindState`. Either `std::move()` or `Passed` is sufficient to trigger
// reference with repeating callbacks--is used instead of base::Passed(). // move-only semantics.
// 2. Whether or not the bound, move-only argument should be moved to the
// bound functor when invoked. When the argument is bound with `Passed`,
// invoking the callback will destructively move the bound, move-only
// argument to the bound functor. In contrast, if the argument is bound
// with `std::move()`, `RepeatingCallback` will attempt to call the bound
// functor with a constant reference to the bound, move-only argument. This
// will fail if the bound functor accepts that argument by value, since the
// argument cannot be copied. It is this latter case that this
// static_assert aims to catch.
//
// In contrast, `BindOnce()` only has one decision point. Once a move-only
// type is captured by value into the internal `BindState`, the bound,
// move-only argument will always be moved to the functor when invoked.
// Failure to use std::move will simply fail the `kMoveOnlyTypeMustUseStdMove`
// assert below instead.
//
// Note: `Passed()` is a legacy of supporting move-only types when repeating
// callbacks were the only callback type. A `RepeatingCallback` with a
// `Passed()` argument is really a `OnceCallback` and should eventually be
// migrated.
static_assert(
BindArgument<i>::template ForwardedAs<Unwrapped>::
template ToParamWithType<Param>::kMoveOnlyTypeMustUseBasePassed,
"base::BindRepeating() argument is a move-only type. Use base::Passed() "
"instead of std::move() to transfer ownership from the callback to the "
"bound functor.");
static_assert( static_assert(
param_is_forwardable || BindArgument<i>::template ForwardedAs<Unwrapped>::
!std::is_constructible<Param, std::decay_t<Unwrapped>&&>::value, template ToParamWithType<Param>::kCanBeForwardedToBoundFunctor,
"Bound argument |i| is move-only but will be forwarded by copy. " "Type mismatch between bound argument and bound functor's parameter.");
"Ensure |Arg| is bound using base::Passed(), not std::move().");
static_assert(BindArgument<i>::template BoundAs<Arg>::template StoredAs<
Storage>::kMoveOnlyTypeMustUseStdMove,
"Attempting to bind a move-only type. Use std::move() to "
"transfer ownership to the created callback.");
// In practice, this static_assert should be quite rare as the storage type
// is deduced from the arguments passed to `BindOnce()`/`BindRepeating()`.
static_assert( static_assert(
param_is_forwardable, BindArgument<i>::template BoundAs<Arg>::template StoredAs<
"Bound argument |i| of type |Arg| cannot be forwarded as " Storage>::kBindArgumentCanBeCaptured,
"|Unwrapped| to the bound functor, which declares it as |Param|."); "Cannot capture argument: is the argument copyable or movable?");
static constexpr bool arg_is_storable =
std::is_constructible<Storage, Arg>::value;
static_assert(arg_is_storable ||
!std::is_constructible<Storage, std::decay_t<Arg>&&>::value,
"Bound argument |i| is move-only but will be bound by copy. "
"Ensure |Arg| is mutable and bound using std::move().");
static_assert(arg_is_storable,
"Bound argument |i| of type |Arg| cannot be converted and "
"bound as |Storage|.");
}; };
// Takes three same-length TypeLists, and applies AssertConstructible for each // Takes three same-length TypeLists, and applies AssertConstructible for each
......
...@@ -79,7 +79,8 @@ struct NonEmptyFunctor { ...@@ -79,7 +79,8 @@ struct NonEmptyFunctor {
void operator()() const {} void operator()() const {}
}; };
#if defined(NCTEST_METHOD_ON_CONST_OBJECT) // [r"fatal error: static_assert failed due to requirement 'param_is_forwardable' \"Bound argument |i| of type |Arg| cannot be forwarded as |Unwrapped| to the bound functor, which declares it as |Param|.\""] #if defined(NCTEST_METHOD_ON_CONST_OBJECT) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kCanBeForwardedToBoundFunctor.+?Type mismatch between bound argument and bound functor's parameter\."]
// Method bound to const-object. // Method bound to const-object.
// //
// Only const methods should be allowed to work with const objects. // Only const methods should be allowed to work with const objects.
...@@ -116,8 +117,7 @@ void WontCompile() { ...@@ -116,8 +117,7 @@ void WontCompile() {
no_ref_const_cb.Run(); no_ref_const_cb.Run();
} }
#elif defined(NCTEST_CONST_POINTER) // [r"fatal error: static_assert failed due to requirement 'param_is_forwardable' \"Bound argument |i| of type |Arg| cannot be forwarded as |Unwrapped| to the bound functor, which declares it as |Param|.\""] #elif defined(NCTEST_CONST_POINTER) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kCanBeForwardedToBoundFunctor.+?Type mismatch between bound argument and bound functor's parameter\."]
// Const argument used with non-const pointer parameter of same type. // Const argument used with non-const pointer parameter of same type.
// //
// This is just a const-correctness check. // This is just a const-correctness check.
...@@ -128,7 +128,7 @@ void WontCompile() { ...@@ -128,7 +128,7 @@ void WontCompile() {
pointer_same_cb.Run(); pointer_same_cb.Run();
} }
#elif defined(NCTEST_CONST_POINTER_SUBTYPE) // [r"fatal error: static_assert failed due to requirement 'param_is_forwardable' \"Bound argument |i| of type |Arg| cannot be forwarded as |Unwrapped| to the bound functor, which declares it as |Param|.\""] #elif defined(NCTEST_CONST_POINTER_SUBTYPE) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kCanBeForwardedToBoundFunctor.+?Type mismatch between bound argument and bound functor's parameter\."]
// Const argument used with non-const pointer parameter of super type. // Const argument used with non-const pointer parameter of super type.
// //
...@@ -157,11 +157,9 @@ void WontCompile() { ...@@ -157,11 +157,9 @@ void WontCompile() {
ref_arg_cb.Run(p); ref_arg_cb.Run(p);
} }
#elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM) // [r"fatal error: static_assert failed due to requirement 'param_is_forwardable' \"Bound argument |i| of type |Arg| cannot be forwarded as |Unwrapped| to the bound functor, which declares it as |Param|.\""] #elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kCanBeForwardedToBoundFunctor.+?Type mismatch between bound argument and bound functor's parameter\."]
// Binding functions with reference parameters, unsupported. // Binding functions with reference parameters requires `std::ref()`.
//
// See comment in NCTEST_DISALLOW_NON_CONST_REF_PARAM
void WontCompile() { void WontCompile() {
Parent p; Parent p;
RepeatingCallback<int()> ref_cb = BindRepeating(&UnwrapParentRef, p); RepeatingCallback<int()> ref_cb = BindRepeating(&UnwrapParentRef, p);
...@@ -287,21 +285,21 @@ void WontCompile() { ...@@ -287,21 +285,21 @@ void WontCompile() {
BindOnce(std::move(cb), 42); BindOnce(std::move(cb), 42);
} }
#elif defined(NCTEST_BINDONCE_MOVEONLY_TYPE_BY_VALUE) // [r"fatal error: static_assert failed due to requirement 'arg_is_storable || !std::is_constructible<std::__1::unique_ptr<int, std::__1::default_delete<int> >, std::__1::unique_ptr<int, std::__1::default_delete<int> > &&>::value' \"Bound argument |i| is move-only but will be bound by copy. Ensure |Arg| is mutable and bound using std::move\(\).\""] #elif defined(NCTEST_BINDONCE_MOVEONLY_TYPE_BY_VALUE) // [r"static_assert failed.+?BindArgument<0>::BoundAs<.+?>::StoredAs<.+?>::kMoveOnlyTypeMustUseStdMove.+?Attempting to bind a move-only type\. Use std::move\(\) to transfer ownership to the created callback\."]
void WontCompile() { void WontCompile() {
std::unique_ptr<int> x; std::unique_ptr<int> x;
BindOnce(&TakesMoveOnly, x); BindOnce(&TakesMoveOnly, x);
} }
#elif defined(NCTEST_BIND_MOVEONLY_TYPE_BY_VALUE) // [r"Bound argument \|i\| is move-only but will be forwarded by copy\. Ensure \|Arg\| is bound using base::Passed\(\), not std::move\(\)."] #elif defined(NCTEST_BIND_MOVEONLY_TYPE_BY_VALUE) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kMoveOnlyTypeMustUseBasePassed.+?base::BindRepeating\(\) argument is a move-only type\. Use base::Passed\(\) instead of std::move\(\) to transfer ownership from the callback to the bound functor\."]
void WontCompile() { void WontCompile() {
std::unique_ptr<int> x; std::unique_ptr<int> x;
BindRepeating(&TakesMoveOnly, x); BindRepeating(&TakesMoveOnly, x);
} }
#elif defined(NCTEST_BIND_MOVEONLY_TYPE_WITH_STDMOVE) // [r"Bound argument \|i\| is move-only but will be forwarded by copy\. Ensure \|Arg\| is bound using base::Passed\(\), not std::move\(\)."] #elif defined(NCTEST_BIND_MOVEONLY_TYPE_WITH_STDMOVE) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kMoveOnlyTypeMustUseBasePassed.+?base::BindRepeating\(\) argument is a move-only type\. Use base::Passed\(\) instead of std::move\(\) to transfer ownership from the callback to the bound functor\."]
void WontCompile() { void WontCompile() {
std::unique_ptr<int> x; std::unique_ptr<int> x;
...@@ -328,6 +326,20 @@ void WontCompile() { ...@@ -328,6 +326,20 @@ void WontCompile() {
const auto mutable_lambda = [&]() mutable {}; const auto mutable_lambda = [&]() mutable {};
BindLambdaForTesting(std::move(mutable_lambda)); BindLambdaForTesting(std::move(mutable_lambda));
} }
#elif defined(NCTEST_BIND_UNCOPYABLE_AND_UNMOVABLE_TYPE) // [r"static_assert failed.+?BindArgument<0>::BoundAs<.+?>::StoredAs<.+?>::kBindArgumentCanBeCaptured.+?Cannot capture argument: is the argument copyable or movable\?"]
void WontCompile() {
struct UncopyableUnmovable {
UncopyableUnmovable() = default;
UncopyableUnmovable(const UncopyableUnmovable&) = delete;
UncopyableUnmovable& operator=(const UncopyableUnmovable&) = delete;
};
UncopyableUnmovable u;
BindOnce([] (const UncopyableUnmovable&) {}, u);
}
#endif #endif
} // namespace base } // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment