Add feature tests for C++20 constexpr and consteval

This restores support for building pw_rpc with xtensa-esp32-elf-gcc
(crosstool-NG esp-2020r3) 8.4.0.

No-Docs-Update-Reason: bug fix
Change-Id: Ia8af09844855f130e866c2c42f18230da7962118
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/48701
Commit-Queue: Michael Spang <spang@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/pw_bytes/public/pw_bytes/array.h b/pw_bytes/public/pw_bytes/array.h
index 453f9ea..5b81229 100644
--- a/pw_bytes/public/pw_bytes/array.h
+++ b/pw_bytes/public/pw_bytes/array.h
@@ -20,6 +20,8 @@
 #include <cstddef>
 #include <iterator>
 
+#include "pw_polyfill/language_feature_macros.h"
+
 namespace pw::bytes {
 namespace internal {
 
@@ -30,7 +32,7 @@
 // byte-sized elements or the underlying bytes of an integer (as little-endian).
 // std::memcpy cannot be used since it is not constexpr.
 template <typename B, typename T, typename... Args>
-consteval void CopyBytes(B* array, T value, Args... args) {
+PW_CONSTEVAL void CopyBytes(B* array, T value, Args... args) {
   static_assert(sizeof(B) == sizeof(std::byte));
 
   if constexpr (UseBytesDirectly<T>) {
@@ -56,7 +58,7 @@
 
 // Evaluates to the size in bytes of an integer or byte array.
 template <typename T>
-consteval size_t SizeOfBytes(const T& arg) {
+PW_CONSTEVAL size_t SizeOfBytes(const T& arg) {
   if constexpr (UseBytesDirectly<T>) {
     return sizeof(arg);
   } else {
@@ -66,12 +68,12 @@
 }
 
 template <typename B, typename T, size_t... kIndex>
-consteval auto String(const T& array, std::index_sequence<kIndex...>) {
+PW_CONSTEVAL auto String(const T& array, std::index_sequence<kIndex...>) {
   return std::array{static_cast<B>(array[kIndex])...};
 }
 
 template <typename T, typename U>
-consteval bool CanBeRepresentedAsByteType(const U& value) {
+PW_CONSTEVAL bool CanBeRepresentedAsByteType(const U& value) {
   return static_cast<U>(static_cast<T>(value)) == value;
 }
 
@@ -80,7 +82,7 @@
 // Concatenates arrays or integers as a byte array at compile time. Integer
 // values are copied little-endian. Spans are copied byte-for-byte.
 template <typename B = std::byte, typename... Args>
-consteval auto Concat(Args... args) {
+PW_CONSTEVAL auto Concat(Args... args) {
   std::array<B, (internal::SizeOfBytes(args) + ...)> bytes{};
   internal::CopyBytes(bytes.begin(), args...);
   return bytes;
@@ -90,27 +92,27 @@
 template <typename B = std::byte,
           size_t kSize,
           typename Indices = std::make_index_sequence<kSize - 1>>
-consteval auto String(const char (&str)[kSize]) {
+PW_CONSTEVAL auto String(const char (&str)[kSize]) {
   return internal::String<B>(str, Indices{});
 }
 
 // String overload for the empty string "".
 template <typename B = std::byte>
-consteval auto String(const char (&)[1]) {
+PW_CONSTEVAL auto String(const char (&)[1]) {
   return std::array<B, 0>{};
 }
 
 // Creates an array of bytes from values passed as template parameters. The
 // values are guaranteed to be representable in the destination byte type.
 template <typename B, auto... values>
-consteval auto Array() {
+PW_CONSTEVAL auto Array() {
   static_assert((internal::CanBeRepresentedAsByteType<B>(values) && ...));
   return std::array<B, sizeof...(values)>{static_cast<B>(values)...};
 }
 
 // Array() defaults to using std::byte.
 template <auto... values>
-consteval auto Array() {
+PW_CONSTEVAL auto Array() {
   return Array<std::byte, values...>();
 }
 
diff --git a/pw_polyfill/public/pw_polyfill/language_feature_macros.h b/pw_polyfill/public/pw_polyfill/language_feature_macros.h
index 389493f..60f290c 100644
--- a/pw_polyfill/public/pw_polyfill/language_feature_macros.h
+++ b/pw_polyfill/public/pw_polyfill/language_feature_macros.h
@@ -29,3 +29,17 @@
 #else
 #define PW_CONSTEXPR_FUNCTION
 #endif  // __cpp_constexpr >= 201304L
+
+// Mark functions as constexpr if C++20 or newer
+#if __cplusplus >= 202002L
+#define PW_CONSTEXPR_CPP20 constexpr
+#else
+#define PW_CONSTEXPR_CPP20
+#endif  // __cpp_constexpr >= 201304L
+
+// Mark functions as consteval if supported.
+#if defined(__cpp_consteval) && __cpp_consteval >= 201811L
+#define PW_CONSTEVAL consteval
+#else
+#define PW_CONSTEVAL PW_CONSTEXPR_FUNCTION
+#endif  // __cpp_consteval >= 201811L
diff --git a/pw_string/public/pw_string/util.h b/pw_string/public/pw_string/util.h
index 6b58308..0b81867 100644
--- a/pw_string/public/pw_string/util.h
+++ b/pw_string/public/pw_string/util.h
@@ -73,8 +73,8 @@
 //
 // Precondition: The destination and source shall not overlap.
 // Precondition: The source shall be a valid pointer.
-constexpr StatusWithSize Copy(const std::string_view& source,
-                              std::span<char> dest) {
+PW_CONSTEXPR_CPP20 inline StatusWithSize Copy(const std::string_view& source,
+                                              std::span<char> dest) {
   if (dest.empty()) {
     return StatusWithSize::ResourceExhausted();
   }
@@ -87,12 +87,15 @@
       copied);
 }
 
-constexpr StatusWithSize Copy(const char* source, std::span<char> dest) {
+PW_CONSTEXPR_CPP20 inline StatusWithSize Copy(const char* source,
+                                              std::span<char> dest) {
   PW_DASSERT(source != nullptr);
   return Copy(ClampedCString(source, dest.size()), dest);
 }
 
-constexpr StatusWithSize Copy(const char* source, char* dest, size_t num) {
+PW_CONSTEXPR_CPP20 inline StatusWithSize Copy(const char* source,
+                                              char* dest,
+                                              size_t num) {
   return Copy(source, std::span<char>(dest, num));
 }