`absl/meta`: Add C++17 port of C++20 `requires` expression for internal use

PiperOrigin-RevId: 846259270
Change-Id: I0126a78949b15fbbafca7c8dba3e3b60df30914b
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index cc9f792..b419f1b 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -213,6 +213,7 @@
   "log/vlog_is_on.h"
   "memory/memory.h"
   "meta/type_traits.h"
+  "meta/internal/requires.h"
   "numeric/bits.h"
   "numeric/int128.cc"
   "numeric/int128.h"
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index f6efa42..6036895 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -35,6 +35,29 @@
 licenses(["notice"])
 
 cc_library(
+    name = "requires",
+    hdrs = ["internal/requires.h"],
+    copts = ABSL_DEFAULT_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    visibility = ["//absl:__subpackages__"],
+    deps = [
+        "//absl/base:config",
+    ],
+)
+
+cc_test(
+    name = "requires_test",
+    srcs = ["internal/requires_test.cc"],
+    copts = ABSL_TEST_COPTS,
+    linkopts = ABSL_DEFAULT_LINKOPTS,
+    deps = [
+        ":requires",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
+
+cc_library(
     name = "type_traits",
     hdrs = ["type_traits.h"],
     copts = ABSL_DEFAULT_COPTS,
diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt
index d509114..20650cc 100644
--- a/absl/meta/CMakeLists.txt
+++ b/absl/meta/CMakeLists.txt
@@ -16,6 +16,29 @@
 
 absl_cc_library(
   NAME
+    requires_internal
+  HDRS
+    "internal/requires.h"
+  COPTS
+    ${ABSL_DEFAULT_COPTS}
+  DEPS
+    absl::config
+)
+
+absl_cc_test(
+  NAME
+    requires_test
+  SRCS
+    "internal/requires_test.cc"
+  COPTS
+    ${ABSL_TEST_COPTS}
+  DEPS
+    absl::requires_internal
+    GTest::gmock_main
+)
+
+absl_cc_library(
+  NAME
     type_traits
   HDRS
     "type_traits.h"
diff --git a/absl/meta/internal/requires.h b/absl/meta/internal/requires.h
new file mode 100644
index 0000000..2166e99
--- /dev/null
+++ b/absl/meta/internal/requires.h
@@ -0,0 +1,67 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_META_INTERNAL_REQUIRES_H_
+#define ABSL_META_INTERNAL_REQUIRES_H_
+
+#include <type_traits>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace meta_internal {
+
+// C++17 port of the C++20 `requires` expressions.
+// It allows easy inline test of properties of types in template code.
+// https://en.cppreference.com/w/cpp/language/constraints#Requires_expressions
+//
+// Example usage:
+//
+// if constexpr (Requires<T>([](auto&& x) -> decltype(x.foo()) {})) {
+//   // T has foo()
+//   return t.foo();
+// } else if constexpr (Requires<T>([](auto&& x) -> decltype(Bar(x)) {})) {
+//   // Can call Bar with T
+//   return Bar(t);
+// } else if constexpr (Requires<T, U>(
+//     // Can test expression with multiple inputs
+//     [](auto&& x, auto&& y) -> decltype(x + y) {})) {
+//   return t + t2;
+// }
+//
+// The `Requires` function takes a list of types and a generic lambda where all
+// arguments are of type `auto&&`. The lambda is never actually invoked and the
+// body must be empty.
+// When used this way, `Requires` returns whether the expression inside
+// `decltype` is well-formed, when the lambda parameters have the types that
+// are specified by the corresponding template arguments.
+//
+// NOTE: C++17 does not allow lambdas in template parameters, which means that
+// code like the following is _not_ valid in C++17:
+//
+//  template <typename T,
+//            typename = std::enable_if_t<gtl::Requires<T>(
+//              [] (auto&& v) -> decltype(<expr>) {})>>
+//
+template <typename... T, typename F>
+constexpr bool Requires(F) {
+  return std::is_invocable_v<F, T...>;
+}
+
+}  // namespace meta_internal
+ABSL_NAMESPACE_END
+}  // namespace absl
+
+#endif  // ABSL_META_INTERNAL_REQUIRES_H_
diff --git a/absl/meta/internal/requires_test.cc b/absl/meta/internal/requires_test.cc
new file mode 100644
index 0000000..046d8f3
--- /dev/null
+++ b/absl/meta/internal/requires_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/meta/internal/requires.h"
+
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(RequiresTest, SimpleLambdasWork) {
+  static_assert(absl::meta_internal::Requires([] {}));
+  static_assert(absl::meta_internal::Requires<int>([](auto&&) {}));
+  static_assert(
+      absl::meta_internal::Requires<int, char>([](auto&&, auto&&) {}));
+}
+
+template <typename T>
+inline constexpr bool has_cstr =
+    absl::meta_internal::Requires<T>([](auto&& x) -> decltype(x.c_str()) {});
+
+template <typename T, typename U>
+inline constexpr bool have_plus = absl::meta_internal::Requires<T, U>(
+    [](auto&& x, auto&& y) -> decltype(x + y) {});
+
+TEST(RequiresTest, CanTestProperties) {
+  static_assert(has_cstr<std::string>);
+  static_assert(!has_cstr<std::vector<int>>);
+
+  static_assert(have_plus<int, double>);
+  static_assert(have_plus<std::string, std::string>);
+  static_assert(!have_plus<std::string, double>);
+}
+
+TEST(RequiresTest, WorksWithUnmovableTypes) {
+  struct S {
+    S(const S&) = delete;
+    int foo() { return 0; }
+  };
+  static_assert(
+      absl::meta_internal::Requires<S>([](auto&& x) -> decltype(x.foo()) {}));
+  static_assert(
+      !absl::meta_internal::Requires<S>([](auto&& x) -> decltype(x.bar()) {}));
+}
+
+TEST(RequiresTest, WorksWithArrays) {
+  static_assert(
+      absl::meta_internal::Requires<int[2]>([](auto&& x) -> decltype(x[1]) {}));
+  static_assert(
+      !absl::meta_internal::Requires<int[2]>([](auto&& x) -> decltype(-x) {}));
+}
+
+}  // namespace