-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc++][iterator][ranges] P2997R1: Removing the common reference requirement from the indirectly invocable concepts #98817
Conversation
… from the indirectly invocable concepts Implements: https://wg21.link/P2997R1
…ence-requirement-from-the-indirectly-invocable-concepts
✅ With the latest revision this PR passed the Python code formatter. |
…ence-requirement-from-the-indirectly-invocable-concepts
@llvm/pr-subscribers-libcxx Author: Hristo Hristov (H-G-Hristov) ChangesImplements as DR against C++20: https://wg21.link/P2997R1 Patch is 21.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/98817.diff 16 Files Affected:
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 7cd20acc6ff4a..82cfa76b744b7 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -452,6 +452,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_philox_engine`` *unimplemented*
---------------------------------------------------------- -----------------
+ ``__cpp_lib_ranges`` ``202406L``
+ ---------------------------------------------------------- -----------------
``__cpp_lib_ranges_concat`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_ratio`` ``202306L``
diff --git a/libcxx/include/__iterator/concepts.h b/libcxx/include/__iterator/concepts.h
index afb7b821a99ce..0a4878308d55f 100644
--- a/libcxx/include/__iterator/concepts.h
+++ b/libcxx/include/__iterator/concepts.h
@@ -177,19 +177,19 @@ concept __has_arrow = input_iterator<_Ip> && (is_pointer_v<_Ip> || requires(_Ip
template <class _Fp, class _It>
concept indirectly_unary_invocable =
indirectly_readable<_It> && copy_constructible<_Fp> && invocable<_Fp&, iter_value_t<_It>&> &&
- invocable<_Fp&, iter_reference_t<_It>> && invocable<_Fp&, iter_common_reference_t<_It>> &&
+ invocable<_Fp&, iter_reference_t<_It>> &&
common_reference_with< invoke_result_t<_Fp&, iter_value_t<_It>&>, invoke_result_t<_Fp&, iter_reference_t<_It>>>;
template <class _Fp, class _It>
concept indirectly_regular_unary_invocable =
indirectly_readable<_It> && copy_constructible<_Fp> && regular_invocable<_Fp&, iter_value_t<_It>&> &&
- regular_invocable<_Fp&, iter_reference_t<_It>> && regular_invocable<_Fp&, iter_common_reference_t<_It>> &&
+ regular_invocable<_Fp&, iter_reference_t<_It>> &&
common_reference_with< invoke_result_t<_Fp&, iter_value_t<_It>&>, invoke_result_t<_Fp&, iter_reference_t<_It>>>;
template <class _Fp, class _It>
concept indirect_unary_predicate =
indirectly_readable<_It> && copy_constructible<_Fp> && predicate<_Fp&, iter_value_t<_It>&> &&
- predicate<_Fp&, iter_reference_t<_It>> && predicate<_Fp&, iter_common_reference_t<_It>>;
+ predicate<_Fp&, iter_reference_t<_It>>;
template <class _Fp, class _It1, class _It2>
concept indirect_binary_predicate =
@@ -197,8 +197,7 @@ concept indirect_binary_predicate =
predicate<_Fp&, iter_value_t<_It1>&, iter_value_t<_It2>&> &&
predicate<_Fp&, iter_value_t<_It1>&, iter_reference_t<_It2>> &&
predicate<_Fp&, iter_reference_t<_It1>, iter_value_t<_It2>&> &&
- predicate<_Fp&, iter_reference_t<_It1>, iter_reference_t<_It2>> &&
- predicate<_Fp&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>;
+ predicate<_Fp&, iter_reference_t<_It1>, iter_reference_t<_It2>>;
template <class _Fp, class _It1, class _It2 = _It1>
concept indirect_equivalence_relation =
@@ -206,8 +205,7 @@ concept indirect_equivalence_relation =
equivalence_relation<_Fp&, iter_value_t<_It1>&, iter_value_t<_It2>&> &&
equivalence_relation<_Fp&, iter_value_t<_It1>&, iter_reference_t<_It2>> &&
equivalence_relation<_Fp&, iter_reference_t<_It1>, iter_value_t<_It2>&> &&
- equivalence_relation<_Fp&, iter_reference_t<_It1>, iter_reference_t<_It2>> &&
- equivalence_relation<_Fp&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>;
+ equivalence_relation<_Fp&, iter_reference_t<_It1>, iter_reference_t<_It2>>;
template <class _Fp, class _It1, class _It2 = _It1>
concept indirect_strict_weak_order =
@@ -215,8 +213,7 @@ concept indirect_strict_weak_order =
strict_weak_order<_Fp&, iter_value_t<_It1>&, iter_value_t<_It2>&> &&
strict_weak_order<_Fp&, iter_value_t<_It1>&, iter_reference_t<_It2>> &&
strict_weak_order<_Fp&, iter_reference_t<_It1>, iter_value_t<_It2>&> &&
- strict_weak_order<_Fp&, iter_reference_t<_It1>, iter_reference_t<_It2>> &&
- strict_weak_order<_Fp&, iter_common_reference_t<_It1>, iter_common_reference_t<_It2>>;
+ strict_weak_order<_Fp&, iter_reference_t<_It1>, iter_reference_t<_It2>>;
template <class _Fp, class... _Its>
requires(indirectly_readable<_Its> && ...) && invocable<_Fp, iter_reference_t<_Its>...>
diff --git a/libcxx/include/version b/libcxx/include/version
index 1f66bce40df8a..0c5cec87b7b01 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -181,8 +181,9 @@ __cpp_lib_philox_engine 202406L <random>
__cpp_lib_polymorphic_allocator 201902L <memory_resource>
__cpp_lib_print 202207L <ostream> <print>
__cpp_lib_quoted_string_io 201304L <iomanip>
-__cpp_lib_ranges 202207L <algorithm> <functional> <iterator>
+__cpp_lib_ranges 202406L <algorithm> <functional> <iterator>
<memory> <ranges>
+ 202207L // C++20
__cpp_lib_ranges_as_const 202207L <ranges>
__cpp_lib_ranges_as_rvalue 202207L <ranges>
__cpp_lib_ranges_chunk 202202L <ranges>
@@ -534,6 +535,8 @@ __cpp_lib_void_t 201411L <type_traits>
# undef __cpp_lib_out_ptr
// # define __cpp_lib_out_ptr 202311L
// # define __cpp_lib_philox_engine 202406L
+# undef __cpp_lib_ranges
+# define __cpp_lib_ranges 202406L
// # define __cpp_lib_ranges_concat 202403L
# define __cpp_lib_ratio 202306L
// # define __cpp_lib_rcu 202306L
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp
index e04c508852820..76f38232b4e04 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp
@@ -76,12 +76,12 @@ struct BadPredicate5 {
};
static_assert(!std::indirect_binary_predicate<BadPredicate5, It1, It2>);
-// Should fail when the predicate can't be called with (iter_common_reference_t, iter_common_reference_t)
+// This case was made valid by P2997R1.
struct BadPredicate6 {
template <class T, class U> bool operator()(T const&, U const&) const;
bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
};
-static_assert(!std::indirect_binary_predicate<BadPredicate6, It1, It2>);
+static_assert(std::indirect_binary_predicate<BadPredicate6, It1, It2>);
// Test ADL-proofing (P2538R1)
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp
index 56f52f28ff6fe..06a0291e439cc 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp
@@ -91,12 +91,12 @@ struct BadRelation5 {
};
static_assert(!std::indirect_equivalence_relation<BadRelation5, It1, It2>);
-// Should fail when the function can't be called with (iter_common_reference_t, iter_common_reference_t)
+// This case was made valid by P2997R1.
struct BadRelation6 {
template <class T, class U> bool operator()(T const&, U const&) const;
bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
};
-static_assert(!std::indirect_equivalence_relation<BadRelation6, It1, It2>);
+static_assert(std::indirect_equivalence_relation<BadRelation6, It1, It2>);
// Test ADL-proofing (P2538R1)
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp
index f36431c89f288..9ca335a98c550 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp
@@ -91,12 +91,12 @@ struct BadOrder5 {
};
static_assert(!std::indirect_strict_weak_order<BadOrder5, It1, It2>);
-// Should fail when the function can't be called with (iter_common_reference_t, iter_common_reference_t)
+// This case was made valid by P2997R1.
struct BadOrder6 {
template <class T, class U> bool operator()(T const&, U const&) const;
bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
};
-static_assert(!std::indirect_strict_weak_order<BadOrder6, It1, It2>);
+static_assert(std::indirect_strict_weak_order<BadOrder6, It1, It2>);
// Test ADL-proofing (P2538R1)
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp
index 4551113af82e8..870f82cc8c3c0 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp
@@ -57,12 +57,12 @@ struct BadPredicate3 {
};
static_assert(!std::indirect_unary_predicate<BadPredicate3, It>);
-// Should fail when the predicate can't be called with std::iter_common_reference_t<It>
+// This case was made valid by P2997R1.
struct BadPredicate4 {
template <class T> bool operator()(T const&) const;
bool operator()(std::iter_common_reference_t<It>) const = delete;
};
-static_assert(!std::indirect_unary_predicate<BadPredicate4, It>);
+static_assert(std::indirect_unary_predicate<BadPredicate4, It>);
// Test ADL-proofing (P2538R1)
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp
index 9e0c3c99dee98..5ebbba6e24861 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp
@@ -56,12 +56,12 @@ struct BadInvocable3 {
};
static_assert(!std::indirectly_regular_unary_invocable<BadInvocable3, It>);
-// Should fail when the invocable can't be called with (iter_common_reference_t)
+// This case was made valid by P2997R1.
struct BadInvocable4 {
template <class T> R1 operator()(T const&) const;
R1 operator()(std::iter_common_reference_t<It>) const = delete;
};
-static_assert(!std::indirectly_regular_unary_invocable<BadInvocable4, It>);
+static_assert(std::indirectly_regular_unary_invocable<BadInvocable4, It>);
// Should fail when the invocable doesn't have a common reference between its return types
struct BadInvocable5 {
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
index fc955eb88585a..d6a26f50eb748 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
@@ -56,12 +56,12 @@ struct BadInvocable3 {
};
static_assert(!std::indirectly_unary_invocable<BadInvocable3, It>);
-// Should fail when the invocable can't be called with (iter_common_reference_t)
+// This case was made valid by P2997R1.
struct BadInvocable4 {
template <class T> R1 operator()(T const&) const;
R1 operator()(std::iter_common_reference_t<It>) const = delete;
};
-static_assert(!std::indirectly_unary_invocable<BadInvocable4, It>);
+static_assert(std::indirectly_unary_invocable<BadInvocable4, It>);
// Should fail when the invocable doesn't have a common reference between its return types
struct BadInvocable5 {
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
index 8ccd252115ac8..f9be4ef6ec1f6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp
@@ -22,6 +22,7 @@
__cpp_lib_freestanding_algorithm 202311L [C++26]
__cpp_lib_parallel_algorithm 201603L [C++17]
__cpp_lib_ranges 202207L [C++20]
+ 202406L [C++26]
__cpp_lib_ranges_contains 202207L [C++23]
__cpp_lib_ranges_starts_ends_with 202106L [C++23]
__cpp_lib_robust_nonmodifying_seq_ops 201304L [C++14]
@@ -401,8 +402,8 @@
# ifndef __cpp_lib_ranges
# error "__cpp_lib_ranges should be defined in c++26"
# endif
-# if __cpp_lib_ranges != 202207L
-# error "__cpp_lib_ranges should have the value 202207L in c++26"
+# if __cpp_lib_ranges != 202406L
+# error "__cpp_lib_ranges should have the value 202406L in c++26"
# endif
# ifndef __cpp_lib_ranges_contains
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index 27e76e5b2b05a..e4eb94aad60c5 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -28,6 +28,7 @@
__cpp_lib_move_only_function 202110L [C++23]
__cpp_lib_not_fn 201603L [C++17]
__cpp_lib_ranges 202207L [C++20]
+ 202406L [C++26]
__cpp_lib_reference_wrapper 202403L [C++26]
__cpp_lib_result_of_sfinae 201210L [C++14]
__cpp_lib_transparent_operators 201210L [C++14]
@@ -531,8 +532,8 @@
# ifndef __cpp_lib_ranges
# error "__cpp_lib_ranges should be defined in c++26"
# endif
-# if __cpp_lib_ranges != 202207L
-# error "__cpp_lib_ranges should have the value 202207L in c++26"
+# if __cpp_lib_ranges != 202406L
+# error "__cpp_lib_ranges should have the value 202406L in c++26"
# endif
# ifndef __cpp_lib_reference_wrapper
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
index 700907ce9bb07..4e29f3db51c3b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp
@@ -24,6 +24,7 @@
__cpp_lib_nonmember_container_access 201411L [C++17]
__cpp_lib_null_iterators 201304L [C++14]
__cpp_lib_ranges 202207L [C++20]
+ 202406L [C++26]
__cpp_lib_ssize 201902L [C++20]
*/
@@ -313,8 +314,8 @@
# ifndef __cpp_lib_ranges
# error "__cpp_lib_ranges should be defined in c++26"
# endif
-# if __cpp_lib_ranges != 202207L
-# error "__cpp_lib_ranges should have the value 202207L in c++26"
+# if __cpp_lib_ranges != 202406L
+# error "__cpp_lib_ranges should have the value 202406L in c++26"
# endif
# ifndef __cpp_lib_ssize
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index 45d9271faa578..e44474a4c0aff 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -29,6 +29,7 @@
__cpp_lib_out_ptr 202106L [C++23]
202311L [C++26]
__cpp_lib_ranges 202207L [C++20]
+ 202406L [C++26]
__cpp_lib_raw_memory_algorithms 201606L [C++17]
__cpp_lib_shared_ptr_arrays 201611L [C++17]
201707L [C++20]
@@ -638,8 +639,8 @@
# ifndef __cpp_lib_ranges
# error "__cpp_lib_ranges should be defined in c++26"
# endif
-# if __cpp_lib_ranges != 202207L
-# error "__cpp_lib_ranges should have the value 202207L in c++26"
+# if __cpp_lib_ranges != 202406L
+# error "__cpp_lib_ranges should have the value 202406L in c++26"
# endif
# ifndef __cpp_lib_raw_memory_algorithms
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 30feacd796d8e..6c558eafbc333 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -18,6 +18,7 @@
/* Constant Value
__cpp_lib_default_template_type_for_algorithm_values 202403L [C++26]
__cpp_lib_ranges 202207L [C++20]
+ 202406L [C++26]
__cpp_lib_ranges_as_const 202207L [C++23]
__cpp_lib_ranges_as_rvalue 202207L [C++23]
__cpp_lib_ranges_chunk 202202L [C++23]
@@ -364,8 +365,8 @@
# ifndef __cpp_lib_ranges
# error "__cpp_lib_ranges should be defined in c++26"
# endif
-# if __cpp_lib_ranges != 202207L
-# error "__cpp_lib_ranges should have the value 202207L in c++26"
+# if __cpp_lib_ranges != 202406L
+# error "__cpp_lib_ranges should have the value 202406L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index aa5ff80afb56a..a39425c968e3e 100644
--- a/libcxx/test/st...
[truncated]
|
…ence-requirement-from-the-indirectly-invocable-concepts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jwakely @StephanTLavavej Did you folks also implement this as a DR? It certainly makes sense to me -- I'm generally against extensions but having a different behavior in C++20 / C++26 seems really confusing for no reason. WDYT?
....requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
Outdated
Show resolved
Hide resolved
I believe @frederick-vs-ja implemented P2997R1 in MS STL. |
When it says so in the wording of the straw poll that WG21 takes. But in some cases, like this one, implementers agree to do it despite it not being voted that way. @ldionne I haven't thought about it yet, but we'll follow the herd if libc++ and MSVC do it. |
…ence-requirement-from-the-indirectly-invocable-concepts
✅ With the latest revision this PR passed the C/C++ code formatter. |
…ence-requirement-from-the-indirectly-invocable-concepts
Yep, we implemented this one as a de facto DR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks.
@jwakely Per discussion in the thread, libc++ will do this one as a DR against C++20.
…uirement from the indirectly invocable concepts (llvm#98817) Implements as DR against C++20: https://wg21.link/P2997R1 References: - https://eel.is/c++draft/indirectcallable.indirectinvocable - https://eel.is/c++draft/version.syn#header:%3cversion%3e --------- Co-authored-by: Hristo Hristov <[email protected]>
…uirement from the indirectly invocable concepts (llvm#98817) Implements as DR against C++20: https://wg21.link/P2997R1 References: - https://eel.is/c++draft/indirectcallable.indirectinvocable - https://eel.is/c++draft/version.syn#header:%3cversion%3e --------- Co-authored-by: Hristo Hristov <[email protected]>
…uirement from the indirectly invocable concepts (#98817) Summary: Implements as DR against C++20: https://wg21.link/P2997R1 References: - https://eel.is/c++draft/indirectcallable.indirectinvocable - https://eel.is/c++draft/version.syn#header:%3cversion%3e --------- Co-authored-by: Hristo Hristov <[email protected]> Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60250909
Implements as DR against C++20: https://wg21.link/P2997R1
References: