pw_function: Make constexpr constructible

This makes pw::Function usable in classes with constexpr constructors
and makes it compatible with constinit.

Change-Id: Iba20a6e2973f2488ab767bf0aafa99bf47f5ec2e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/50361
Commit-Queue: Wyatt Hepler <hepler@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/pw_function/BUILD.gn b/pw_function/BUILD.gn
index 82009b1..17befd1 100644
--- a/pw_function/BUILD.gn
+++ b/pw_function/BUILD.gn
@@ -63,7 +63,10 @@
 }
 
 pw_test("function_test") {
-  deps = [ ":pw_function" ]
+  deps = [
+    ":pw_function",
+    dir_pw_polyfill,
+  ]
   sources = [ "function_test.cc" ]
 }
 
diff --git a/pw_function/docs.rst b/pw_function/docs.rst
index 8a2cf76..a2cb0c5 100644
--- a/pw_function/docs.rst
+++ b/pw_function/docs.rst
@@ -54,6 +54,23 @@
     function();
   }
 
+``pw::Function``'s default constructor is ``constexpr``, so default-constructed
+functions may be used in classes with ``constexpr`` constructors and in
+``constinit`` expressions.
+
+.. code-block:: c++
+
+  class MyClass {
+   public:
+    // Default construction of a pw::Function is constexpr.
+    constexpr MyClass() { ... }
+
+    pw::Function<void(int)> my_function;
+  };
+
+  // pw::Function and classes that use it may be constant initialized.
+  constinit MyClass instance;
+
 Storage
 -------
 By default, a ``Function`` stores its callable inline within the object. The
diff --git a/pw_function/function_test.cc b/pw_function/function_test.cc
index f76181c..fcc222d 100644
--- a/pw_function/function_test.cc
+++ b/pw_function/function_test.cc
@@ -15,10 +15,14 @@
 #include "pw_function/function.h"
 
 #include "gtest/gtest.h"
+#include "pw_polyfill/language_feature_macros.h"
 
 namespace pw {
 namespace {
 
+// Ensure that Function can be constant initialized.
+[[maybe_unused]] PW_CONSTINIT Function<void()> can_be_constant_initialized;
+
 int Multiply(int a, int b) { return a * b; }
 
 TEST(Function, OperatorCall) {
diff --git a/pw_function/public/pw_function/internal/function.h b/pw_function/public/pw_function/internal/function.h
index 1a28c3b..f6062aa 100644
--- a/pw_function/public/pw_function/internal/function.h
+++ b/pw_function/public/pw_function/internal/function.h
@@ -54,14 +54,15 @@
 template <typename Return, typename... Args>
 class FunctionTarget {
  public:
-  FunctionTarget() = default;
-  virtual ~FunctionTarget() = default;
+  constexpr FunctionTarget() = default;
 
   FunctionTarget(const FunctionTarget&) = delete;
   FunctionTarget(FunctionTarget&&) = delete;
   FunctionTarget& operator=(const FunctionTarget&) = delete;
   FunctionTarget& operator=(FunctionTarget&&) = delete;
 
+  virtual void Destroy() {}
+
   virtual bool IsNull() const = 0;
 
   // Invoke the callable stored by the function target.
@@ -69,6 +70,9 @@
 
   // Move initialize the function target to a provided location.
   virtual void MoveInitializeTo(void* ptr) = 0;
+
+ protected:
+  ~FunctionTarget() = default;  // The destructor is never called.
 };
 
 // A function target that does not store any callable. Attempting to invoke it
@@ -76,8 +80,7 @@
 template <typename Return, typename... Args>
 class NullFunctionTarget final : public FunctionTarget<Return, Args...> {
  public:
-  NullFunctionTarget() = default;
-  ~NullFunctionTarget() final = default;
+  constexpr NullFunctionTarget() = default;
 
   NullFunctionTarget(const NullFunctionTarget&) = delete;
   NullFunctionTarget(NullFunctionTarget&&) = delete;
@@ -98,7 +101,7 @@
   explicit InlineFunctionTarget(Callable&& callable)
       : callable_(std::move(callable)) {}
 
-  ~InlineFunctionTarget() final = default;
+  void Destroy() final { callable_.~Callable(); }
 
   InlineFunctionTarget(const InlineFunctionTarget&) = delete;
   InlineFunctionTarget& operator=(const InlineFunctionTarget&) = delete;
@@ -131,7 +134,7 @@
     new (address_) Callable(std::move(callable));
   }
 
-  ~MemoryFunctionTarget() final {
+  void Destroy() final {
     // Multiple MemoryFunctionTargets may have referred to the same callable
     // (due to moves), but only one can have a valid pointer to it. The owner is
     // responsible for destructing the callable.
@@ -182,7 +185,7 @@
 template <size_t kSizeBytes, typename Return, typename... Args>
 class FunctionTargetHolder {
  public:
-  FunctionTargetHolder() = default;
+  constexpr FunctionTargetHolder() : null_function_{} {}
 
   FunctionTargetHolder(const FunctionTargetHolder&) = delete;
   FunctionTargetHolder(FunctionTargetHolder&&) = delete;
@@ -193,7 +196,7 @@
     using NullFunctionTarget = NullFunctionTarget<Return, Args...>;
     static_assert(sizeof(NullFunctionTarget) <= kSizeBytes,
                   "NullFunctionTarget must fit within FunctionTargetHolder");
-    new (&bits_) NullFunctionTarget;
+    new (&null_function_) NullFunctionTarget;
   }
 
   // Initializes an InlineFunctionTarget with the callable, failing if it is too
@@ -218,7 +221,7 @@
     new (&bits_) MemoryFunctionTarget(storage, std::move(callable));
   }
 
-  void DestructTarget() { target().~Target(); }
+  void DestructTarget() { target().Destroy(); }
 
   // Initializes the function target within this callable from another target
   // holder's function target.
@@ -234,8 +237,13 @@
   }
 
  private:
-  // Storage for an implementation of the FunctionTarget interface.
-  FunctionStorage<kSizeBytes> bits_;
+  // Storage for an implementation of the FunctionTarget interface. Make this a
+  // union with NullFunctionTarget so that the constexpr constructor can
+  // initialize null_function_ directly.
+  union {
+    FunctionStorage<kSizeBytes> bits_;
+    NullFunctionTarget<Return, Args...> null_function_;
+  };
 };
 
 template <typename Return, typename... Args>
@@ -244,7 +252,7 @@
 template <typename Return, typename... Args>
 class Function<Return(Args...)> {
  public:
-  constexpr Function() { holder_.InitializeNullTarget(); }
+  constexpr Function() = default;
   constexpr Function(std::nullptr_t) : Function() {}
 
   template <typename Callable>