pw_sync: Fixed InlineBorrowable constructor ambiguity

There is ambiguity about which constructor to invoke when constructing
an inline object that has a single construtor argument
using the in-place syntax with an lvalue parameter. Added enable_if
guards so that the factory-function constructors are only instantiated
for compatible callables.

Change-Id: I7db859bf4b4cf31491c10d1bd9105f34b11a133d
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/111290
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Anton Markov <amarkov@google.com>
diff --git a/pw_sync/inline_borrowable_test.cc b/pw_sync/inline_borrowable_test.cc
index a626027..4e55ac7 100644
--- a/pw_sync/inline_borrowable_test.cc
+++ b/pw_sync/inline_borrowable_test.cc
@@ -36,6 +36,7 @@
 // A custom type that is neither copyable nor movable.
 class CustomType {
  public:
+  explicit constexpr CustomType(int z) : x_(z), y_(-z) {}
   constexpr CustomType(int x, int y) : x_(x), y_(y) {}
 
   CustomType(const CustomType&) = delete;
@@ -79,7 +80,18 @@
   EXPECT_TRUE(trivial.acquire()->yes());
 }
 
-TEST(InlineBorrowableTest, TestCustomTypeInPlace) {
+TEST(InlineBorrowableTest, TestCustomTypeInPlace1Arg) {
+  InlineBorrowable<CustomType> custom(std::in_place, 1);
+  EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, -1));
+}
+
+TEST(InlineBorrowableTest, TestCustomTypeInPlace1ArgLValue) {
+  int x = 1;
+  InlineBorrowable<CustomType> custom(std::in_place, x);
+  EXPECT_EQ(custom.acquire()->data(), std::make_pair(x, -x));
+}
+
+TEST(InlineBorrowableTest, TestCustomTypeInPlace2Arg) {
   InlineBorrowable<CustomType> custom(std::in_place, 1, 2);
   EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
 }
@@ -94,6 +106,16 @@
   EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
 }
 
+TEST(InlineBorrowableTest, TestCustomTypeFromMutableFactory) {
+  int i = 0;
+  auto factory = [&i]() mutable {
+    i++;
+    return CustomType(1, 2);
+  };
+  InlineBorrowable<CustomType> custom(factory);
+  EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
+}
+
 TEST(InlineBorrowableTest, TestTrivialTypeWithInterruptSpinLock) {
   InlineBorrowable<TrivialType, VirtualInterruptSpinLock>
       trivial_interrupt_safe;
diff --git a/pw_sync/public/pw_sync/inline_borrowable.h b/pw_sync/public/pw_sync/inline_borrowable.h
index e9d0d93..0ee9fb5 100644
--- a/pw_sync/public/pw_sync/inline_borrowable.h
+++ b/pw_sync/public/pw_sync/inline_borrowable.h
@@ -91,27 +91,47 @@
   //        [&]{ return Foo{foo_arg1, foo_arg2}; }
   //        [&]{ return MyLock{lock_arg1, lock_arg2}; }
   //
-  template <typename ObjectConstructor, typename LockConstructor = Lock()>
+  template <typename ObjectConstructor,
+            typename LockConstructor = Lock(),
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<GuardedType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr explicit InlineBorrowable(
       const ObjectConstructor& object_ctor,
       const LockConstructor& lock_ctor = internal::DefaultConstruct<Lock>)
       : Storage(object_ctor, lock_ctor),
         Base(Storage::object_, Storage::lock_) {}
 
-  template <typename ObjectConstructor, typename LockConstructor = Lock()>
+  template <typename ObjectConstructor,
+            typename LockConstructor = Lock(),
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<GuardedType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr explicit InlineBorrowable(
       ObjectConstructor& object_ctor,
       const LockConstructor& lock_ctor = internal::DefaultConstruct<Lock>)
       : Storage(object_ctor, lock_ctor),
         Base(Storage::object_, Storage::lock_) {}
 
-  template <typename ObjectConstructor, typename LockConstructor = Lock()>
+  template <typename ObjectConstructor,
+            typename LockConstructor = Lock(),
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<GuardedType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr explicit InlineBorrowable(const ObjectConstructor& object_ctor,
                                       LockConstructor& lock_ctor)
       : Storage(object_ctor, lock_ctor),
         Base(Storage::object_, Storage::lock_) {}
 
-  template <typename ObjectConstructor, typename LockConstructor = Lock()>
+  template <typename ObjectConstructor,
+            typename LockConstructor = Lock(),
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<GuardedType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr explicit InlineBorrowable(ObjectConstructor& object_ctor,
                                       LockConstructor& lock_ctor)
       : Storage(object_ctor, lock_ctor),
diff --git a/pw_sync/public/pw_sync/internal/borrowable_storage.h b/pw_sync/public/pw_sync/internal/borrowable_storage.h
index d05965e..d13c94f 100644
--- a/pw_sync/public/pw_sync/internal/borrowable_storage.h
+++ b/pw_sync/public/pw_sync/internal/borrowable_storage.h
@@ -24,7 +24,7 @@
  protected:
   // Construct the object in-place using a list of arguments.
   template <typename... Args>
-  constexpr BorrowableStorage(std::in_place_t, Args&&... args)
+  constexpr explicit BorrowableStorage(std::in_place_t, Args&&... args)
       : object_{std::forward<Args>(args)...}, lock_ {}
   {}
 
@@ -38,22 +38,42 @@
             std::forward<std::tuple<LockArgs...>>(lock_args))} {}
 
   // Construct the object and lock in-place using the provided factories.
-  template <typename ObjectConstructor, typename LockConstructor>
+  template <typename ObjectConstructor,
+            typename LockConstructor,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<ObjectType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr BorrowableStorage(const ObjectConstructor& object_ctor,
                               const LockConstructor& lock_ctor)
       : object_{object_ctor()}, lock_{lock_ctor()} {}
 
-  template <typename ObjectConstructor, typename LockConstructor>
+  template <typename ObjectConstructor,
+            typename LockConstructor,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<ObjectType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr BorrowableStorage(ObjectConstructor& object_ctor,
                               const LockConstructor& lock_ctor)
       : object_{object_ctor()}, lock_{lock_ctor()} {}
 
-  template <typename ObjectConstructor, typename LockConstructor>
+  template <typename ObjectConstructor,
+            typename LockConstructor,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<ObjectType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr BorrowableStorage(const ObjectConstructor& object_ctor,
                               LockConstructor& lock_ctor)
       : object_{object_ctor()}, lock_{lock_ctor()} {}
 
-  template <typename ObjectConstructor, typename LockConstructor>
+  template <typename ObjectConstructor,
+            typename LockConstructor,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<ObjectType&&, ObjectConstructor>>,
+            typename = std::enable_if_t<
+                std::is_invocable_r_v<Lock&&, LockConstructor>>>
   constexpr BorrowableStorage(ObjectConstructor& object_ctor,
                               LockConstructor& lock_ctor)
       : object_{object_ctor()}, lock_{lock_ctor()} {}