public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v2 0/8] Optimize more type traits
@ 2023-12-23 22:02 Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 1/8] c++: Implement __is_const built-in trait Ken Matsui
                   ` (8 more replies)
  0 siblings, 9 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch series implements __is_const, __is_volatile, __is_pointer,
and __is_unbounded_array built-in traits, which were isolated from my
previous patch series "Optimize type traits compilation performance"
because they contained performance regression.  I confirmed that this
patch series does not cause any performance regression.  The main reason
of the performance regression were the exhaustiveness of the benchmarks
and the instability of the benchmark results.  Here are new benchmark
results:

is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023

time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%

is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023

time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%

is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023

time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%

is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023

time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%

Changes in v2:

- Removed testsuite_tr1.h includes from the testcases.

Ken Matsui (8):
  c++: Implement __is_const built-in trait
  libstdc++: Optimize std::is_const compilation performance
  c++: Implement __is_volatile built-in trait
  libstdc++: Optimize std::is_volatile compilation performance
  c++: Implement __is_pointer built-in trait
  libstdc++: Optimize std::is_pointer compilation performance
  c++: Implement __is_unbounded_array built-in trait
  libstdc++: Optimize std::is_unbounded_array compilation performance

 gcc/cp/constraint.cc                          | 12 +++
 gcc/cp/cp-trait.def                           |  4 +
 gcc/cp/semantics.cc                           | 16 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      | 12 +++
 gcc/testsuite/g++.dg/ext/is_const.C           | 20 +++++
 gcc/testsuite/g++.dg/ext/is_pointer.C         | 51 +++++++++++++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 ++++++++++
 gcc/testsuite/g++.dg/ext/is_volatile.C        | 20 +++++
 libstdc++-v3/include/bits/cpp_type_traits.h   | 29 ++++++++
 libstdc++-v3/include/std/type_traits          | 73 +++++++++++++++++--
 10 files changed, 266 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 1/8] c++: Implement __is_const built-in trait
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 2/8] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index eeacead52a5..f1b07aa2853 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3734,6 +3734,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index e6dba29ee81..364d87ee34d 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12415,6 +12415,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12657,6 +12660,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 2/8] libstdc++: Optimize std::is_const compilation performance
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 1/8] c++: Implement __is_const built-in trait Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 3/8] c++: Implement __is_volatile built-in trait Ken Matsui
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index f00c07f94f9..f40831de838 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3315,10 +3322,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 3/8] c++: Implement __is_volatile built-in trait
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 1/8] c++: Implement __is_const built-in trait Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 2/8] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 4/8] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index f1b07aa2853..d9ba62d443a 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 364d87ee34d..c7971c969ae 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12501,6 +12501,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12671,6 +12674,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 4/8] libstdc++: Optimize std::is_volatile compilation performance
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
                   ` (2 preceding siblings ...)
  2023-12-23 22:02 ` [PATCH v2 3/8] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 5/8] c++: Implement __is_pointer built-in trait Ken Matsui
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index f40831de838..30b0778e58a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3344,10 +3351,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 5/8] c++: Implement __is_pointer built-in trait
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
                   ` (3 preceding siblings ...)
  2023-12-23 22:02 ` [PATCH v2 4/8] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d9ba62d443a..44bf8dc5c5b 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3795,6 +3795,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c7971c969ae..01a7ccc5225 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12471,6 +12471,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12670,6 +12673,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..96b7a89e4f1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -125,6 +125,9 @@
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
                   ` (4 preceding siblings ...)
  2023-12-23 22:02 ` [PATCH v2 5/8] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2024-01-04 22:06   ` Patrick Palka
  2024-01-04 22:13   ` Jonathan Wakely
  2023-12-23 22:02 ` [PATCH v2 7/8] c++: Implement __is_unbounded_array built-in trait Ken Matsui
                   ` (2 subsequent siblings)
  8 siblings, 2 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 29 ++++++++++++++
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 4312f32a4e0..c348df97f72 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 30b0778e58a..d53911b2fa0 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 7/8] c++: Implement __is_unbounded_array built-in trait
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
                   ` (5 preceding siblings ...)
  2023-12-23 22:02 ` [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2023-12-23 22:02 ` [PATCH v2 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 44bf8dc5c5b..defb2ac0ee8 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..05514a51c21 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 01a7ccc5225..176eda629ff 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12501,6 +12501,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12677,6 +12680,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 96b7a89e4f1..b1430e9bd8b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_trivially_copyable)
 # error "__has_builtin (__is_trivially_copyable) failed"
 #endif
+#if !__has_builtin (__is_unbounded_array)
+# error "__has_builtin (__is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..283a74e1a0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v2 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
                   ` (6 preceding siblings ...)
  2023-12-23 22:02 ` [PATCH v2 7/8] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2023-12-23 22:02 ` Ken Matsui
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
  8 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2023-12-23 22:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__is_unbounded_array built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__is_unbounded_array built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index d53911b2fa0..a548982236d 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3670,11 +3670,16 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v = __is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2023-12-23 22:02 ` [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-01-04 22:06   ` Patrick Palka
  2024-01-04 22:16     ` Patrick Palka
  2024-01-04 22:13   ` Jonathan Wakely
  1 sibling, 1 reply; 244+ messages in thread
From: Patrick Palka @ 2024-01-04 22:06 UTC (permalink / raw)
  To: Ken Matsui; +Cc: gcc-patches, libstdc++, Jonathan Wakely

On Sat, 23 Dec 2023, Ken Matsui wrote:

> This patch optimizes the compilation performance of std::is_pointer
> by dispatching to the new __is_pointer built-in trait.
> 
> libstdc++-v3/ChangeLog:
> 
> 	* include/bits/cpp_type_traits.h (__is_pointer): Use
> 	__is_pointer built-in trait.  Optimize its implementation.
> 	* include/std/type_traits (is_pointer): Likewise.
> 	(is_pointer_v): Likewise.
> 
> Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>  libstdc++-v3/include/bits/cpp_type_traits.h | 29 ++++++++++++++
>  libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
>  2 files changed, 65 insertions(+), 8 deletions(-)
> 
> diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
> index 4312f32a4e0..c348df97f72 100644
> --- a/libstdc++-v3/include/bits/cpp_type_traits.h
> +++ b/libstdc++-v3/include/bits/cpp_type_traits.h
> @@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
>    //
>    // Pointer types
>    //
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> +  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
> +    struct __is_pointer : __truth_type<_IsPtr>
> +    {
> +      enum { __value = _IsPtr };
> +    };
> +#else
>    template<typename _Tp>
>      struct __is_pointer
>      {
> @@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
>        typedef __true_type __type;
>      };
>  
> +  template<typename _Tp>
> +    struct __is_pointer<_Tp* const>
> +    {
> +      enum { __value = 1 };
> +      typedef __true_type __type;
> +    };
> +
> +  template<typename _Tp>
> +    struct __is_pointer<_Tp* volatile>
> +    {
> +      enum { __value = 1 };
> +      typedef __true_type __type;
> +    };
> +#endif
> +
> +  template<typename _Tp>
> +    struct __is_pointer<_Tp* const volatile>
> +    {
> +      enum { __value = 1 };
> +      typedef __true_type __type;
> +    };
> +
>    //
>    // An arithmetic type is an integer type or a floating point type
>    //
> diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> index 30b0778e58a..d53911b2fa0 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      : public true_type { };
>  #endif
>  
> -  template<typename>
> -    struct __is_pointer_helper
> +  /// is_pointer
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> +  template<typename _Tp>
> +    struct is_pointer
> +    : public __bool_constant<__is_pointer(_Tp)>
> +    { };
> +#else
> +  template<typename _Tp>
> +    struct is_pointer
>      : public false_type { };
>  
>    template<typename _Tp>
> -    struct __is_pointer_helper<_Tp*>
> +    struct is_pointer<_Tp*>
>      : public true_type { };
>  
> -  /// is_pointer
>    template<typename _Tp>
> -    struct is_pointer
> -    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
> -    { };
> +    struct is_pointer<_Tp* const>
> +    : public true_type { };
> +
> +  template<typename _Tp>
> +    struct is_pointer<_Tp* volatile>
> +    : public true_type { };
> +
> +  template<typename _Tp>
> +    struct is_pointer<_Tp* const volatile>
> +    : public true_type { };
> +#endif
>  
>    /// is_lvalue_reference
>    template<typename>
> @@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
>    inline constexpr bool is_array_v<_Tp[_Num]> = true;
>  #endif
>  
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
> +#else
>  template <typename _Tp>
> -  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
> +  inline constexpr bool is_pointer_v = false;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp*> = true;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp* const> = true;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;

Is this fallback implementation faster than the current implementation?
LGTM if so.

> +#endif
> +
>  template <typename _Tp>
>    inline constexpr bool is_lvalue_reference_v = false;
>  template <typename _Tp>
> -- 
> 2.43.0
> 
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2023-12-23 22:02 ` [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
  2024-01-04 22:06   ` Patrick Palka
@ 2024-01-04 22:13   ` Jonathan Wakely
  2024-01-06  4:59     ` Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Jonathan Wakely @ 2024-01-04 22:13 UTC (permalink / raw)
  To: Ken Matsui; +Cc: gcc-patches, libstdc++

On Sat, 23 Dec 2023 at 22:07, Ken Matsui <kmatsui@gcc.gnu.org> wrote:
>
> This patch optimizes the compilation performance of std::is_pointer
> by dispatching to the new __is_pointer built-in trait.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/cpp_type_traits.h (__is_pointer): Use
>         __is_pointer built-in trait.  Optimize its implementation.
>         * include/std/type_traits (is_pointer): Likewise.
>         (is_pointer_v): Likewise.
>
> Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>  libstdc++-v3/include/bits/cpp_type_traits.h | 29 ++++++++++++++
>  libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
>  2 files changed, 65 insertions(+), 8 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
> index 4312f32a4e0..c348df97f72 100644
> --- a/libstdc++-v3/include/bits/cpp_type_traits.h
> +++ b/libstdc++-v3/include/bits/cpp_type_traits.h
> @@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
>    //
>    // Pointer types
>    //
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> +  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
> +    struct __is_pointer : __truth_type<_IsPtr>
> +    {
> +      enum { __value = _IsPtr };
> +    };
> +#else
>    template<typename _Tp>
>      struct __is_pointer
>      {
> @@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
>        typedef __true_type __type;
>      };
>
> +  template<typename _Tp>
> +    struct __is_pointer<_Tp* const>
> +    {
> +      enum { __value = 1 };
> +      typedef __true_type __type;
> +    };
> +
> +  template<typename _Tp>
> +    struct __is_pointer<_Tp* volatile>
> +    {
> +      enum { __value = 1 };
> +      typedef __true_type __type;
> +    };
> +#endif
> +
> +  template<typename _Tp>
> +    struct __is_pointer<_Tp* const volatile>
> +    {
> +      enum { __value = 1 };
> +      typedef __true_type __type;
> +    };

Why is this partial specialization outside the #else group?


> +
>    //
>    // An arithmetic type is an integer type or a floating point type
>    //
> diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> index 30b0778e58a..d53911b2fa0 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      : public true_type { };
>  #endif
>
> -  template<typename>
> -    struct __is_pointer_helper
> +  /// is_pointer
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> +  template<typename _Tp>
> +    struct is_pointer
> +    : public __bool_constant<__is_pointer(_Tp)>
> +    { };
> +#else
> +  template<typename _Tp>
> +    struct is_pointer
>      : public false_type { };
>
>    template<typename _Tp>
> -    struct __is_pointer_helper<_Tp*>
> +    struct is_pointer<_Tp*>
>      : public true_type { };
>
> -  /// is_pointer
>    template<typename _Tp>
> -    struct is_pointer
> -    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
> -    { };
> +    struct is_pointer<_Tp* const>
> +    : public true_type { };
> +
> +  template<typename _Tp>
> +    struct is_pointer<_Tp* volatile>
> +    : public true_type { };
> +
> +  template<typename _Tp>
> +    struct is_pointer<_Tp* const volatile>
> +    : public true_type { };
> +#endif
>
>    /// is_lvalue_reference
>    template<typename>
> @@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
>    inline constexpr bool is_array_v<_Tp[_Num]> = true;
>  #endif
>
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
> +#else
>  template <typename _Tp>
> -  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
> +  inline constexpr bool is_pointer_v = false;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp*> = true;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp* const> = true;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
> +template <typename _Tp>
> +  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
> +#endif
> +
>  template <typename _Tp>
>    inline constexpr bool is_lvalue_reference_v = false;
>  template <typename _Tp>
> --
> 2.43.0
>


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2024-01-04 22:06   ` Patrick Palka
@ 2024-01-04 22:16     ` Patrick Palka
  2024-01-06  4:59       ` Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Patrick Palka @ 2024-01-04 22:16 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Ken Matsui, gcc-patches, libstdc++, Jonathan Wakely

On Thu, 4 Jan 2024, Patrick Palka wrote:

> On Sat, 23 Dec 2023, Ken Matsui wrote:
> 
> > This patch optimizes the compilation performance of std::is_pointer
> > by dispatching to the new __is_pointer built-in trait.
> > 
> > libstdc++-v3/ChangeLog:
> > 
> > 	* include/bits/cpp_type_traits.h (__is_pointer): Use
> > 	__is_pointer built-in trait.  Optimize its implementation.
> > 	* include/std/type_traits (is_pointer): Likewise.
> > 	(is_pointer_v): Likewise.
> > 
> > Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >  libstdc++-v3/include/bits/cpp_type_traits.h | 29 ++++++++++++++
> >  libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
> >  2 files changed, 65 insertions(+), 8 deletions(-)
> > 
> > diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
> > index 4312f32a4e0..c348df97f72 100644
> > --- a/libstdc++-v3/include/bits/cpp_type_traits.h
> > +++ b/libstdc++-v3/include/bits/cpp_type_traits.h
> > @@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
> >    //
> >    // Pointer types
> >    //
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > +  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
> > +    struct __is_pointer : __truth_type<_IsPtr>
> > +    {
> > +      enum { __value = _IsPtr };
> > +    };
> > +#else
> >    template<typename _Tp>
> >      struct __is_pointer
> >      {
> > @@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
> >        typedef __true_type __type;
> >      };
> >  
> > +  template<typename _Tp>
> > +    struct __is_pointer<_Tp* const>
> > +    {
> > +      enum { __value = 1 };
> > +      typedef __true_type __type;
> > +    };
> > +
> > +  template<typename _Tp>
> > +    struct __is_pointer<_Tp* volatile>
> > +    {
> > +      enum { __value = 1 };
> > +      typedef __true_type __type;
> > +    };
> > +#endif
> > +
> > +  template<typename _Tp>
> > +    struct __is_pointer<_Tp* const volatile>
> > +    {
> > +      enum { __value = 1 };
> > +      typedef __true_type __type;
> > +    };
> > +
> >    //
> >    // An arithmetic type is an integer type or a floating point type
> >    //
> > diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> > index 30b0778e58a..d53911b2fa0 100644
> > --- a/libstdc++-v3/include/std/type_traits
> > +++ b/libstdc++-v3/include/std/type_traits
> > @@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      : public true_type { };
> >  #endif
> >  
> > -  template<typename>
> > -    struct __is_pointer_helper
> > +  /// is_pointer
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > +  template<typename _Tp>
> > +    struct is_pointer
> > +    : public __bool_constant<__is_pointer(_Tp)>
> > +    { };
> > +#else
> > +  template<typename _Tp>
> > +    struct is_pointer
> >      : public false_type { };
> >  
> >    template<typename _Tp>
> > -    struct __is_pointer_helper<_Tp*>
> > +    struct is_pointer<_Tp*>
> >      : public true_type { };
> >  
> > -  /// is_pointer
> >    template<typename _Tp>
> > -    struct is_pointer
> > -    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
> > -    { };
> > +    struct is_pointer<_Tp* const>
> > +    : public true_type { };
> > +
> > +  template<typename _Tp>
> > +    struct is_pointer<_Tp* volatile>
> > +    : public true_type { };
> > +
> > +  template<typename _Tp>
> > +    struct is_pointer<_Tp* const volatile>
> > +    : public true_type { };
> > +#endif
> >  
> >    /// is_lvalue_reference
> >    template<typename>
> > @@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
> >    inline constexpr bool is_array_v<_Tp[_Num]> = true;
> >  #endif
> >  
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
> > +#else
> >  template <typename _Tp>
> > -  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
> > +  inline constexpr bool is_pointer_v = false;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp*> = true;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp* const> = true;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
> 
> Is this fallback implementation faster than the current implementation?
> LGTM if so.

By the way, a related optimization might be to optimize __remove_cv_t
via the built-in (related since the current fallback implementation is
in terms of __remove_cv_t).  This should be safe since the alias
template isn't used in any function signature, so it's never mangled.

> 
> > +#endif
> > +
> >  template <typename _Tp>
> >    inline constexpr bool is_lvalue_reference_v = false;
> >  template <typename _Tp>
> > -- 
> > 2.43.0
> > 
> > 
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2024-01-04 22:16     ` Patrick Palka
@ 2024-01-06  4:59       ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  4:59 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Ken Matsui, gcc-patches, libstdc++, Jonathan Wakely

On Thu, Jan 4, 2024 at 2:16 PM Patrick Palka <ppalka@redhat.com> wrote:
>
> On Thu, 4 Jan 2024, Patrick Palka wrote:
>
> > On Sat, 23 Dec 2023, Ken Matsui wrote:
> >
> > > This patch optimizes the compilation performance of std::is_pointer
> > > by dispatching to the new __is_pointer built-in trait.
> > >
> > > libstdc++-v3/ChangeLog:
> > >
> > >     * include/bits/cpp_type_traits.h (__is_pointer): Use
> > >     __is_pointer built-in trait.  Optimize its implementation.
> > >     * include/std/type_traits (is_pointer): Likewise.
> > >     (is_pointer_v): Likewise.
> > >
> > > Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
> > > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > > ---
> > >  libstdc++-v3/include/bits/cpp_type_traits.h | 29 ++++++++++++++
> > >  libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
> > >  2 files changed, 65 insertions(+), 8 deletions(-)
> > >
> > > diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
> > > index 4312f32a4e0..c348df97f72 100644
> > > --- a/libstdc++-v3/include/bits/cpp_type_traits.h
> > > +++ b/libstdc++-v3/include/bits/cpp_type_traits.h
> > > @@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
> > >    //
> > >    // Pointer types
> > >    //
> > > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > > +  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
> > > +    struct __is_pointer : __truth_type<_IsPtr>
> > > +    {
> > > +      enum { __value = _IsPtr };
> > > +    };
> > > +#else
> > >    template<typename _Tp>
> > >      struct __is_pointer
> > >      {
> > > @@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
> > >        typedef __true_type __type;
> > >      };
> > >
> > > +  template<typename _Tp>
> > > +    struct __is_pointer<_Tp* const>
> > > +    {
> > > +      enum { __value = 1 };
> > > +      typedef __true_type __type;
> > > +    };
> > > +
> > > +  template<typename _Tp>
> > > +    struct __is_pointer<_Tp* volatile>
> > > +    {
> > > +      enum { __value = 1 };
> > > +      typedef __true_type __type;
> > > +    };
> > > +#endif
> > > +
> > > +  template<typename _Tp>
> > > +    struct __is_pointer<_Tp* const volatile>
> > > +    {
> > > +      enum { __value = 1 };
> > > +      typedef __true_type __type;
> > > +    };
> > > +
> > >    //
> > >    // An arithmetic type is an integer type or a floating point type
> > >    //
> > > diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> > > index 30b0778e58a..d53911b2fa0 100644
> > > --- a/libstdc++-v3/include/std/type_traits
> > > +++ b/libstdc++-v3/include/std/type_traits
> > > @@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > >      : public true_type { };
> > >  #endif
> > >
> > > -  template<typename>
> > > -    struct __is_pointer_helper
> > > +  /// is_pointer
> > > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > > +  template<typename _Tp>
> > > +    struct is_pointer
> > > +    : public __bool_constant<__is_pointer(_Tp)>
> > > +    { };
> > > +#else
> > > +  template<typename _Tp>
> > > +    struct is_pointer
> > >      : public false_type { };
> > >
> > >    template<typename _Tp>
> > > -    struct __is_pointer_helper<_Tp*>
> > > +    struct is_pointer<_Tp*>
> > >      : public true_type { };
> > >
> > > -  /// is_pointer
> > >    template<typename _Tp>
> > > -    struct is_pointer
> > > -    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
> > > -    { };
> > > +    struct is_pointer<_Tp* const>
> > > +    : public true_type { };
> > > +
> > > +  template<typename _Tp>
> > > +    struct is_pointer<_Tp* volatile>
> > > +    : public true_type { };
> > > +
> > > +  template<typename _Tp>
> > > +    struct is_pointer<_Tp* const volatile>
> > > +    : public true_type { };
> > > +#endif
> > >
> > >    /// is_lvalue_reference
> > >    template<typename>
> > > @@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
> > >    inline constexpr bool is_array_v<_Tp[_Num]> = true;
> > >  #endif
> > >
> > > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > > +template <typename _Tp>
> > > +  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
> > > +#else
> > >  template <typename _Tp>
> > > -  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
> > > +  inline constexpr bool is_pointer_v = false;
> > > +template <typename _Tp>
> > > +  inline constexpr bool is_pointer_v<_Tp*> = true;
> > > +template <typename _Tp>
> > > +  inline constexpr bool is_pointer_v<_Tp* const> = true;
> > > +template <typename _Tp>
> > > +  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
> > > +template <typename _Tp>
> > > +  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
> >
> > Is this fallback implementation faster than the current implementation?
> > LGTM if so.
>

Yes.  Here are the benchmarks: 1 is current impl vs. built-in, and 2
is new impl vs. built-in.  There is no explicit benchmark that
compares the current impl and the new impl, but I think this shows the
new impl is faster.

1. https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624227.html

Time: -62.1344%
Peak Memory Usage: -52.4281%
Total Memory Usage: -53.5889%

2. https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624326.html

Time: -2.79488%
Peak Memory Usage: -2.39379%
Total Memory Usage: -3.39559%

> By the way, a related optimization might be to optimize __remove_cv_t
> via the built-in (related since the current fallback implementation is
> in terms of __remove_cv_t).  This should be safe since the alias
> template isn't used in any function signature, so it's never mangled.
>

Do we want to try this implementation as well?  I think in this
is_pointer case, the explicit specialization way is enough, but I am
happy to hear your thoughts.

> >
> > > +#endif
> > > +
> > >  template <typename _Tp>
> > >    inline constexpr bool is_lvalue_reference_v = false;
> > >  template <typename _Tp>
> > > --
> > > 2.43.0
> > >
> > >
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2024-01-04 22:13   ` Jonathan Wakely
@ 2024-01-06  4:59     ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  4:59 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Ken Matsui, gcc-patches, libstdc++

On Thu, Jan 4, 2024 at 2:13 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Sat, 23 Dec 2023 at 22:07, Ken Matsui <kmatsui@gcc.gnu.org> wrote:
> >
> > This patch optimizes the compilation performance of std::is_pointer
> > by dispatching to the new __is_pointer built-in trait.
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/bits/cpp_type_traits.h (__is_pointer): Use
> >         __is_pointer built-in trait.  Optimize its implementation.
> >         * include/std/type_traits (is_pointer): Likewise.
> >         (is_pointer_v): Likewise.
> >
> > Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >  libstdc++-v3/include/bits/cpp_type_traits.h | 29 ++++++++++++++
> >  libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
> >  2 files changed, 65 insertions(+), 8 deletions(-)
> >
> > diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
> > index 4312f32a4e0..c348df97f72 100644
> > --- a/libstdc++-v3/include/bits/cpp_type_traits.h
> > +++ b/libstdc++-v3/include/bits/cpp_type_traits.h
> > @@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
> >    //
> >    // Pointer types
> >    //
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > +  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
> > +    struct __is_pointer : __truth_type<_IsPtr>
> > +    {
> > +      enum { __value = _IsPtr };
> > +    };
> > +#else
> >    template<typename _Tp>
> >      struct __is_pointer
> >      {
> > @@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
> >        typedef __true_type __type;
> >      };
> >
> > +  template<typename _Tp>
> > +    struct __is_pointer<_Tp* const>
> > +    {
> > +      enum { __value = 1 };
> > +      typedef __true_type __type;
> > +    };
> > +
> > +  template<typename _Tp>
> > +    struct __is_pointer<_Tp* volatile>
> > +    {
> > +      enum { __value = 1 };
> > +      typedef __true_type __type;
> > +    };
> > +#endif
> > +
> > +  template<typename _Tp>
> > +    struct __is_pointer<_Tp* const volatile>
> > +    {
> > +      enum { __value = 1 };
> > +      typedef __true_type __type;
> > +    };
>
> Why is this partial specialization outside the #else group?
>

This is totally my fault.  Thank you for pointing this out!

>
> > +
> >    //
> >    // An arithmetic type is an integer type or a floating point type
> >    //
> > diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> > index 30b0778e58a..d53911b2fa0 100644
> > --- a/libstdc++-v3/include/std/type_traits
> > +++ b/libstdc++-v3/include/std/type_traits
> > @@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >      : public true_type { };
> >  #endif
> >
> > -  template<typename>
> > -    struct __is_pointer_helper
> > +  /// is_pointer
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > +  template<typename _Tp>
> > +    struct is_pointer
> > +    : public __bool_constant<__is_pointer(_Tp)>
> > +    { };
> > +#else
> > +  template<typename _Tp>
> > +    struct is_pointer
> >      : public false_type { };
> >
> >    template<typename _Tp>
> > -    struct __is_pointer_helper<_Tp*>
> > +    struct is_pointer<_Tp*>
> >      : public true_type { };
> >
> > -  /// is_pointer
> >    template<typename _Tp>
> > -    struct is_pointer
> > -    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
> > -    { };
> > +    struct is_pointer<_Tp* const>
> > +    : public true_type { };
> > +
> > +  template<typename _Tp>
> > +    struct is_pointer<_Tp* volatile>
> > +    : public true_type { };
> > +
> > +  template<typename _Tp>
> > +    struct is_pointer<_Tp* const volatile>
> > +    : public true_type { };
> > +#endif
> >
> >    /// is_lvalue_reference
> >    template<typename>
> > @@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
> >    inline constexpr bool is_array_v<_Tp[_Num]> = true;
> >  #endif
> >
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
> > +#else
> >  template <typename _Tp>
> > -  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
> > +  inline constexpr bool is_pointer_v = false;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp*> = true;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp* const> = true;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
> > +template <typename _Tp>
> > +  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
> > +#endif
> > +
> >  template <typename _Tp>
> >    inline constexpr bool is_lvalue_reference_v = false;
> >  template <typename _Tp>
> > --
> > 2.43.0
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 0/8] Optimize more type traits
  2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
                   ` (7 preceding siblings ...)
  2023-12-23 22:02 ` [PATCH v2 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-01-06  5:05 ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 1/8] c++: Implement __is_const built-in trait Ken Matsui
                     ` (9 more replies)
  8 siblings, 10 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

Changes in v3:

- Rebased on top of master.
- Fixed __is_pointer in cpp_type_traits.h.

Changes in v2:

- Removed testsuite_tr1.h includes from the testcases.

---

This patch series implements __is_const, __is_volatile, __is_pointer,
and __is_unbounded_array built-in traits, which were isolated from my
previous patch series "Optimize type traits compilation performance"
because they contained performance regression.  I confirmed that this
patch series does not cause any performance regression.  The main reason
of the performance regression were the exhaustiveness of the benchmarks
and the instability of the benchmark results.  Here are new benchmark
results:

is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023

time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%

is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023

time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%

is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023

time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%

is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023

time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%

Ken Matsui (8):
  c++: Implement __is_const built-in trait
  libstdc++: Optimize std::is_const compilation performance
  c++: Implement __is_volatile built-in trait
  libstdc++: Optimize std::is_volatile compilation performance
  c++: Implement __is_pointer built-in trait
  libstdc++: Optimize std::is_pointer compilation performance
  c++: Implement __is_unbounded_array built-in trait
  libstdc++: Optimize std::is_unbounded_array compilation performance

 gcc/cp/constraint.cc                          | 12 +++
 gcc/cp/cp-trait.def                           |  4 +
 gcc/cp/semantics.cc                           | 16 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      | 12 +++
 gcc/testsuite/g++.dg/ext/is_const.C           | 20 +++++
 gcc/testsuite/g++.dg/ext/is_pointer.C         | 51 +++++++++++++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 ++++++++++
 gcc/testsuite/g++.dg/ext/is_volatile.C        | 20 +++++
 libstdc++-v3/include/bits/cpp_type_traits.h   | 31 +++++++-
 libstdc++-v3/include/std/type_traits          | 73 +++++++++++++++++--
 10 files changed, 267 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 1/8] c++: Implement __is_const built-in trait
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 2/8] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index fef68cf7ab2..669803b586c 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3734,6 +3734,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 082fe2db4f2..1a6f08c37ec 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12415,6 +12415,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12657,6 +12660,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 2/8] libstdc++: Optimize std::is_const compilation performance
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 1/8] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 3/8] c++: Implement __is_volatile built-in trait Ken Matsui
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 3b1b419bbbb..e64ed1de891 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3315,10 +3322,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 3/8] c++: Implement __is_volatile built-in trait
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 1/8] c++: Implement __is_const built-in trait Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 2/8] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 4/8] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                     ` (6 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 669803b586c..6f13546b9b2 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 1a6f08c37ec..0f304cdc642 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12501,6 +12501,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12671,6 +12674,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 4/8] libstdc++: Optimize std::is_volatile compilation performance
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (2 preceding siblings ...)
  2024-01-06  5:05   ` [PATCH v3 3/8] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 5/8] c++: Implement __is_pointer built-in trait Ken Matsui
                     ` (5 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index e64ed1de891..2bfc31b141d 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3344,10 +3351,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 5/8] c++: Implement __is_pointer built-in trait
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (3 preceding siblings ...)
  2024-01-06  5:05   ` [PATCH v3 4/8] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 6f13546b9b2..3d7c0509f6b 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3795,6 +3795,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0f304cdc642..4b75ff744d4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12471,6 +12471,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12670,6 +12673,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..96b7a89e4f1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -125,6 +125,9 @@
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 6/8] libstdc++: Optimize std::is_pointer compilation performance
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (4 preceding siblings ...)
  2024-01-06  5:05   ` [PATCH v3 5/8] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 7/8] c++: Implement __is_unbounded_array built-in trait Ken Matsui
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..210a9ea00da 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 2bfc31b141d..018bf20ba1d 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3252,8 +3266,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 7/8] c++: Implement __is_unbounded_array built-in trait
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (5 preceding siblings ...)
  2024-01-06  5:05   ` [PATCH v3 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-01-06  5:05   ` [PATCH v3 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                     ` (2 subsequent siblings)
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 3d7c0509f6b..9454739a76f 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..05514a51c21 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 4b75ff744d4..a2ab945e50a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12501,6 +12501,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12677,6 +12680,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 96b7a89e4f1..b1430e9bd8b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_trivially_copyable)
 # error "__has_builtin (__is_trivially_copyable) failed"
 #endif
+#if !__has_builtin (__is_unbounded_array)
+# error "__has_builtin (__is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..283a74e1a0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v3 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (6 preceding siblings ...)
  2024-01-06  5:05   ` [PATCH v3 7/8] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2024-01-06  5:05   ` Ken Matsui
  2024-02-15  5:07   ` PING: [PATCH v3 0/8] Optimize more type traits Ken Matsui
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-01-06  5:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__is_unbounded_array built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__is_unbounded_array built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 018bf20ba1d..36344a014c3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3675,11 +3675,16 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v = __is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.43.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* PING: [PATCH v3 0/8] Optimize more type traits
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (7 preceding siblings ...)
  2024-01-06  5:05   ` [PATCH v3 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-02-15  5:07   ` Ken Matsui
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
  9 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-15  5:07 UTC (permalink / raw)
  To: Ken Matsui; +Cc: gcc-patches, libstdc++

IIRC, all libstdc++ patches were already reviewed.  It would be great
if gcc patches were reviewed as well.  Thank you for your time.

Sincerely,
Ken Matsui

On Fri, Jan 5, 2024 at 9:08 PM Ken Matsui <kmatsui@gcc.gnu.org> wrote:
>
> Changes in v3:
>
> - Rebased on top of master.
> - Fixed __is_pointer in cpp_type_traits.h.
>
> Changes in v2:
>
> - Removed testsuite_tr1.h includes from the testcases.
>
> ---
>
> This patch series implements __is_const, __is_volatile, __is_pointer,
> and __is_unbounded_array built-in traits, which were isolated from my
> previous patch series "Optimize type traits compilation performance"
> because they contained performance regression.  I confirmed that this
> patch series does not cause any performance regression.  The main reason
> of the performance regression were the exhaustiveness of the benchmarks
> and the instability of the benchmark results.  Here are new benchmark
> results:
>
> is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023
>
> time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%
>
> is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023
>
> time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%
>
> is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023
>
> time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%
>
> is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023
>
> time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%
>
> Ken Matsui (8):
>   c++: Implement __is_const built-in trait
>   libstdc++: Optimize std::is_const compilation performance
>   c++: Implement __is_volatile built-in trait
>   libstdc++: Optimize std::is_volatile compilation performance
>   c++: Implement __is_pointer built-in trait
>   libstdc++: Optimize std::is_pointer compilation performance
>   c++: Implement __is_unbounded_array built-in trait
>   libstdc++: Optimize std::is_unbounded_array compilation performance
>
>  gcc/cp/constraint.cc                          | 12 +++
>  gcc/cp/cp-trait.def                           |  4 +
>  gcc/cp/semantics.cc                           | 16 ++++
>  gcc/testsuite/g++.dg/ext/has-builtin-1.C      | 12 +++
>  gcc/testsuite/g++.dg/ext/is_const.C           | 20 +++++
>  gcc/testsuite/g++.dg/ext/is_pointer.C         | 51 +++++++++++++
>  gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 ++++++++++
>  gcc/testsuite/g++.dg/ext/is_volatile.C        | 20 +++++
>  libstdc++-v3/include/bits/cpp_type_traits.h   | 31 +++++++-
>  libstdc++-v3/include/std/type_traits          | 73 +++++++++++++++++--
>  10 files changed, 267 insertions(+), 9 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
>
> --
> 2.43.0
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 01/22] c++: Implement __is_const built-in trait
  2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
                     ` (8 preceding siblings ...)
  2024-02-15  5:07   ` PING: [PATCH v3 0/8] Optimize more type traits Ken Matsui
@ 2024-02-19 13:51   ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 02/22] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                       ` (22 more replies)
  9 siblings, 23 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d9569013bd3..8b7833d6cae 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3766,6 +3766,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 57840176863..0d08900492b 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12446,6 +12446,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12688,6 +12691,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 02/22] libstdc++: Optimize std::is_const compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 03/22] c++: Implement __is_volatile built-in trait Ken Matsui
                       ` (21 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 21402fd8c13..6e9ebfb8a18 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3327,10 +3334,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 03/22] c++: Implement __is_volatile built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 02/22] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 04/22] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                       ` (20 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 8b7833d6cae..91ace54cac1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3860,6 +3860,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0d08900492b..41c25f43d27 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12702,6 +12705,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 04/22] libstdc++: Optimize std::is_volatile compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 02/22] libstdc++: Optimize std::is_const compilation performance Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 03/22] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 05/22] c++: Implement __is_pointer built-in trait Ken Matsui
                       ` (19 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6e9ebfb8a18..60cd22b6f15 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3356,10 +3363,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 05/22] c++: Implement __is_pointer built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (2 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 04/22] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 06/22] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                       ` (18 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 91ace54cac1..22cabd97cb6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3827,6 +3827,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 41c25f43d27..9dcdb06191a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12502,6 +12502,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12701,6 +12704,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..96b7a89e4f1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -125,6 +125,9 @@
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 06/22] libstdc++: Optimize std::is_pointer compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (3 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 05/22] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 07/22] c++: Implement __is_unbounded_array built-in trait Ken Matsui
                       ` (17 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..210a9ea00da 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 60cd22b6f15..6407738a726 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3264,8 +3278,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 07/22] c++: Implement __is_unbounded_array built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (4 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 06/22] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 08/22] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                       ` (16 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 22cabd97cb6..62b264e4757 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3860,6 +3860,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..05514a51c21 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9dcdb06191a..1794e83baa2 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12708,6 +12711,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 96b7a89e4f1..b1430e9bd8b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_trivially_copyable)
 # error "__has_builtin (__is_trivially_copyable) failed"
 #endif
+#if !__has_builtin (__is_unbounded_array)
+# error "__has_builtin (__is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..283a74e1a0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 08/22] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (5 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 07/22] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 09/22] c++: Implement __add_pointer built-in trait Ken Matsui
                       ` (15 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__is_unbounded_array built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__is_unbounded_array built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6407738a726..c4585a23df9 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3706,11 +3706,16 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v = __is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 09/22] c++: Implement __add_pointer built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (6 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 08/22] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 10/22] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
                       ` (14 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_pointer.
	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __add_pointer.
	* g++.dg/ext/add_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  9 ++++++
 gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 52 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 05514a51c21..63f879287ce 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 1794e83baa2..635441a7a90 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12776,6 +12776,15 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_POINTER:
+      if (FUNC_OR_METHOD_TYPE_P (type1)
+	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+	      || type_memfn_rqual (type1) != REF_QUAL_NONE))
+	return type1;
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+      return build_pointer_type (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
new file mode 100644
index 00000000000..c405cdd0feb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_pointer(int), int*));
+SA(__is_same(__add_pointer(int*), int**));
+SA(__is_same(__add_pointer(const int), const int*));
+SA(__is_same(__add_pointer(int&), int*));
+SA(__is_same(__add_pointer(ClassType*), ClassType**));
+SA(__is_same(__add_pointer(ClassType), ClassType*));
+SA(__is_same(__add_pointer(void), void*));
+SA(__is_same(__add_pointer(const void), const void*));
+SA(__is_same(__add_pointer(volatile void), volatile void*));
+SA(__is_same(__add_pointer(const volatile void), const volatile void*));
+
+void f1();
+using f1_type = decltype(f1);
+using pf1_type = decltype(&f1);
+SA(__is_same(__add_pointer(f1_type), pf1_type));
+
+void f2() noexcept; // PR libstdc++/78361
+using f2_type = decltype(f2);
+using pf2_type = decltype(&f2);
+SA(__is_same(__add_pointer(f2_type), pf2_type));
+
+using fn_type = void();
+using pfn_type = void(*)();
+SA(__is_same(__add_pointer(fn_type), pfn_type));
+
+SA(__is_same(__add_pointer(void() &), void() &));
+SA(__is_same(__add_pointer(void() & noexcept), void() & noexcept));
+SA(__is_same(__add_pointer(void() const), void() const));
+SA(__is_same(__add_pointer(void(...) &), void(...) &));
+SA(__is_same(__add_pointer(void(...) & noexcept), void(...) & noexcept));
+SA(__is_same(__add_pointer(void(...) const), void(...) const));
+
+SA(__is_same(__add_pointer(void() __restrict), void() __restrict));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b1430e9bd8b..9d861398bae 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_pointer)
+# error "__has_builtin (__add_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 10/22] libstdc++: Optimize std::add_pointer compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (7 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 09/22] c++: Implement __add_pointer built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 11/22] c++: Implement __remove_extent built-in trait Ken Matsui
                       ` (13 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::add_pointer
by dispatching to the new __add_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_pointer): Use __add_pointer
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index c4585a23df9..6346d1daee2 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2149,6 +2149,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 #endif
 
+  /// add_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_pointer)
+  template<typename _Tp>
+    struct add_pointer
+    { using type = __add_pointer(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_pointer_helper
     { using type = _Tp; };
@@ -2157,7 +2163,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __add_pointer_helper<_Tp, __void_t<_Tp*>>
     { using type = _Tp*; };
 
-  /// add_pointer
   template<typename _Tp>
     struct add_pointer
     : public __add_pointer_helper<_Tp>
@@ -2170,6 +2175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct add_pointer<_Tp&&>
     { using type = _Tp*; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_pointer
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 11/22] c++: Implement __remove_extent built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (8 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 10/22] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 12/22] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
                       ` (12 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::remove_extent.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_extent.
	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __remove_extent.
	* g++.dg/ext/remove_extent.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  5 +++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
 4 files changed, 25 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 63f879287ce..577c96d579b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
+DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 635441a7a90..58696225fc4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12793,6 +12793,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return cv_unqualified (type1);
 
+    case CPTK_REMOVE_EXTENT:
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	type1 = TREE_TYPE (type1);
+      return type1;
+
     case CPTK_REMOVE_POINTER:
       if (TYPE_PTR_P (type1))
 	type1 = TREE_TYPE (type1);
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 9d861398bae..5d5cbe3b019 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -182,6 +182,9 @@
 #if !__has_builtin (__remove_cvref)
 # error "__has_builtin (__remove_cvref) failed"
 #endif
+#if !__has_builtin (__remove_extent)
+# error "__has_builtin (__remove_extent) failed"
+#endif
 #if !__has_builtin (__remove_pointer)
 # error "__has_builtin (__remove_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
new file mode 100644
index 00000000000..6183aca5a48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_extent(int), int));
+SA(__is_same(__remove_extent(int[2]), int));
+SA(__is_same(__remove_extent(int[2][3]), int[3]));
+SA(__is_same(__remove_extent(int[][3]), int[3]));
+SA(__is_same(__remove_extent(const int[2]), const int));
+SA(__is_same(__remove_extent(ClassType), ClassType));
+SA(__is_same(__remove_extent(ClassType[2]), ClassType));
+SA(__is_same(__remove_extent(ClassType[2][3]), ClassType[3]));
+SA(__is_same(__remove_extent(ClassType[][3]), ClassType[3]));
+SA(__is_same(__remove_extent(const ClassType[2]), const ClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 12/22] libstdc++: Optimize std::remove_extent compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (9 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 11/22] c++: Implement __remove_extent built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 13/22] c++: Implement __remove_all_extents built-in trait Ken Matsui
                       ` (11 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::remove_extent
by dispatching to the new __remove_extent built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_extent): Use __remove_extent
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6346d1daee2..73ddce351fd 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2092,6 +2092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Array modifications.
 
   /// remove_extent
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_extent)
+  template<typename _Tp>
+    struct remove_extent
+    { using type = __remove_extent(_Tp); };
+#else
   template<typename _Tp>
     struct remove_extent
     { using type = _Tp; };
@@ -2103,6 +2108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_extent<_Tp[]>
     { using type = _Tp; };
+#endif
 
   /// remove_all_extents
   template<typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 13/22] c++: Implement __remove_all_extents built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (10 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 12/22] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 14/22] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
                       ` (10 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::remove_all_extents.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_all_extents.
	* semantics.cc (finish_trait_type): Handle
	CPTK_REMOVE_ALL_EXTENTS.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__remove_all_extents.
	* g++.dg/ext/remove_all_extents.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  3 +++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 577c96d579b..933c8bcbe68 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 58696225fc4..078424dac23 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12785,6 +12785,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return build_pointer_type (type1);
 
+    case CPTK_REMOVE_ALL_EXTENTS:
+      return strip_array_types (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 5d5cbe3b019..85b74bd676b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -176,6 +176,9 @@
 #if !__has_builtin (__reference_converts_from_temporary)
 # error "__has_builtin (__reference_converts_from_temporary) failed"
 #endif
+#if !__has_builtin (__remove_all_extents)
+# error "__has_builtin (__remove_all_extents) failed"
+#endif
 #if !__has_builtin (__remove_cv)
 # error "__has_builtin (__remove_cv) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
new file mode 100644
index 00000000000..60ade2ade7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_all_extents(int), int));
+SA(__is_same(__remove_all_extents(int[2]), int));
+SA(__is_same(__remove_all_extents(int[2][3]), int));
+SA(__is_same(__remove_all_extents(int[][3]), int));
+SA(__is_same(__remove_all_extents(const int[2][3]), const int));
+SA(__is_same(__remove_all_extents(ClassType), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2][3]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[][3]), ClassType));
+SA(__is_same(__remove_all_extents(const ClassType[2][3]), const ClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 14/22] libstdc++: Optimize std::remove_all_extents compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (11 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 13/22] c++: Implement __remove_all_extents built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 15/22] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
                       ` (9 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::remove_all_extents by dispatching to the new __remove_all_extents
built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_all_extents): Use
	__remove_all_extents built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 73ddce351fd..34475e6279a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2111,6 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// remove_all_extents
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_all_extents)
+  template<typename _Tp>
+    struct remove_all_extents
+    { using type = __remove_all_extents(_Tp); };
+#else
   template<typename _Tp>
     struct remove_all_extents
     { using type = _Tp; };
@@ -2122,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_all_extents<_Tp[]>
     { using type = typename remove_all_extents<_Tp>::type; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_extent
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 15/22] c++: Implement __add_lvalue_reference built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (12 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 14/22] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 16/22] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
                       ` (8 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_lvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_lvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_LVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_lvalue_reference.
	* g++.dg/ext/add_lvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  8 +++++++
 .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 33 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 933c8bcbe68..9a27dca4ea3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 078424dac23..05f5b62f9df 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12776,6 +12776,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_LVALUE_REFERENCE:
+      if (VOID_TYPE_P (type1)
+	  || (FUNC_OR_METHOD_TYPE_P (type1)
+	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
+	return type1;
+      return cp_build_reference_type (type1, /*rval=*/false);
+
     case CPTK_ADD_POINTER:
       if (FUNC_OR_METHOD_TYPE_P (type1)
 	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
new file mode 100644
index 00000000000..8fe1e0300e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_lvalue_reference(int), int&));
+SA(__is_same(__add_lvalue_reference(int&), int&));
+SA(__is_same(__add_lvalue_reference(const int), const int&));
+SA(__is_same(__add_lvalue_reference(int*), int*&));
+SA(__is_same(__add_lvalue_reference(ClassType&), ClassType&));
+SA(__is_same(__add_lvalue_reference(ClassType), ClassType&));
+SA(__is_same(__add_lvalue_reference(int(int)), int(&)(int)));
+SA(__is_same(__add_lvalue_reference(int&&), int&));
+SA(__is_same(__add_lvalue_reference(ClassType&&), ClassType&));
+SA(__is_same(__add_lvalue_reference(void), void));
+SA(__is_same(__add_lvalue_reference(const void), const void));
+SA(__is_same(__add_lvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_lvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_lvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_lvalue_reference(bool(int)), bool(&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 85b74bd676b..3fca9cfabcc 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_lvalue_reference)
+# error "__has_builtin (__add_lvalue_reference) failed"
+#endif
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 16/22] libstdc++: Optimize std::add_lvalue_reference compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (13 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 15/22] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 17/22] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
                       ` (7 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::add_lvalue_reference by dispatching to the new
__add_lvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_lvalue_reference): Use
	__add_lvalue_reference built-in trait.
	(__add_lvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 34475e6279a..17bf47d59d3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1157,6 +1157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct __add_lvalue_reference_helper
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_lvalue_reference_helper
     { using type = _Tp; };
@@ -1164,6 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_lvalue_reference_helper<_Tp, __void_t<_Tp&>>
     { using type = _Tp&; };
+#endif
 
   template<typename _Tp>
     using __add_lval_ref_t = typename __add_lvalue_reference_helper<_Tp>::type;
@@ -1731,9 +1737,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_lvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct add_lvalue_reference
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_lvalue_reference
     { using type = __add_lval_ref_t<_Tp>; };
+#endif
 
   /// add_rvalue_reference
   template<typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 17/22] c++: Implement __add_rvalue_reference built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (14 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 16/22] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 18/22] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
                       ` (6 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_rvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_rvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_RVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_rvalue_reference.
	* g++.dg/ext/add_rvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  8 ++++++++
 .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 32 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 9a27dca4ea3..173818adf79 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -50,6 +50,7 @@
 
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
+DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 05f5b62f9df..19d6f87a9ea 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12793,6 +12793,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return build_pointer_type (type1);
 
+    case CPTK_ADD_RVALUE_REFERENCE:
+      if (VOID_TYPE_P (type1)
+	  || (FUNC_OR_METHOD_TYPE_P (type1)
+	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
+	return type1;
+      return cp_build_reference_type (type1, /*rval=*/true);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
new file mode 100644
index 00000000000..c92fe6bfa17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_rvalue_reference(int), int&&));
+SA(__is_same(__add_rvalue_reference(int&&), int&&));
+SA(__is_same(__add_rvalue_reference(int&), int&));
+SA(__is_same(__add_rvalue_reference(const int), const int&&));
+SA(__is_same(__add_rvalue_reference(int*), int*&&));
+SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+SA(__is_same(__add_rvalue_reference(void), void));
+SA(__is_same(__add_rvalue_reference(const void), const void));
+SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 3fca9cfabcc..c2503c5d82b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -8,6 +8,9 @@
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
+#if !__has_builtin (__add_rvalue_reference)
+# error "__has_builtin (__add_rvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 18/22] libstdc++: Optimize std::add_rvalue_reference compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (15 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 17/22] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 19/22] c++: Implement __decay built-in trait Ken Matsui
                       ` (5 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::add_rvalue_reference by dispatching to the new
__add_rvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_rvalue_reference): Use
	__add_rvalue_reference built-in trait.
	(__add_rvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 17bf47d59d3..18a5e4de2d3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1185,6 +1185,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct __add_rvalue_reference_helper
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_rvalue_reference_helper
     { using type = _Tp; };
@@ -1192,6 +1197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_rvalue_reference_helper<_Tp, __void_t<_Tp&&>>
     { using type = _Tp&&; };
+#endif
 
   template<typename _Tp>
     using __add_rval_ref_t = typename __add_rvalue_reference_helper<_Tp>::type;
@@ -1748,9 +1754,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_rvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct add_rvalue_reference
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_rvalue_reference
     { using type = __add_rval_ref_t<_Tp>; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_reference
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 19/22] c++: Implement __decay built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (16 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 18/22] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 20/22] libstdc++: Optimize std::decay compilation performance Ken Matsui
                       ` (4 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::decay.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __decay.
	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __decay.
	* g++.dg/ext/decay.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 12 ++++++++
 gcc/testsuite/g++.dg/ext/decay.C         | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 55 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 173818adf79..2d1cb7c227c 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -51,6 +51,7 @@
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
+DEFTRAIT_TYPE (DECAY, "__decay", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 19d6f87a9ea..45dc509855a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12801,6 +12801,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	return type1;
       return cp_build_reference_type (type1, /*rval=*/true);
 
+    case CPTK_DECAY:
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
+				  complain);
+      else if (TREE_CODE (type1) == FUNCTION_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
+      else
+	return cv_unqualified (type1);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
new file mode 100644
index 00000000000..cf224b7452c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/decay.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+// class ClassType { };
+
+// Positive tests.
+using test1_type = __decay(bool);
+SA(__is_same(test1_type, bool));
+
+// NB: DR 705.
+using test2_type = __decay(const int);
+SA(__is_same(test2_type, int));
+
+using test3_type = __decay(int[4]);
+SA(__is_same(test3_type, __remove_extent(int[4])*));
+
+using fn_type = void ();
+using test4_type = __decay(fn_type);
+SA(__is_same(test4_type, __add_pointer(fn_type)));
+
+using cfn_type = void () const;
+using test5_type = __decay(cfn_type);
+SA(__is_same(test5_type, cfn_type));
+
+// SA(__is_same(__add_rvalue_reference(int), int&&));
+// SA(__is_same(__add_rvalue_reference(int&&), int&&));
+// SA(__is_same(__add_rvalue_reference(int&), int&));
+// SA(__is_same(__add_rvalue_reference(const int), const int&&));
+// SA(__is_same(__add_rvalue_reference(int*), int*&&));
+// SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+// SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+// SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+// SA(__is_same(__add_rvalue_reference(void), void));
+// SA(__is_same(__add_rvalue_reference(const void), const void));
+// SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+// SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+// SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+// SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index c2503c5d82b..3aca273aad6 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -11,6 +11,9 @@
 #if !__has_builtin (__add_rvalue_reference)
 # error "__has_builtin (__add_rvalue_reference) failed"
 #endif
+#if !__has_builtin (__decay)
+# error "__has_builtin (__decay) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 20/22] libstdc++: Optimize std::decay compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (17 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 19/22] c++: Implement __decay built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 21/22] c++: Implement __rank built-in trait Ken Matsui
                       ` (3 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::decay
by dispatching to the new __decay built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (decay): Use __decay built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 18a5e4de2d3..2f4c8dd3b21 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2316,6 +2316,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @cond undocumented
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__decay)
+  template<typename _Tp>
+    struct decay
+    { using type = __decay(_Tp); };
+#else
   // Decay trait for arrays and functions, used for perfect forwarding
   // in make_pair, make_tuple, etc.
   template<typename _Up>
@@ -2347,6 +2352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct decay<_Tp&&>
     { using type = typename __decay_selector<_Tp>::type; };
+#endif
 
   /// @cond undocumented
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 21/22] c++: Implement __rank built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (18 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 20/22] libstdc++: Optimize std::decay compilation performance Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-19 13:51     ` [PATCH v7 22/22] libstdc++: Optimize std::rank compilation performance Ken Matsui
                       ` (2 subsequent siblings)
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::rank.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __rank.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __rank.
	* g++.dg/ext/rank.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
 5 files changed, 51 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 62b264e4757..a9b6e7416fa 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3869,6 +3869,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
+    case CPTK_RANK:
+      inform (loc, "  %qT cannot yield a rank", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 2d1cb7c227c..85056c8140b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
+DEFTRAIT_EXPR (RANK, "__rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 45dc509855a..7242db75248 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
+    /* __rank is handled in finish_trait_expr. */
+    case CPTK_RANK:
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
   if (processing_template_decl)
     {
       tree trait_expr = make_node (TRAIT_EXPR);
-      TREE_TYPE (trait_expr) = boolean_type_node;
+      if (kind == CPTK_RANK)
+	TREE_TYPE (trait_expr) = size_type_node;
+      else
+	TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
       TRAIT_EXPR_TYPE2 (trait_expr) = type2;
       TRAIT_EXPR_KIND (trait_expr) = kind;
@@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
+    case CPTK_RANK:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  tree val = (trait_expr_value (kind, type1, type2)
-	      ? boolean_true_node : boolean_false_node);
+  tree val;
+  if (kind == CPTK_RANK)
+    {
+      size_t rank = 0;
+      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+	++rank;
+      val = build_int_cst (size_type_node, rank);
+    }
+  else
+    val = (trait_expr_value (kind, type1, type2)
+	   ? boolean_true_node : boolean_false_node);
+
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 3aca273aad6..7f7b27f7aa7 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -179,6 +179,9 @@
 #if !__has_builtin (__is_volatile)
 # error "__has_builtin (__is_volatile) failed"
 #endif
+#if !__has_builtin (__rank)
+# error "__has_builtin (__rank) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
new file mode 100644
index 00000000000..28894184387
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/rank.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <cstddef>
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__rank(int) == 0);
+SA(__rank(int[2]) == 1);
+SA(__rank(int[][4]) == 2);
+SA(__rank(int[2][2][4][4][6][6]) == 6);
+SA(__rank(ClassType) == 0);
+SA(__rank(ClassType[2]) == 1);
+SA(__rank(ClassType[][4]) == 2);
+SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
+
+template<class T> void f(T) = delete;
+void f(size_t);
+
+template<class T>
+void g() { f(__rank(T)); }
+
+template void g<int>();
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v7 22/22] libstdc++: Optimize std::rank compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (19 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 21/22] c++: Implement __rank built-in trait Ken Matsui
@ 2024-02-19 13:51     ` Ken Matsui
  2024-02-20  5:20     ` [PATCH v8 23/24] c++: Implement __is_invocable built-in trait Ken Matsui
  2024-02-20  5:20     ` [PATCH v8 24/24] libstdc++: Optimize std::is_invocable " Ken Matsui
  22 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-19 13:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::rank
by dispatching to the new __rank built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (rank): Use __rank built-in trait.
	(rank_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 2f4c8dd3b21..1577042a5b8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1473,6 +1473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// rank
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__rank)
+  template<typename _Tp>
+    struct rank
+    : public integral_constant<std::size_t, __rank(_Tp)> { };
+#else
   template<typename>
     struct rank
     : public integral_constant<std::size_t, 0> { };
@@ -1484,6 +1489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct rank<_Tp[]>
     : public integral_constant<std::size_t, 1 + rank<_Tp>::value> { };
+#endif
 
   /// extent
   template<typename, unsigned _Uint = 0>
@@ -3579,12 +3585,17 @@ template <typename _Tp>
 template <typename _Tp>
   inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__rank)
+template <typename _Tp>
+  inline constexpr size_t rank_v = __rank(_Tp);
+#else
 template <typename _Tp>
   inline constexpr size_t rank_v = 0;
 template <typename _Tp, size_t _Size>
   inline constexpr size_t rank_v<_Tp[_Size]> = 1 + rank_v<_Tp>;
 template <typename _Tp>
   inline constexpr size_t rank_v<_Tp[]> = 1 + rank_v<_Tp>;
+#endif
 
 template <typename _Tp, unsigned _Idx = 0>
   inline constexpr size_t extent_v = 0;
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v8 23/24] c++: Implement __is_invocable built-in trait
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (20 preceding siblings ...)
  2024-02-19 13:51     ` [PATCH v7 22/22] libstdc++: Optimize std::rank compilation performance Ken Matsui
@ 2024-02-20  5:20     ` Ken Matsui
  2024-02-20 15:52       ` [PATCH v9 " Ken Matsui
  2024-02-20  5:20     ` [PATCH v8 24/24] libstdc++: Optimize std::is_invocable " Ken Matsui
  22 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-20  5:20 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 132 +++++++++
 gcc/cp/semantics.cc                      |   4 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 720 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 23ea66d9c12..c87b126fdb1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 85056c8140b..6cb2b55f4ea 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 334c11396c2..261d3a71faa 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7334,6 +7334,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 98c10e6a8b5..8c7352abcde 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,138 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (fn_type == error_mark_node || arg_types == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    /* Only a pointer to data member with one argument is invocable.  */
+    return error_mark_node;
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    /* A pointer to member function with no arguments is not invocable.  */
+    return error_mark_node;
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+        {
+	  if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
+	    return error_mark_node;
+        }
+      else if (!CLASS_TYPE_P (datum_type))
+	return error_mark_node;
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  is_refwrap = true;
+		  const_tree template_info = TYPE_TEMPLATE_INFO (datum_type);
+		  const_tree template_args = TYPE_TI_ARGS (template_info);
+		  datum_type = TREE_VEC_ELT (template_args, 0);
+		}
+	    }
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr) && !is_refwrap)
+	{
+	  tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+	  const bool ptrmem_is_base_of_datum =
+	    same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+						       datum_type)
+	    || DERIVED_FROM_P (ptrmem_class_type, datum_type);
+
+	  if (!ptrmem_is_base_of_datum)
+	    {
+	      /* 1.3 & 1.6: Try to dereference datum_expr.  */
+	      datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+						 RO_UNARY_STAR, NULL_TREE,
+						 complain);
+	      /* Rebuild ptrmem_expr.  */
+	      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
+						   complain);
+	    }
+	}
+      /* 1.1 & 1.4: Otherwise.  */
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7242db75248..149c0631d62 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       break;
 
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7f7b27f7aa7..d2a7ebdf25c 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -104,6 +104,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v8 24/24] libstdc++: Optimize std::is_invocable compilation performance
  2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
                       ` (21 preceding siblings ...)
  2024-02-20  5:20     ` [PATCH v8 23/24] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-02-20  5:20     ` Ken Matsui
  2024-02-20 22:17       ` [PATCH v10 " Ken Matsui
  22 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-20  5:20 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __is_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use __is_invocable
	built-in trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                        | 6 ++++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc   | 1 +
 .../testsuite/20_util/is_invocable/incomplete_neg.cc        | 1 +
 3 files changed, 8 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1577042a5b8..51d6df5ca66 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3233,9 +3233,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
 
   /// std::is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable)
+  template<typename _Fn, typename... _ArgTypes>
+    struct is_invocable
+    : public __bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+#else
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v9 23/24] c++: Implement __is_invocable built-in trait
  2024-02-20  5:20     ` [PATCH v8 23/24] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-02-20 15:52       ` Ken Matsui
  2024-02-21  6:20         ` [PATCH v11 " Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-20 15:52 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 134 +++++++++
 gcc/cp/semantics.cc                      |   4 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 722 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 23ea66d9c12..c87b126fdb1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 85056c8140b..6cb2b55f4ea 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 334c11396c2..261d3a71faa 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7334,6 +7334,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 98c10e6a8b5..14bd70ecf06 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,140 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (fn_type == error_mark_node || arg_types == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    /* Only a pointer to data member with one argument is invocable.  */
+    return error_mark_node;
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    /* A pointer to member function with no arguments is not invocable.  */
+    return error_mark_node;
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+        {
+	  if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
+	    return error_mark_node;
+        }
+      else if (!CLASS_TYPE_P (datum_type))
+	return error_mark_node;
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  is_refwrap = true;
+		  const_tree template_info = TYPE_TEMPLATE_INFO (datum_type);
+		  const_tree template_args = TYPE_TI_ARGS (template_info);
+		  datum_type = TREE_VEC_ELT (template_args, 0);
+		}
+	    }
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr) && !is_refwrap)
+	{
+	  tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+	  const bool ptrmem_is_base_of_datum =
+	    (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+	  if (!ptrmem_is_base_of_datum)
+	    {
+	      /* 1.3 & 1.6: Try to dereference datum_expr.  */
+	      datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+						 RO_UNARY_STAR, NULL_TREE,
+						 complain);
+	      /* Rebuild ptrmem_expr.  */
+	      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
+						   complain);
+	    }
+	}
+      /* 1.1 & 1.4: Otherwise.  */
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7242db75248..149c0631d62 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       break;
 
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7f7b27f7aa7..d2a7ebdf25c 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -104,6 +104,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v10 24/24] libstdc++: Optimize std::is_invocable compilation performance
  2024-02-20  5:20     ` [PATCH v8 24/24] libstdc++: Optimize std::is_invocable " Ken Matsui
@ 2024-02-20 22:17       ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-20 22:17 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __is_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use __is_invocable
	built-in trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc     | 1 +
 libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1577042a5b8..9af233bcc75 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3235,7 +3235,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable)
+    : public __bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+#else
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v11 23/24] c++: Implement __is_invocable built-in trait
  2024-02-20 15:52       ` [PATCH v9 " Ken Matsui
@ 2024-02-21  6:20         ` Ken Matsui
  2024-02-21  8:51           ` [PATCH v12 25/26] c++: Implement __is_nothrow_invocable " Ken Matsui
  2024-02-21  8:51           ` [PATCH v12 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  0 siblings, 2 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  6:20 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 132 +++++++++
 gcc/cp/semantics.cc                      |   4 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 720 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 23ea66d9c12..c87b126fdb1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 85056c8140b..6cb2b55f4ea 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 334c11396c2..261d3a71faa 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7334,6 +7334,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 98c10e6a8b5..953f1bed6fc 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,138 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (fn_type == error_mark_node || arg_types == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    /* Only a pointer to data member with one argument is invocable.  */
+    return error_mark_node;
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    /* A pointer to member function with no arguments is not invocable.  */
+    return error_mark_node;
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+        {
+	  if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
+	    return error_mark_node;
+        }
+      else if (!CLASS_TYPE_P (datum_type))
+	return error_mark_node;
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr) && !is_refwrap)
+	{
+	  tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+	  const bool ptrmem_is_base_of_datum =
+	    (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+	  if (!ptrmem_is_base_of_datum)
+	    {
+	      /* 1.3 & 1.6: Try to dereference datum_expr.  */
+	      datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+						 RO_UNARY_STAR, NULL_TREE,
+						 complain);
+	      /* Rebuild ptrmem_expr.  */
+	      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
+						   complain);
+	    }
+	}
+      /* 1.1 & 1.4: Otherwise.  */
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7242db75248..149c0631d62 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       break;
 
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7f7b27f7aa7..d2a7ebdf25c 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -104,6 +104,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v12 25/26] c++: Implement __is_nothrow_invocable built-in trait
  2024-02-21  6:20         ` [PATCH v11 " Ken Matsui
@ 2024-02-21  8:51           ` Ken Matsui
  2024-02-21  8:51           ` [PATCH v12 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  1 sibling, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  8:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_nothrow_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_nothrow_invocable.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_NOTHROW_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/is_nothrow_invocable.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  6 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 .../g++.dg/ext/is_nothrow_invocable.C         | 66 +++++++++++++++++++
 4 files changed, 77 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c87b126fdb1..43d4f2102d6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3824,6 +3824,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
 	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
       break;
+    case CPTK_IS_NOTHROW_INVOCABLE:
+	if (!t2)
+	  inform (loc, "  %qT is not nothrow invocable", t1);
+	else
+	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
+	break;
     case CPTK_IS_OBJECT:
       inform (loc, "  %qT is not an object type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 6cb2b55f4ea..a9714921e94 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
 DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
 DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
 DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 149c0631d62..dba7b43a109 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12494,6 +12494,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
       return is_nothrow_convertible (type1, type2);
 
+    case CPTK_IS_NOTHROW_INVOCABLE:
+      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
+
     case CPTK_IS_OBJECT:
       return (type_code1 != FUNCTION_TYPE
 	      && type_code1 != REFERENCE_TYPE
@@ -12689,6 +12692,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_IS_TRIVIALLY_ASSIGNABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
new file mode 100644
index 00000000000..b95307bb3a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
@@ -0,0 +1,66 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+struct T { T(int) { } };
+struct NT { NT(int) noexcept { } };
+struct Ex { explicit Ex(int) noexcept { } };
+
+using func_type = void(*)();
+SA( ! __is_nothrow_invocable(func_type) );
+
+#if __cpp_noexcept_function_type
+using func_type_nt = void(*)() noexcept;
+SA(   __is_nothrow_invocable(func_type_nt) );
+#endif
+
+struct X { };
+using mem_type = int X::*;
+
+SA( ! __is_nothrow_invocable(mem_type) );
+SA( ! __is_nothrow_invocable(mem_type, int) );
+SA( ! __is_nothrow_invocable(mem_type, int&) );
+SA(   __is_nothrow_invocable(mem_type, X&) );
+
+using memfun_type = int (X::*)();
+
+SA( ! __is_nothrow_invocable(memfun_type) );
+SA( ! __is_nothrow_invocable(memfun_type, int) );
+SA( ! __is_nothrow_invocable(memfun_type, int&) );
+SA( ! __is_nothrow_invocable(memfun_type, X&) );
+SA( ! __is_nothrow_invocable(memfun_type, X*) );
+
+#if __cpp_noexcept_function_type
+using memfun_type_nt = int (X::*)() noexcept;
+
+SA( ! __is_nothrow_invocable(memfun_type_nt) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X*) );
+#endif
+
+struct F {
+  int& operator()();
+  long& operator()() const noexcept;
+  short& operator()(int) &&;
+  char& operator()(int) const& noexcept;
+private:
+  void operator()(int, int) noexcept;
+};
+using CF = const F;
+
+SA( ! __is_nothrow_invocable(F ) );
+SA(   __is_nothrow_invocable(CF) );
+
+SA( ! __is_nothrow_invocable(F,   int) );
+SA(   __is_nothrow_invocable(F&,  int) );
+
+SA(   __is_nothrow_invocable(CF,   int) );
+SA(   __is_nothrow_invocable(CF&,  int) );
+SA( ! __is_nothrow_invocable(F, int, int) );
+
+struct FX {
+  X operator()() const noexcept { return {}; }
+};
+SA(   __is_nothrow_invocable(FX) );
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v12 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-02-21  6:20         ` [PATCH v11 " Ken Matsui
  2024-02-21  8:51           ` [PATCH v12 25/26] c++: Implement __is_nothrow_invocable " Ken Matsui
@ 2024-02-21  8:51           ` Ken Matsui
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  8:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_nothrow_invocable by dispatching to the new
__is_nothrow_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_nothrow_invocable): Use
	__is_nothrow_invocable built-in trait.
	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
	Handle the new error from __is_nothrow_invocable.
	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
	Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
 .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 9af233bcc75..093d85a51a8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3265,8 +3265,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_nothrow_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable)
+    : public __bool_constant<__is_nothrow_invocable(_Fn, _ArgTypes...)>
+#else
     : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
 	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
index 3c225883eaf..3f8542dd366 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
index 5a728bfa03b..d3bdf08448b 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 01/26] c++: Implement __is_const built-in trait
  2024-02-21  8:51           ` [PATCH v12 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
@ 2024-02-21  9:35             ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                                 ` (25 more replies)
  0 siblings, 26 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 49de3211d4c..f32a1c78d63 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3767,6 +3767,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 57840176863..0d08900492b 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12446,6 +12446,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12688,6 +12691,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 02/26] libstdc++: Optimize std::is_const compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
                                 ` (24 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 21402fd8c13..6e9ebfb8a18 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3327,10 +3334,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 03/26] c++: Implement __is_volatile built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                                 ` (23 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index f32a1c78d63..9a7a12629e7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3861,6 +3861,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0d08900492b..41c25f43d27 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12702,6 +12705,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 04/26] libstdc++: Optimize std::is_volatile compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
                                 ` (22 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6e9ebfb8a18..60cd22b6f15 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3356,10 +3363,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 05/26] c++: Implement __is_pointer built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (2 preceding siblings ...)
  2024-02-21  9:35               ` [PATCH v13 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                                 ` (21 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 9a7a12629e7..244070d93c2 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 41c25f43d27..9dcdb06191a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12502,6 +12502,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12701,6 +12704,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..96b7a89e4f1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -125,6 +125,9 @@
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 06/26] libstdc++: Optimize std::is_pointer compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (3 preceding siblings ...)
  2024-02-21  9:35               ` [PATCH v13 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
                                 ` (20 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..210a9ea00da 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 60cd22b6f15..6407738a726 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3264,8 +3278,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 07/26] c++: Implement __is_unbounded_array built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (4 preceding siblings ...)
  2024-02-21  9:35               ` [PATCH v13 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                                 ` (19 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 244070d93c2..000df847342 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3861,6 +3861,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..05514a51c21 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9dcdb06191a..1794e83baa2 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12708,6 +12711,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 96b7a89e4f1..b1430e9bd8b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_trivially_copyable)
 # error "__has_builtin (__is_trivially_copyable) failed"
 #endif
+#if !__has_builtin (__is_unbounded_array)
+# error "__has_builtin (__is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..283a74e1a0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (5 preceding siblings ...)
  2024-02-21  9:35               ` [PATCH v13 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:35               ` [PATCH v13 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
                                 ` (18 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__is_unbounded_array built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__is_unbounded_array built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6407738a726..c4585a23df9 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3706,11 +3706,16 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v = __is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 09/26] c++: Implement __add_pointer built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (6 preceding siblings ...)
  2024-02-21  9:35               ` [PATCH v13 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-02-21  9:35               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
                                 ` (17 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_pointer.
	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __add_pointer.
	* g++.dg/ext/add_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  9 ++++++
 gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 52 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 05514a51c21..63f879287ce 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 1794e83baa2..635441a7a90 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12776,6 +12776,15 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_POINTER:
+      if (FUNC_OR_METHOD_TYPE_P (type1)
+	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+	      || type_memfn_rqual (type1) != REF_QUAL_NONE))
+	return type1;
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+      return build_pointer_type (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
new file mode 100644
index 00000000000..c405cdd0feb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_pointer(int), int*));
+SA(__is_same(__add_pointer(int*), int**));
+SA(__is_same(__add_pointer(const int), const int*));
+SA(__is_same(__add_pointer(int&), int*));
+SA(__is_same(__add_pointer(ClassType*), ClassType**));
+SA(__is_same(__add_pointer(ClassType), ClassType*));
+SA(__is_same(__add_pointer(void), void*));
+SA(__is_same(__add_pointer(const void), const void*));
+SA(__is_same(__add_pointer(volatile void), volatile void*));
+SA(__is_same(__add_pointer(const volatile void), const volatile void*));
+
+void f1();
+using f1_type = decltype(f1);
+using pf1_type = decltype(&f1);
+SA(__is_same(__add_pointer(f1_type), pf1_type));
+
+void f2() noexcept; // PR libstdc++/78361
+using f2_type = decltype(f2);
+using pf2_type = decltype(&f2);
+SA(__is_same(__add_pointer(f2_type), pf2_type));
+
+using fn_type = void();
+using pfn_type = void(*)();
+SA(__is_same(__add_pointer(fn_type), pfn_type));
+
+SA(__is_same(__add_pointer(void() &), void() &));
+SA(__is_same(__add_pointer(void() & noexcept), void() & noexcept));
+SA(__is_same(__add_pointer(void() const), void() const));
+SA(__is_same(__add_pointer(void(...) &), void(...) &));
+SA(__is_same(__add_pointer(void(...) & noexcept), void(...) & noexcept));
+SA(__is_same(__add_pointer(void(...) const), void(...) const));
+
+SA(__is_same(__add_pointer(void() __restrict), void() __restrict));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b1430e9bd8b..9d861398bae 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_pointer)
+# error "__has_builtin (__add_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 10/26] libstdc++: Optimize std::add_pointer compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (7 preceding siblings ...)
  2024-02-21  9:35               ` [PATCH v13 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
                                 ` (16 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::add_pointer
by dispatching to the new __add_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_pointer): Use __add_pointer
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index c4585a23df9..6346d1daee2 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2149,6 +2149,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 #endif
 
+  /// add_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_pointer)
+  template<typename _Tp>
+    struct add_pointer
+    { using type = __add_pointer(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_pointer_helper
     { using type = _Tp; };
@@ -2157,7 +2163,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __add_pointer_helper<_Tp, __void_t<_Tp*>>
     { using type = _Tp*; };
 
-  /// add_pointer
   template<typename _Tp>
     struct add_pointer
     : public __add_pointer_helper<_Tp>
@@ -2170,6 +2175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct add_pointer<_Tp&&>
     { using type = _Tp*; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_pointer
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 11/26] c++: Implement __remove_extent built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (8 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
                                 ` (15 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::remove_extent.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_extent.
	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __remove_extent.
	* g++.dg/ext/remove_extent.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  5 +++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
 4 files changed, 25 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 63f879287ce..577c96d579b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
+DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 635441a7a90..58696225fc4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12793,6 +12793,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return cv_unqualified (type1);
 
+    case CPTK_REMOVE_EXTENT:
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	type1 = TREE_TYPE (type1);
+      return type1;
+
     case CPTK_REMOVE_POINTER:
       if (TYPE_PTR_P (type1))
 	type1 = TREE_TYPE (type1);
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 9d861398bae..5d5cbe3b019 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -182,6 +182,9 @@
 #if !__has_builtin (__remove_cvref)
 # error "__has_builtin (__remove_cvref) failed"
 #endif
+#if !__has_builtin (__remove_extent)
+# error "__has_builtin (__remove_extent) failed"
+#endif
 #if !__has_builtin (__remove_pointer)
 # error "__has_builtin (__remove_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
new file mode 100644
index 00000000000..6183aca5a48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_extent(int), int));
+SA(__is_same(__remove_extent(int[2]), int));
+SA(__is_same(__remove_extent(int[2][3]), int[3]));
+SA(__is_same(__remove_extent(int[][3]), int[3]));
+SA(__is_same(__remove_extent(const int[2]), const int));
+SA(__is_same(__remove_extent(ClassType), ClassType));
+SA(__is_same(__remove_extent(ClassType[2]), ClassType));
+SA(__is_same(__remove_extent(ClassType[2][3]), ClassType[3]));
+SA(__is_same(__remove_extent(ClassType[][3]), ClassType[3]));
+SA(__is_same(__remove_extent(const ClassType[2]), const ClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 12/26] libstdc++: Optimize std::remove_extent compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (9 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
                                 ` (14 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::remove_extent
by dispatching to the new __remove_extent built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_extent): Use __remove_extent
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6346d1daee2..73ddce351fd 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2092,6 +2092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Array modifications.
 
   /// remove_extent
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_extent)
+  template<typename _Tp>
+    struct remove_extent
+    { using type = __remove_extent(_Tp); };
+#else
   template<typename _Tp>
     struct remove_extent
     { using type = _Tp; };
@@ -2103,6 +2108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_extent<_Tp[]>
     { using type = _Tp; };
+#endif
 
   /// remove_all_extents
   template<typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 13/26] c++: Implement __remove_all_extents built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (10 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
                                 ` (13 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::remove_all_extents.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_all_extents.
	* semantics.cc (finish_trait_type): Handle
	CPTK_REMOVE_ALL_EXTENTS.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__remove_all_extents.
	* g++.dg/ext/remove_all_extents.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  3 +++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 577c96d579b..933c8bcbe68 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 58696225fc4..078424dac23 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12785,6 +12785,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return build_pointer_type (type1);
 
+    case CPTK_REMOVE_ALL_EXTENTS:
+      return strip_array_types (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 5d5cbe3b019..85b74bd676b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -176,6 +176,9 @@
 #if !__has_builtin (__reference_converts_from_temporary)
 # error "__has_builtin (__reference_converts_from_temporary) failed"
 #endif
+#if !__has_builtin (__remove_all_extents)
+# error "__has_builtin (__remove_all_extents) failed"
+#endif
 #if !__has_builtin (__remove_cv)
 # error "__has_builtin (__remove_cv) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
new file mode 100644
index 00000000000..60ade2ade7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_all_extents(int), int));
+SA(__is_same(__remove_all_extents(int[2]), int));
+SA(__is_same(__remove_all_extents(int[2][3]), int));
+SA(__is_same(__remove_all_extents(int[][3]), int));
+SA(__is_same(__remove_all_extents(const int[2][3]), const int));
+SA(__is_same(__remove_all_extents(ClassType), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2][3]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[][3]), ClassType));
+SA(__is_same(__remove_all_extents(const ClassType[2][3]), const ClassType));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 14/26] libstdc++: Optimize std::remove_all_extents compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (11 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
                                 ` (12 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::remove_all_extents by dispatching to the new __remove_all_extents
built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_all_extents): Use
	__remove_all_extents built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 73ddce351fd..34475e6279a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2111,6 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// remove_all_extents
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_all_extents)
+  template<typename _Tp>
+    struct remove_all_extents
+    { using type = __remove_all_extents(_Tp); };
+#else
   template<typename _Tp>
     struct remove_all_extents
     { using type = _Tp; };
@@ -2122,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_all_extents<_Tp[]>
     { using type = typename remove_all_extents<_Tp>::type; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_extent
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 15/26] c++: Implement __add_lvalue_reference built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (12 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
                                 ` (11 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_lvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_lvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_LVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_lvalue_reference.
	* g++.dg/ext/add_lvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  8 +++++++
 .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 33 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 933c8bcbe68..9a27dca4ea3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 078424dac23..05f5b62f9df 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12776,6 +12776,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_LVALUE_REFERENCE:
+      if (VOID_TYPE_P (type1)
+	  || (FUNC_OR_METHOD_TYPE_P (type1)
+	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
+	return type1;
+      return cp_build_reference_type (type1, /*rval=*/false);
+
     case CPTK_ADD_POINTER:
       if (FUNC_OR_METHOD_TYPE_P (type1)
 	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
new file mode 100644
index 00000000000..8fe1e0300e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_lvalue_reference(int), int&));
+SA(__is_same(__add_lvalue_reference(int&), int&));
+SA(__is_same(__add_lvalue_reference(const int), const int&));
+SA(__is_same(__add_lvalue_reference(int*), int*&));
+SA(__is_same(__add_lvalue_reference(ClassType&), ClassType&));
+SA(__is_same(__add_lvalue_reference(ClassType), ClassType&));
+SA(__is_same(__add_lvalue_reference(int(int)), int(&)(int)));
+SA(__is_same(__add_lvalue_reference(int&&), int&));
+SA(__is_same(__add_lvalue_reference(ClassType&&), ClassType&));
+SA(__is_same(__add_lvalue_reference(void), void));
+SA(__is_same(__add_lvalue_reference(const void), const void));
+SA(__is_same(__add_lvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_lvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_lvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_lvalue_reference(bool(int)), bool(&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 85b74bd676b..3fca9cfabcc 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_lvalue_reference)
+# error "__has_builtin (__add_lvalue_reference) failed"
+#endif
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (13 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
                                 ` (10 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::add_lvalue_reference by dispatching to the new
__add_lvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_lvalue_reference): Use
	__add_lvalue_reference built-in trait.
	(__add_lvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 34475e6279a..17bf47d59d3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1157,6 +1157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct __add_lvalue_reference_helper
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_lvalue_reference_helper
     { using type = _Tp; };
@@ -1164,6 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_lvalue_reference_helper<_Tp, __void_t<_Tp&>>
     { using type = _Tp&; };
+#endif
 
   template<typename _Tp>
     using __add_lval_ref_t = typename __add_lvalue_reference_helper<_Tp>::type;
@@ -1731,9 +1737,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_lvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct add_lvalue_reference
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_lvalue_reference
     { using type = __add_lval_ref_t<_Tp>; };
+#endif
 
   /// add_rvalue_reference
   template<typename _Tp>
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 17/26] c++: Implement __add_rvalue_reference built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (14 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
                                 ` (9 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_rvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_rvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_RVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_rvalue_reference.
	* g++.dg/ext/add_rvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  8 ++++++++
 .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 32 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 9a27dca4ea3..173818adf79 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -50,6 +50,7 @@
 
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
+DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 05f5b62f9df..19d6f87a9ea 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12793,6 +12793,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return build_pointer_type (type1);
 
+    case CPTK_ADD_RVALUE_REFERENCE:
+      if (VOID_TYPE_P (type1)
+	  || (FUNC_OR_METHOD_TYPE_P (type1)
+	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
+	return type1;
+      return cp_build_reference_type (type1, /*rval=*/true);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
new file mode 100644
index 00000000000..c92fe6bfa17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_rvalue_reference(int), int&&));
+SA(__is_same(__add_rvalue_reference(int&&), int&&));
+SA(__is_same(__add_rvalue_reference(int&), int&));
+SA(__is_same(__add_rvalue_reference(const int), const int&&));
+SA(__is_same(__add_rvalue_reference(int*), int*&&));
+SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+SA(__is_same(__add_rvalue_reference(void), void));
+SA(__is_same(__add_rvalue_reference(const void), const void));
+SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 3fca9cfabcc..c2503c5d82b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -8,6 +8,9 @@
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
+#if !__has_builtin (__add_rvalue_reference)
+# error "__has_builtin (__add_rvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (15 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 19/26] c++: Implement __decay built-in trait Ken Matsui
                                 ` (8 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::add_rvalue_reference by dispatching to the new
__add_rvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_rvalue_reference): Use
	__add_rvalue_reference built-in trait.
	(__add_rvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 17bf47d59d3..18a5e4de2d3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1185,6 +1185,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct __add_rvalue_reference_helper
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_rvalue_reference_helper
     { using type = _Tp; };
@@ -1192,6 +1197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_rvalue_reference_helper<_Tp, __void_t<_Tp&&>>
     { using type = _Tp&&; };
+#endif
 
   template<typename _Tp>
     using __add_rval_ref_t = typename __add_rvalue_reference_helper<_Tp>::type;
@@ -1748,9 +1754,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_rvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct add_rvalue_reference
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_rvalue_reference
     { using type = __add_rval_ref_t<_Tp>; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_reference
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 19/26] c++: Implement __decay built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (16 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
                                 ` (7 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::decay.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __decay.
	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __decay.
	* g++.dg/ext/decay.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 12 ++++++++
 gcc/testsuite/g++.dg/ext/decay.C         | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 55 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 173818adf79..2d1cb7c227c 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -51,6 +51,7 @@
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
+DEFTRAIT_TYPE (DECAY, "__decay", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 19d6f87a9ea..45dc509855a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12801,6 +12801,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	return type1;
       return cp_build_reference_type (type1, /*rval=*/true);
 
+    case CPTK_DECAY:
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
+				  complain);
+      else if (TREE_CODE (type1) == FUNCTION_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
+      else
+	return cv_unqualified (type1);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
new file mode 100644
index 00000000000..cf224b7452c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/decay.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+// class ClassType { };
+
+// Positive tests.
+using test1_type = __decay(bool);
+SA(__is_same(test1_type, bool));
+
+// NB: DR 705.
+using test2_type = __decay(const int);
+SA(__is_same(test2_type, int));
+
+using test3_type = __decay(int[4]);
+SA(__is_same(test3_type, __remove_extent(int[4])*));
+
+using fn_type = void ();
+using test4_type = __decay(fn_type);
+SA(__is_same(test4_type, __add_pointer(fn_type)));
+
+using cfn_type = void () const;
+using test5_type = __decay(cfn_type);
+SA(__is_same(test5_type, cfn_type));
+
+// SA(__is_same(__add_rvalue_reference(int), int&&));
+// SA(__is_same(__add_rvalue_reference(int&&), int&&));
+// SA(__is_same(__add_rvalue_reference(int&), int&));
+// SA(__is_same(__add_rvalue_reference(const int), const int&&));
+// SA(__is_same(__add_rvalue_reference(int*), int*&&));
+// SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+// SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+// SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+// SA(__is_same(__add_rvalue_reference(void), void));
+// SA(__is_same(__add_rvalue_reference(const void), const void));
+// SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+// SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+// SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+// SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index c2503c5d82b..3aca273aad6 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -11,6 +11,9 @@
 #if !__has_builtin (__add_rvalue_reference)
 # error "__has_builtin (__add_rvalue_reference) failed"
 #endif
+#if !__has_builtin (__decay)
+# error "__has_builtin (__decay) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 20/26] libstdc++: Optimize std::decay compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (17 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 19/26] c++: Implement __decay built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 21/26] c++: Implement __rank built-in trait Ken Matsui
                                 ` (6 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::decay
by dispatching to the new __decay built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (decay): Use __decay built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 18a5e4de2d3..2f4c8dd3b21 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2316,6 +2316,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @cond undocumented
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__decay)
+  template<typename _Tp>
+    struct decay
+    { using type = __decay(_Tp); };
+#else
   // Decay trait for arrays and functions, used for perfect forwarding
   // in make_pair, make_tuple, etc.
   template<typename _Up>
@@ -2347,6 +2352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct decay<_Tp&&>
     { using type = typename __decay_selector<_Tp>::type; };
+#endif
 
   /// @cond undocumented
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 21/26] c++: Implement __rank built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (18 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
                                 ` (5 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::rank.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __rank.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __rank.
	* g++.dg/ext/rank.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
 5 files changed, 51 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 000df847342..23ea66d9c12 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
+    case CPTK_RANK:
+      inform (loc, "  %qT cannot yield a rank", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 2d1cb7c227c..85056c8140b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
+DEFTRAIT_EXPR (RANK, "__rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 45dc509855a..7242db75248 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
+    /* __rank is handled in finish_trait_expr. */
+    case CPTK_RANK:
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
   if (processing_template_decl)
     {
       tree trait_expr = make_node (TRAIT_EXPR);
-      TREE_TYPE (trait_expr) = boolean_type_node;
+      if (kind == CPTK_RANK)
+	TREE_TYPE (trait_expr) = size_type_node;
+      else
+	TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
       TRAIT_EXPR_TYPE2 (trait_expr) = type2;
       TRAIT_EXPR_KIND (trait_expr) = kind;
@@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
+    case CPTK_RANK:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  tree val = (trait_expr_value (kind, type1, type2)
-	      ? boolean_true_node : boolean_false_node);
+  tree val;
+  if (kind == CPTK_RANK)
+    {
+      size_t rank = 0;
+      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+	++rank;
+      val = build_int_cst (size_type_node, rank);
+    }
+  else
+    val = (trait_expr_value (kind, type1, type2)
+	   ? boolean_true_node : boolean_false_node);
+
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 3aca273aad6..7f7b27f7aa7 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -179,6 +179,9 @@
 #if !__has_builtin (__is_volatile)
 # error "__has_builtin (__is_volatile) failed"
 #endif
+#if !__has_builtin (__rank)
+# error "__has_builtin (__rank) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
new file mode 100644
index 00000000000..28894184387
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/rank.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <cstddef>
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__rank(int) == 0);
+SA(__rank(int[2]) == 1);
+SA(__rank(int[][4]) == 2);
+SA(__rank(int[2][2][4][4][6][6]) == 6);
+SA(__rank(ClassType) == 0);
+SA(__rank(ClassType[2]) == 1);
+SA(__rank(ClassType[][4]) == 2);
+SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
+
+template<class T> void f(T) = delete;
+void f(size_t);
+
+template<class T>
+void g() { f(__rank(T)); }
+
+template void g<int>();
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 22/26] libstdc++: Optimize std::rank compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (19 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 21/26] c++: Implement __rank built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
                                 ` (4 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::rank
by dispatching to the new __rank built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (rank): Use __rank built-in trait.
	(rank_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 2f4c8dd3b21..1577042a5b8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1473,6 +1473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// rank
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__rank)
+  template<typename _Tp>
+    struct rank
+    : public integral_constant<std::size_t, __rank(_Tp)> { };
+#else
   template<typename>
     struct rank
     : public integral_constant<std::size_t, 0> { };
@@ -1484,6 +1489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct rank<_Tp[]>
     : public integral_constant<std::size_t, 1 + rank<_Tp>::value> { };
+#endif
 
   /// extent
   template<typename, unsigned _Uint = 0>
@@ -3579,12 +3585,17 @@ template <typename _Tp>
 template <typename _Tp>
   inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__rank)
+template <typename _Tp>
+  inline constexpr size_t rank_v = __rank(_Tp);
+#else
 template <typename _Tp>
   inline constexpr size_t rank_v = 0;
 template <typename _Tp, size_t _Size>
   inline constexpr size_t rank_v<_Tp[_Size]> = 1 + rank_v<_Tp>;
 template <typename _Tp>
   inline constexpr size_t rank_v<_Tp[]> = 1 + rank_v<_Tp>;
+#endif
 
 template <typename _Tp, unsigned _Idx = 0>
   inline constexpr size_t extent_v = 0;
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 23/26] c++: Implement __is_invocable built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (20 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
                                 ` (3 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 132 +++++++++
 gcc/cp/semantics.cc                      |   4 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 720 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 23ea66d9c12..c87b126fdb1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 85056c8140b..6cb2b55f4ea 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 334c11396c2..261d3a71faa 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7334,6 +7334,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 98c10e6a8b5..953f1bed6fc 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,138 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (fn_type == error_mark_node || arg_types == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    /* Only a pointer to data member with one argument is invocable.  */
+    return error_mark_node;
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    /* A pointer to member function with no arguments is not invocable.  */
+    return error_mark_node;
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+        {
+	  if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
+	    return error_mark_node;
+        }
+      else if (!CLASS_TYPE_P (datum_type))
+	return error_mark_node;
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr) && !is_refwrap)
+	{
+	  tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+	  const bool ptrmem_is_base_of_datum =
+	    (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+	  if (!ptrmem_is_base_of_datum)
+	    {
+	      /* 1.3 & 1.6: Try to dereference datum_expr.  */
+	      datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+						 RO_UNARY_STAR, NULL_TREE,
+						 complain);
+	      /* Rebuild ptrmem_expr.  */
+	      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
+						   complain);
+	    }
+	}
+      /* 1.1 & 1.4: Otherwise.  */
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7242db75248..149c0631d62 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       break;
 
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7f7b27f7aa7..d2a7ebdf25c 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -104,6 +104,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 24/26] libstdc++: Optimize std::is_invocable compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (21 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
                                 ` (2 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __is_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use __is_invocable
	built-in trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc     | 1 +
 libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1577042a5b8..9af233bcc75 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3235,7 +3235,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable)
+    : public __bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+#else
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 25/26] c++: Implement __is_nothrow_invocable built-in trait
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (22 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-21  9:36               ` [PATCH v13 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_nothrow_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_nothrow_invocable.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_NOTHROW_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_nothrow_invocable.
	* g++.dg/ext/is_nothrow_invocable.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  6 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +
 .../g++.dg/ext/is_nothrow_invocable.C         | 66 +++++++++++++++++++
 5 files changed, 80 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c87b126fdb1..43d4f2102d6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3824,6 +3824,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
 	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
       break;
+    case CPTK_IS_NOTHROW_INVOCABLE:
+	if (!t2)
+	  inform (loc, "  %qT is not nothrow invocable", t1);
+	else
+	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
+	break;
     case CPTK_IS_OBJECT:
       inform (loc, "  %qT is not an object type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 6cb2b55f4ea..a9714921e94 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
 DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
 DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
 DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 149c0631d62..dba7b43a109 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12494,6 +12494,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
       return is_nothrow_convertible (type1, type2);
 
+    case CPTK_IS_NOTHROW_INVOCABLE:
+      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
+
     case CPTK_IS_OBJECT:
       return (type_code1 != FUNCTION_TYPE
 	      && type_code1 != REFERENCE_TYPE
@@ -12689,6 +12692,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_IS_TRIVIALLY_ASSIGNABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index d2a7ebdf25c..624d3525f27 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -131,6 +131,9 @@
 #if !__has_builtin (__is_nothrow_convertible)
 # error "__has_builtin (__is_nothrow_convertible) failed"
 #endif
+#if !__has_builtin (__is_nothrow_invocable)
+# error "__has_builtin (__is_nothrow_invocable) failed"
+#endif
 #if !__has_builtin (__is_object)
 # error "__has_builtin (__is_object) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
new file mode 100644
index 00000000000..b95307bb3a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
@@ -0,0 +1,66 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+struct T { T(int) { } };
+struct NT { NT(int) noexcept { } };
+struct Ex { explicit Ex(int) noexcept { } };
+
+using func_type = void(*)();
+SA( ! __is_nothrow_invocable(func_type) );
+
+#if __cpp_noexcept_function_type
+using func_type_nt = void(*)() noexcept;
+SA(   __is_nothrow_invocable(func_type_nt) );
+#endif
+
+struct X { };
+using mem_type = int X::*;
+
+SA( ! __is_nothrow_invocable(mem_type) );
+SA( ! __is_nothrow_invocable(mem_type, int) );
+SA( ! __is_nothrow_invocable(mem_type, int&) );
+SA(   __is_nothrow_invocable(mem_type, X&) );
+
+using memfun_type = int (X::*)();
+
+SA( ! __is_nothrow_invocable(memfun_type) );
+SA( ! __is_nothrow_invocable(memfun_type, int) );
+SA( ! __is_nothrow_invocable(memfun_type, int&) );
+SA( ! __is_nothrow_invocable(memfun_type, X&) );
+SA( ! __is_nothrow_invocable(memfun_type, X*) );
+
+#if __cpp_noexcept_function_type
+using memfun_type_nt = int (X::*)() noexcept;
+
+SA( ! __is_nothrow_invocable(memfun_type_nt) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X*) );
+#endif
+
+struct F {
+  int& operator()();
+  long& operator()() const noexcept;
+  short& operator()(int) &&;
+  char& operator()(int) const& noexcept;
+private:
+  void operator()(int, int) noexcept;
+};
+using CF = const F;
+
+SA( ! __is_nothrow_invocable(F ) );
+SA(   __is_nothrow_invocable(CF) );
+
+SA( ! __is_nothrow_invocable(F,   int) );
+SA(   __is_nothrow_invocable(F&,  int) );
+
+SA(   __is_nothrow_invocable(CF,   int) );
+SA(   __is_nothrow_invocable(CF&,  int) );
+SA( ! __is_nothrow_invocable(F, int, int) );
+
+struct FX {
+  X operator()() const noexcept { return {}; }
+};
+SA(   __is_nothrow_invocable(FX) );
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v13 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (23 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
@ 2024-02-21  9:36               ` Ken Matsui
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-21  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_nothrow_invocable by dispatching to the new
__is_nothrow_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_nothrow_invocable): Use
	__is_nothrow_invocable built-in trait.
	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
	Handle the new error from __is_nothrow_invocable.
	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
	Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
 .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 9af233bcc75..093d85a51a8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3265,8 +3265,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_nothrow_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable)
+    : public __bool_constant<__is_nothrow_invocable(_Fn, _ArgTypes...)>
+#else
     : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
 	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
index 3c225883eaf..3f8542dd366 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
index 5a728bfa03b..d3bdf08448b 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 00/26] Optimize more type traits
  2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                 ` (24 preceding siblings ...)
  2024-02-21  9:36               ` [PATCH v13 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
@ 2024-02-28 19:26               ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                   ` (27 more replies)
  25 siblings, 28 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

Hi,

This patch series implements __is_const, __is_volatile, __is_pointer,
and __is_unbounded_array built-in traits, which were isolated from my
previous patch series "Optimize type traits compilation performance"
because they contained performance regression.  I confirmed that this
patch series does not cause any performance regression.  The main reason
of the performance regression were the exhaustiveness of the benchmarks
and the instability of the benchmark results.  Also, this patch series
includes built-ins for add_pointer, remove_extent, remove_all_extents,
add_lvalue_reference, add_rvalue_reference, decay, rank, is_invocable,
and is_nothrow_invocable.  Here are the benchmark results:

is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023
time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%

is_const_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_const_v.md#sat-jun-24-044815-am-pdt-2023
time: -2.86467%, peak memory: -1.0654%, total memory: -1.62369%

is_volatile: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile.md#sun-oct-22-091644-pm-pdt-2023
time: -5.25164%, peak memory: -0.337971%, total memory: -0.247934%

is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023
time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%

is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023
time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%

is_pointer_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer_v.md#sun-oct-22-122257-am-pdt-2023
time: -4.71336%, peak memory: -2.25026%, total memory: -3.125%

is_unbounded_array: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array.md#sun-oct-22-091644-pm-pdt-2023
time: -6.33287%, peak memory: -0.602494%, total memory: -1.56035%

is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023
time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%

add_pointer_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_pointer_t.md#wed-feb-28-060044-am-pst-2024
time: -21.6673%, peak memory: -14.6666%, total memory: -17.4716%

remove_extent_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_extent_t.md#wed-feb-28-063021-am-pst-2024
time: -14.4089%, peak memory: -2.71836%, total memory: -9.87013%

remove_all_extents_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_all_extents_t.md#wed-feb-28-064716-am-pst-2024
time: -28.8941%, peak memory: -16.6981%, total memory: -23.6088%

add_lvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_lvalue_reference_t.md#wed-feb-28-070023-am-pst-2024
time: -33.8827%, peak memory: -24.9292%, total memory: -25.3043%

add_rvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_rvalue_reference_t.md#wed-feb-28-070701-am-pst-2024
time: -23.9186%, peak memory: -17.1311%, total memory: -19.5891%

decay_t: https://github.com/ken-matsui/gcc-bench/blob/main/decay_t.md#wed-feb-28-072330-am-pst-2024
time: -42.4076%, peak memory: -29.2077%, total memory: -33.0914%

rank: https://github.com/ken-matsui/gcc-bench/blob/main/rank.md#wed-feb-28-074917-am-pst-2024
time: -33.7312%, peak memory: -27.5885%, total memory: -34.5736%

rank_v: https://github.com/ken-matsui/gcc-bench/blob/main/rank_v.md#wed-feb-28-073632-am-pst-2024
time: -40.7174%, peak memory: -16.4653%, total memory: -23.0131%

is_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_invocable.md#wed-feb-28-111001-am-pst-2024
time: -58.8307%, peak memory: -59.4966%, total memory: -59.8871%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

is_nothrow_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_nothrow_invocable.md#wed-feb-28-112414-am-pst-2024
time: -70.4102%, peak memory: -62.5516%, total memory: -65.5853%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

Sincerely,
Ken Matsui

Ken Matsui (26):
  c++: Implement __is_const built-in trait
  libstdc++: Optimize std::is_const compilation performance
  c++: Implement __is_volatile built-in trait
  libstdc++: Optimize std::is_volatile compilation performance
  c++: Implement __is_pointer built-in trait
  libstdc++: Optimize std::is_pointer compilation performance
  c++: Implement __is_unbounded_array built-in trait
  libstdc++: Optimize std::is_unbounded_array compilation performance
  c++: Implement __add_pointer built-in trait
  libstdc++: Optimize std::add_pointer compilation performance
  c++: Implement __remove_extent built-in trait
  libstdc++: Optimize std::remove_extent compilation performance
  c++: Implement __remove_all_extents built-in trait
  libstdc++: Optimize std::remove_all_extents compilation performance
  c++: Implement __add_lvalue_reference built-in trait
  libstdc++: Optimize std::add_lvalue_reference compilation performance
  c++: Implement __add_rvalue_reference built-in trait
  libstdc++: Optimize std::add_rvalue_reference compilation performance
  c++: Implement __decay built-in trait
  libstdc++: Optimize std::decay compilation performance
  c++: Implement __rank built-in trait
  libstdc++: Optimize std::rank compilation performance
  c++: Implement __is_invocable built-in trait
  libstdc++: Optimize std::is_invocable compilation performance
  c++: Implement __is_nothrow_invocable built-in trait
  libstdc++: Optimize std::is_nothrow_invocable compilation performance

 gcc/cp/constraint.cc                          |  27 ++
 gcc/cp/cp-trait.def                           |  13 +
 gcc/cp/cp-tree.h                              |   2 +
 gcc/cp/method.cc                              | 132 +++++++
 gcc/cp/semantics.cc                           |  92 ++++-
 .../g++.dg/ext/add_lvalue_reference.C         |  21 ++
 gcc/testsuite/g++.dg/ext/add_pointer.C        |  39 ++
 .../g++.dg/ext/add_rvalue_reference.C         |  20 +
 gcc/testsuite/g++.dg/ext/decay.C              |  22 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  39 ++
 gcc/testsuite/g++.dg/ext/is_const.C           |  20 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C      | 349 ++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C      | 139 +++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C      |  51 +++
 gcc/testsuite/g++.dg/ext/is_invocable4.C      |  33 ++
 .../g++.dg/ext/is_nothrow_invocable.C         |  62 ++++
 gcc/testsuite/g++.dg/ext/is_pointer.C         |  51 +++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C |  37 ++
 gcc/testsuite/g++.dg/ext/is_volatile.C        |  20 +
 gcc/testsuite/g++.dg/ext/rank.C               |  24 ++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C |  16 +
 gcc/testsuite/g++.dg/ext/remove_extent.C      |  16 +
 libstdc++-v3/include/bits/cpp_type_traits.h   |  31 +-
 libstdc++-v3/include/std/type_traits          | 142 ++++++-
 .../is_invocable/incomplete_args_neg.cc       |   1 +
 .../20_util/is_invocable/incomplete_neg.cc    |   1 +
 .../incomplete_args_neg.cc                    |   1 +
 .../is_nothrow_invocable/incomplete_neg.cc    |   1 +
 28 files changed, 1389 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 01/26] c++: Implement __is_const built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 20:54                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                                   ` (26 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 49de3211d4c..f32a1c78d63 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3767,6 +3767,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 57840176863..0d08900492b 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12446,6 +12446,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12688,6 +12691,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 02/26] libstdc++: Optimize std::is_const compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 01/26] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
                                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 21402fd8c13..6e9ebfb8a18 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3327,10 +3334,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 03/26] c++: Implement __is_volatile built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 01/26] c++: Implement __is_const built-in trait Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 20:54                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                                   ` (24 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index f32a1c78d63..9a7a12629e7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3861,6 +3861,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0d08900492b..41c25f43d27 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12702,6 +12705,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 04/26] libstdc++: Optimize std::is_volatile compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (2 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
                                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6e9ebfb8a18..60cd22b6f15 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3356,10 +3363,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 05/26] c++: Implement __is_pointer built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (3 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 20:56                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                                   ` (22 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 9a7a12629e7..244070d93c2 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 41c25f43d27..9dcdb06191a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12502,6 +12502,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12701,6 +12704,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..96b7a89e4f1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -125,6 +125,9 @@
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 06/26] libstdc++: Optimize std::is_pointer compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (4 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
                                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..210a9ea00da 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 60cd22b6f15..6407738a726 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3264,8 +3278,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 07/26] c++: Implement __is_unbounded_array built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (5 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 20:56                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                                   ` (20 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 244070d93c2..000df847342 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3861,6 +3861,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..05514a51c21 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9dcdb06191a..1794e83baa2 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12708,6 +12711,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 96b7a89e4f1..b1430e9bd8b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_trivially_copyable)
 # error "__has_builtin (__is_trivially_copyable) failed"
 #endif
+#if !__has_builtin (__is_unbounded_array)
+# error "__has_builtin (__is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..283a74e1a0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (6 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
                                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__is_unbounded_array built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__is_unbounded_array built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6407738a726..c4585a23df9 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3706,11 +3706,16 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v = __is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 09/26] c++: Implement __add_pointer built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (7 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 20:58                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
                                   ` (18 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_pointer.
	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __add_pointer.
	* g++.dg/ext/add_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  9 ++++++
 gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 52 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 05514a51c21..63f879287ce 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 1794e83baa2..635441a7a90 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12776,6 +12776,15 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_POINTER:
+      if (FUNC_OR_METHOD_TYPE_P (type1)
+	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+	      || type_memfn_rqual (type1) != REF_QUAL_NONE))
+	return type1;
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+      return build_pointer_type (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
new file mode 100644
index 00000000000..c405cdd0feb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_pointer(int), int*));
+SA(__is_same(__add_pointer(int*), int**));
+SA(__is_same(__add_pointer(const int), const int*));
+SA(__is_same(__add_pointer(int&), int*));
+SA(__is_same(__add_pointer(ClassType*), ClassType**));
+SA(__is_same(__add_pointer(ClassType), ClassType*));
+SA(__is_same(__add_pointer(void), void*));
+SA(__is_same(__add_pointer(const void), const void*));
+SA(__is_same(__add_pointer(volatile void), volatile void*));
+SA(__is_same(__add_pointer(const volatile void), const volatile void*));
+
+void f1();
+using f1_type = decltype(f1);
+using pf1_type = decltype(&f1);
+SA(__is_same(__add_pointer(f1_type), pf1_type));
+
+void f2() noexcept; // PR libstdc++/78361
+using f2_type = decltype(f2);
+using pf2_type = decltype(&f2);
+SA(__is_same(__add_pointer(f2_type), pf2_type));
+
+using fn_type = void();
+using pfn_type = void(*)();
+SA(__is_same(__add_pointer(fn_type), pfn_type));
+
+SA(__is_same(__add_pointer(void() &), void() &));
+SA(__is_same(__add_pointer(void() & noexcept), void() & noexcept));
+SA(__is_same(__add_pointer(void() const), void() const));
+SA(__is_same(__add_pointer(void(...) &), void(...) &));
+SA(__is_same(__add_pointer(void(...) & noexcept), void(...) & noexcept));
+SA(__is_same(__add_pointer(void(...) const), void(...) const));
+
+SA(__is_same(__add_pointer(void() __restrict), void() __restrict));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b1430e9bd8b..9d861398bae 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_pointer)
+# error "__has_builtin (__add_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 10/26] libstdc++: Optimize std::add_pointer compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (8 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
                                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::add_pointer
by dispatching to the new __add_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_pointer): Use __add_pointer
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index c4585a23df9..6346d1daee2 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2149,6 +2149,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 #endif
 
+  /// add_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_pointer)
+  template<typename _Tp>
+    struct add_pointer
+    { using type = __add_pointer(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_pointer_helper
     { using type = _Tp; };
@@ -2157,7 +2163,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __add_pointer_helper<_Tp, __void_t<_Tp*>>
     { using type = _Tp*; };
 
-  /// add_pointer
   template<typename _Tp>
     struct add_pointer
     : public __add_pointer_helper<_Tp>
@@ -2170,6 +2175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct add_pointer<_Tp&&>
     { using type = _Tp*; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_pointer
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 11/26] c++: Implement __remove_extent built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (9 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 20:59                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
                                   ` (16 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::remove_extent.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_extent.
	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __remove_extent.
	* g++.dg/ext/remove_extent.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  5 +++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
 4 files changed, 25 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 63f879287ce..577c96d579b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
+DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 635441a7a90..58696225fc4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12793,6 +12793,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return cv_unqualified (type1);
 
+    case CPTK_REMOVE_EXTENT:
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	type1 = TREE_TYPE (type1);
+      return type1;
+
     case CPTK_REMOVE_POINTER:
       if (TYPE_PTR_P (type1))
 	type1 = TREE_TYPE (type1);
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 9d861398bae..5d5cbe3b019 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -182,6 +182,9 @@
 #if !__has_builtin (__remove_cvref)
 # error "__has_builtin (__remove_cvref) failed"
 #endif
+#if !__has_builtin (__remove_extent)
+# error "__has_builtin (__remove_extent) failed"
+#endif
 #if !__has_builtin (__remove_pointer)
 # error "__has_builtin (__remove_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
new file mode 100644
index 00000000000..6183aca5a48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_extent(int), int));
+SA(__is_same(__remove_extent(int[2]), int));
+SA(__is_same(__remove_extent(int[2][3]), int[3]));
+SA(__is_same(__remove_extent(int[][3]), int[3]));
+SA(__is_same(__remove_extent(const int[2]), const int));
+SA(__is_same(__remove_extent(ClassType), ClassType));
+SA(__is_same(__remove_extent(ClassType[2]), ClassType));
+SA(__is_same(__remove_extent(ClassType[2][3]), ClassType[3]));
+SA(__is_same(__remove_extent(ClassType[][3]), ClassType[3]));
+SA(__is_same(__remove_extent(const ClassType[2]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 12/26] libstdc++: Optimize std::remove_extent compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (10 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
                                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::remove_extent
by dispatching to the new __remove_extent built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_extent): Use __remove_extent
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6346d1daee2..73ddce351fd 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2092,6 +2092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Array modifications.
 
   /// remove_extent
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_extent)
+  template<typename _Tp>
+    struct remove_extent
+    { using type = __remove_extent(_Tp); };
+#else
   template<typename _Tp>
     struct remove_extent
     { using type = _Tp; };
@@ -2103,6 +2108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_extent<_Tp[]>
     { using type = _Tp; };
+#endif
 
   /// remove_all_extents
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 13/26] c++: Implement __remove_all_extents built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (11 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 21:00                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
                                   ` (14 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::remove_all_extents.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_all_extents.
	* semantics.cc (finish_trait_type): Handle
	CPTK_REMOVE_ALL_EXTENTS.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__remove_all_extents.
	* g++.dg/ext/remove_all_extents.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  3 +++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 577c96d579b..933c8bcbe68 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 58696225fc4..078424dac23 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12785,6 +12785,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return build_pointer_type (type1);
 
+    case CPTK_REMOVE_ALL_EXTENTS:
+      return strip_array_types (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 5d5cbe3b019..85b74bd676b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -176,6 +176,9 @@
 #if !__has_builtin (__reference_converts_from_temporary)
 # error "__has_builtin (__reference_converts_from_temporary) failed"
 #endif
+#if !__has_builtin (__remove_all_extents)
+# error "__has_builtin (__remove_all_extents) failed"
+#endif
 #if !__has_builtin (__remove_cv)
 # error "__has_builtin (__remove_cv) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
new file mode 100644
index 00000000000..60ade2ade7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_all_extents(int), int));
+SA(__is_same(__remove_all_extents(int[2]), int));
+SA(__is_same(__remove_all_extents(int[2][3]), int));
+SA(__is_same(__remove_all_extents(int[][3]), int));
+SA(__is_same(__remove_all_extents(const int[2][3]), const int));
+SA(__is_same(__remove_all_extents(ClassType), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2][3]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[][3]), ClassType));
+SA(__is_same(__remove_all_extents(const ClassType[2][3]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 14/26] libstdc++: Optimize std::remove_all_extents compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (12 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
                                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::remove_all_extents by dispatching to the new __remove_all_extents
built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_all_extents): Use
	__remove_all_extents built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 73ddce351fd..34475e6279a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2111,6 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// remove_all_extents
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_all_extents)
+  template<typename _Tp>
+    struct remove_all_extents
+    { using type = __remove_all_extents(_Tp); };
+#else
   template<typename _Tp>
     struct remove_all_extents
     { using type = _Tp; };
@@ -2122,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_all_extents<_Tp[]>
     { using type = typename remove_all_extents<_Tp>::type; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_extent
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 15/26] c++: Implement __add_lvalue_reference built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (13 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 21:00                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
                                   ` (12 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_lvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_lvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_LVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_lvalue_reference.
	* g++.dg/ext/add_lvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  8 +++++++
 .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 33 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 933c8bcbe68..9a27dca4ea3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 078424dac23..05f5b62f9df 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12776,6 +12776,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_LVALUE_REFERENCE:
+      if (VOID_TYPE_P (type1)
+	  || (FUNC_OR_METHOD_TYPE_P (type1)
+	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
+	return type1;
+      return cp_build_reference_type (type1, /*rval=*/false);
+
     case CPTK_ADD_POINTER:
       if (FUNC_OR_METHOD_TYPE_P (type1)
 	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
new file mode 100644
index 00000000000..8fe1e0300e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_lvalue_reference(int), int&));
+SA(__is_same(__add_lvalue_reference(int&), int&));
+SA(__is_same(__add_lvalue_reference(const int), const int&));
+SA(__is_same(__add_lvalue_reference(int*), int*&));
+SA(__is_same(__add_lvalue_reference(ClassType&), ClassType&));
+SA(__is_same(__add_lvalue_reference(ClassType), ClassType&));
+SA(__is_same(__add_lvalue_reference(int(int)), int(&)(int)));
+SA(__is_same(__add_lvalue_reference(int&&), int&));
+SA(__is_same(__add_lvalue_reference(ClassType&&), ClassType&));
+SA(__is_same(__add_lvalue_reference(void), void));
+SA(__is_same(__add_lvalue_reference(const void), const void));
+SA(__is_same(__add_lvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_lvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_lvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_lvalue_reference(bool(int)), bool(&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 85b74bd676b..3fca9cfabcc 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_lvalue_reference)
+# error "__has_builtin (__add_lvalue_reference) failed"
+#endif
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (14 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
                                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::add_lvalue_reference by dispatching to the new
__add_lvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_lvalue_reference): Use
	__add_lvalue_reference built-in trait.
	(__add_lvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 34475e6279a..17bf47d59d3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1157,6 +1157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct __add_lvalue_reference_helper
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_lvalue_reference_helper
     { using type = _Tp; };
@@ -1164,6 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_lvalue_reference_helper<_Tp, __void_t<_Tp&>>
     { using type = _Tp&; };
+#endif
 
   template<typename _Tp>
     using __add_lval_ref_t = typename __add_lvalue_reference_helper<_Tp>::type;
@@ -1731,9 +1737,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_lvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct add_lvalue_reference
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_lvalue_reference
     { using type = __add_lval_ref_t<_Tp>; };
+#endif
 
   /// add_rvalue_reference
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 17/26] c++: Implement __add_rvalue_reference built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (15 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 21:02                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
                                   ` (10 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::add_rvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_rvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_RVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_rvalue_reference.
	* g++.dg/ext/add_rvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  8 ++++++++
 .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 32 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 9a27dca4ea3..173818adf79 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -50,6 +50,7 @@
 
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
+DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 05f5b62f9df..19d6f87a9ea 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12793,6 +12793,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return build_pointer_type (type1);
 
+    case CPTK_ADD_RVALUE_REFERENCE:
+      if (VOID_TYPE_P (type1)
+	  || (FUNC_OR_METHOD_TYPE_P (type1)
+	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
+		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
+	return type1;
+      return cp_build_reference_type (type1, /*rval=*/true);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
new file mode 100644
index 00000000000..c92fe6bfa17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_rvalue_reference(int), int&&));
+SA(__is_same(__add_rvalue_reference(int&&), int&&));
+SA(__is_same(__add_rvalue_reference(int&), int&));
+SA(__is_same(__add_rvalue_reference(const int), const int&&));
+SA(__is_same(__add_rvalue_reference(int*), int*&&));
+SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+SA(__is_same(__add_rvalue_reference(void), void));
+SA(__is_same(__add_rvalue_reference(const void), const void));
+SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 3fca9cfabcc..c2503c5d82b 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -8,6 +8,9 @@
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
+#if !__has_builtin (__add_rvalue_reference)
+# error "__has_builtin (__add_rvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (16 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 19/26] c++: Implement __decay built-in trait Ken Matsui
                                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::add_rvalue_reference by dispatching to the new
__add_rvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_rvalue_reference): Use
	__add_rvalue_reference built-in trait.
	(__add_rvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 17bf47d59d3..18a5e4de2d3 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1185,6 +1185,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct __add_rvalue_reference_helper
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_rvalue_reference_helper
     { using type = _Tp; };
@@ -1192,6 +1197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_rvalue_reference_helper<_Tp, __void_t<_Tp&&>>
     { using type = _Tp&&; };
+#endif
 
   template<typename _Tp>
     using __add_rval_ref_t = typename __add_rvalue_reference_helper<_Tp>::type;
@@ -1748,9 +1754,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_rvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct add_rvalue_reference
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_rvalue_reference
     { using type = __add_rval_ref_t<_Tp>; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_reference
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 19/26] c++: Implement __decay built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (17 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 21:03                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
                                   ` (8 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::decay.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __decay.
	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __decay.
	* g++.dg/ext/decay.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 12 ++++++++++++
 gcc/testsuite/g++.dg/ext/decay.C         | 22 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 4 files changed, 38 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 173818adf79..2d1cb7c227c 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -51,6 +51,7 @@
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
+DEFTRAIT_TYPE (DECAY, "__decay", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 19d6f87a9ea..45dc509855a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12801,6 +12801,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	return type1;
       return cp_build_reference_type (type1, /*rval=*/true);
 
+    case CPTK_DECAY:
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
+				  complain);
+      else if (TREE_CODE (type1) == FUNCTION_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
+      else
+	return cv_unqualified (type1);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
new file mode 100644
index 00000000000..8adedfeefe6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/decay.C
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+// Positive tests.
+using test1_type = __decay(bool);
+SA(__is_same(test1_type, bool));
+
+// NB: DR 705.
+using test2_type = __decay(const int);
+SA(__is_same(test2_type, int));
+
+using test3_type = __decay(int[4]);
+SA(__is_same(test3_type, __remove_extent(int[4])*));
+
+using fn_type = void ();
+using test4_type = __decay(fn_type);
+SA(__is_same(test4_type, __add_pointer(fn_type)));
+
+using cfn_type = void () const;
+using test5_type = __decay(cfn_type);
+SA(__is_same(test5_type, cfn_type));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index c2503c5d82b..3aca273aad6 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -11,6 +11,9 @@
 #if !__has_builtin (__add_rvalue_reference)
 # error "__has_builtin (__add_rvalue_reference) failed"
 #endif
+#if !__has_builtin (__decay)
+# error "__has_builtin (__decay) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 20/26] libstdc++: Optimize std::decay compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (18 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 19/26] c++: Implement __decay built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 21/26] c++: Implement __rank built-in trait Ken Matsui
                                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::decay
by dispatching to the new __decay built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (decay): Use __decay built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 18a5e4de2d3..2f4c8dd3b21 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2316,6 +2316,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @cond undocumented
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__decay)
+  template<typename _Tp>
+    struct decay
+    { using type = __decay(_Tp); };
+#else
   // Decay trait for arrays and functions, used for perfect forwarding
   // in make_pair, make_tuple, etc.
   template<typename _Up>
@@ -2347,6 +2352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct decay<_Tp&&>
     { using type = typename __decay_selector<_Tp>::type; };
+#endif
 
   /// @cond undocumented
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (19 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 21:08                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
                                   ` (6 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::rank.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __rank.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __rank.
	* g++.dg/ext/rank.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
 5 files changed, 51 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 000df847342..23ea66d9c12 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
+    case CPTK_RANK:
+      inform (loc, "  %qT cannot yield a rank", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 2d1cb7c227c..85056c8140b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
+DEFTRAIT_EXPR (RANK, "__rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 45dc509855a..7242db75248 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
+    /* __rank is handled in finish_trait_expr. */
+    case CPTK_RANK:
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
   if (processing_template_decl)
     {
       tree trait_expr = make_node (TRAIT_EXPR);
-      TREE_TYPE (trait_expr) = boolean_type_node;
+      if (kind == CPTK_RANK)
+	TREE_TYPE (trait_expr) = size_type_node;
+      else
+	TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
       TRAIT_EXPR_TYPE2 (trait_expr) = type2;
       TRAIT_EXPR_KIND (trait_expr) = kind;
@@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
+    case CPTK_RANK:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  tree val = (trait_expr_value (kind, type1, type2)
-	      ? boolean_true_node : boolean_false_node);
+  tree val;
+  if (kind == CPTK_RANK)
+    {
+      size_t rank = 0;
+      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+	++rank;
+      val = build_int_cst (size_type_node, rank);
+    }
+  else
+    val = (trait_expr_value (kind, type1, type2)
+	   ? boolean_true_node : boolean_false_node);
+
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 3aca273aad6..7f7b27f7aa7 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -179,6 +179,9 @@
 #if !__has_builtin (__is_volatile)
 # error "__has_builtin (__is_volatile) failed"
 #endif
+#if !__has_builtin (__rank)
+# error "__has_builtin (__rank) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
new file mode 100644
index 00000000000..28894184387
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/rank.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <cstddef>
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__rank(int) == 0);
+SA(__rank(int[2]) == 1);
+SA(__rank(int[][4]) == 2);
+SA(__rank(int[2][2][4][4][6][6]) == 6);
+SA(__rank(ClassType) == 0);
+SA(__rank(ClassType[2]) == 1);
+SA(__rank(ClassType[][4]) == 2);
+SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
+
+template<class T> void f(T) = delete;
+void f(size_t);
+
+template<class T>
+void g() { f(__rank(T)); }
+
+template void g<int>();
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 22/26] libstdc++: Optimize std::rank compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (20 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 21/26] c++: Implement __rank built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
                                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::rank
by dispatching to the new __rank built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (rank): Use __rank built-in trait.
	(rank_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 2f4c8dd3b21..1577042a5b8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1473,6 +1473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// rank
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__rank)
+  template<typename _Tp>
+    struct rank
+    : public integral_constant<std::size_t, __rank(_Tp)> { };
+#else
   template<typename>
     struct rank
     : public integral_constant<std::size_t, 0> { };
@@ -1484,6 +1489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct rank<_Tp[]>
     : public integral_constant<std::size_t, 1 + rank<_Tp>::value> { };
+#endif
 
   /// extent
   template<typename, unsigned _Uint = 0>
@@ -3579,12 +3585,17 @@ template <typename _Tp>
 template <typename _Tp>
   inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__rank)
+template <typename _Tp>
+  inline constexpr size_t rank_v = __rank(_Tp);
+#else
 template <typename _Tp>
   inline constexpr size_t rank_v = 0;
 template <typename _Tp, size_t _Size>
   inline constexpr size_t rank_v<_Tp[_Size]> = 1 + rank_v<_Tp>;
 template <typename _Tp>
   inline constexpr size_t rank_v<_Tp[]> = 1 + rank_v<_Tp>;
+#endif
 
 template <typename _Tp, unsigned _Idx = 0>
   inline constexpr size_t extent_v = 0;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 23/26] c++: Implement __is_invocable built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (21 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-03-08 17:17                   ` Patrick Palka
  2024-02-28 19:26                 ` [PATCH v14 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
                                   ` (4 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 132 +++++++++
 gcc/cp/semantics.cc                      |   4 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 720 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 23ea66d9c12..c87b126fdb1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 85056c8140b..6cb2b55f4ea 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 334c11396c2..261d3a71faa 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7334,6 +7334,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 98c10e6a8b5..953f1bed6fc 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,138 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (fn_type == error_mark_node || arg_types == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    /* Only a pointer to data member with one argument is invocable.  */
+    return error_mark_node;
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    /* A pointer to member function with no arguments is not invocable.  */
+    return error_mark_node;
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+        {
+	  if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
+	    return error_mark_node;
+        }
+      else if (!CLASS_TYPE_P (datum_type))
+	return error_mark_node;
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr) && !is_refwrap)
+	{
+	  tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+	  const bool ptrmem_is_base_of_datum =
+	    (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+	  if (!ptrmem_is_base_of_datum)
+	    {
+	      /* 1.3 & 1.6: Try to dereference datum_expr.  */
+	      datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+						 RO_UNARY_STAR, NULL_TREE,
+						 complain);
+	      /* Rebuild ptrmem_expr.  */
+	      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
+						   complain);
+	    }
+	}
+      /* 1.1 & 1.4: Otherwise.  */
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7242db75248..149c0631d62 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       break;
 
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7f7b27f7aa7..d2a7ebdf25c 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -104,6 +104,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 24/26] libstdc++: Optimize std::is_invocable compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (22 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-02-28 19:26                 ` [PATCH v14 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
                                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __is_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use __is_invocable
	built-in trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc     | 1 +
 libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1577042a5b8..9af233bcc75 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3235,7 +3235,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable)
+    : public __bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+#else
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 25/26] c++: Implement __is_nothrow_invocable built-in trait
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (23 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-04-30 21:09                   ` Jason Merrill
  2024-02-28 19:26                 ` [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
                                   ` (2 subsequent siblings)
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch implements built-in trait for std::is_nothrow_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_nothrow_invocable.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_NOTHROW_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_nothrow_invocable.
	* g++.dg/ext/is_nothrow_invocable.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  6 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +
 .../g++.dg/ext/is_nothrow_invocable.C         | 62 +++++++++++++++++++
 5 files changed, 76 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c87b126fdb1..43d4f2102d6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3824,6 +3824,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
 	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
       break;
+    case CPTK_IS_NOTHROW_INVOCABLE:
+	if (!t2)
+	  inform (loc, "  %qT is not nothrow invocable", t1);
+	else
+	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
+	break;
     case CPTK_IS_OBJECT:
       inform (loc, "  %qT is not an object type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 6cb2b55f4ea..a9714921e94 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
 DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
 DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
 DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 149c0631d62..dba7b43a109 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12494,6 +12494,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
       return is_nothrow_convertible (type1, type2);
 
+    case CPTK_IS_NOTHROW_INVOCABLE:
+      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
+
     case CPTK_IS_OBJECT:
       return (type_code1 != FUNCTION_TYPE
 	      && type_code1 != REFERENCE_TYPE
@@ -12689,6 +12692,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_IS_TRIVIALLY_ASSIGNABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index d2a7ebdf25c..624d3525f27 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -131,6 +131,9 @@
 #if !__has_builtin (__is_nothrow_convertible)
 # error "__has_builtin (__is_nothrow_convertible) failed"
 #endif
+#if !__has_builtin (__is_nothrow_invocable)
+# error "__has_builtin (__is_nothrow_invocable) failed"
+#endif
 #if !__has_builtin (__is_object)
 # error "__has_builtin (__is_object) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
new file mode 100644
index 00000000000..2f9b40e5538
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
@@ -0,0 +1,62 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type = void(*)();
+SA( ! __is_nothrow_invocable(func_type) );
+
+#if __cpp_noexcept_function_type
+using func_type_nt = void(*)() noexcept;
+SA(   __is_nothrow_invocable(func_type_nt) );
+#endif
+
+struct X { };
+using mem_type = int X::*;
+
+SA( ! __is_nothrow_invocable(mem_type) );
+SA( ! __is_nothrow_invocable(mem_type, int) );
+SA( ! __is_nothrow_invocable(mem_type, int&) );
+SA(   __is_nothrow_invocable(mem_type, X&) );
+
+using memfun_type = int (X::*)();
+
+SA( ! __is_nothrow_invocable(memfun_type) );
+SA( ! __is_nothrow_invocable(memfun_type, int) );
+SA( ! __is_nothrow_invocable(memfun_type, int&) );
+SA( ! __is_nothrow_invocable(memfun_type, X&) );
+SA( ! __is_nothrow_invocable(memfun_type, X*) );
+
+#if __cpp_noexcept_function_type
+using memfun_type_nt = int (X::*)() noexcept;
+
+SA( ! __is_nothrow_invocable(memfun_type_nt) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X*) );
+#endif
+
+struct F {
+  int& operator()();
+  long& operator()() const noexcept;
+  short& operator()(int) &&;
+  char& operator()(int) const& noexcept;
+private:
+  void operator()(int, int) noexcept;
+};
+using CF = const F;
+
+SA( ! __is_nothrow_invocable(F ) );
+SA(   __is_nothrow_invocable(CF) );
+
+SA( ! __is_nothrow_invocable(F,   int) );
+SA(   __is_nothrow_invocable(F&,  int) );
+
+SA(   __is_nothrow_invocable(CF,   int) );
+SA(   __is_nothrow_invocable(CF&,  int) );
+SA( ! __is_nothrow_invocable(F, int, int) );
+
+struct FX {
+  X operator()() const noexcept { return {}; }
+};
+SA(   __is_nothrow_invocable(FX) );
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (24 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
@ 2024-02-28 19:26                 ` Ken Matsui
  2024-03-08 17:22                   ` Patrick Palka
  2024-03-07 21:33                 ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
  27 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-02-28 19:26 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Ken Matsui

This patch optimizes the compilation performance of
std::is_nothrow_invocable by dispatching to the new
__is_nothrow_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_nothrow_invocable): Use
	__is_nothrow_invocable built-in trait.
	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
	Handle the new error from __is_nothrow_invocable.
	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
	Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
 .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 9af233bcc75..093d85a51a8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3265,8 +3265,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_nothrow_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable)
+    : public __bool_constant<__is_nothrow_invocable(_Fn, _ArgTypes...)>
+#else
     : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
 	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
index 3c225883eaf..3f8542dd366 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
index 5a728bfa03b..d3bdf08448b 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 00/26] Optimize more type traits
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (25 preceding siblings ...)
  2024-02-28 19:26                 ` [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
@ 2024-03-07 21:33                 ` Ken Matsui
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
  27 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-03-07 21:33 UTC (permalink / raw)
  To: gcc-patches; +Cc: Patrick Palka, libstdc++, Ken Matsui

On Wed, Feb 28, 2024 at 11:32 AM Ken Matsui <kmatsui@gcc.gnu.org> wrote:
>
> Hi,
>
> This patch series implements __is_const, __is_volatile, __is_pointer,
> and __is_unbounded_array built-in traits, which were isolated from my
> previous patch series "Optimize type traits compilation performance"
> because they contained performance regression.  I confirmed that this
> patch series does not cause any performance regression.  The main reason
> of the performance regression were the exhaustiveness of the benchmarks
> and the instability of the benchmark results.  Also, this patch series
> includes built-ins for add_pointer, remove_extent, remove_all_extents,
> add_lvalue_reference, add_rvalue_reference, decay, rank, is_invocable,
> and is_nothrow_invocable.  Here are the benchmark results:

Ping.  Ok for trunk or maybe for 15?

>
> is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023
> time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%
>
> is_const_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_const_v.md#sat-jun-24-044815-am-pdt-2023
> time: -2.86467%, peak memory: -1.0654%, total memory: -1.62369%
>
> is_volatile: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile.md#sun-oct-22-091644-pm-pdt-2023
> time: -5.25164%, peak memory: -0.337971%, total memory: -0.247934%
>
> is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023
> time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%
>
> is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023
> time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%
>
> is_pointer_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer_v.md#sun-oct-22-122257-am-pdt-2023
> time: -4.71336%, peak memory: -2.25026%, total memory: -3.125%
>
> is_unbounded_array: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array.md#sun-oct-22-091644-pm-pdt-2023
> time: -6.33287%, peak memory: -0.602494%, total memory: -1.56035%
>
> is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023
> time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%
>
> add_pointer_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_pointer_t.md#wed-feb-28-060044-am-pst-2024
> time: -21.6673%, peak memory: -14.6666%, total memory: -17.4716%
>
> remove_extent_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_extent_t.md#wed-feb-28-063021-am-pst-2024
> time: -14.4089%, peak memory: -2.71836%, total memory: -9.87013%
>
> remove_all_extents_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_all_extents_t.md#wed-feb-28-064716-am-pst-2024
> time: -28.8941%, peak memory: -16.6981%, total memory: -23.6088%
>
> add_lvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_lvalue_reference_t.md#wed-feb-28-070023-am-pst-2024
> time: -33.8827%, peak memory: -24.9292%, total memory: -25.3043%
>
> add_rvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_rvalue_reference_t.md#wed-feb-28-070701-am-pst-2024
> time: -23.9186%, peak memory: -17.1311%, total memory: -19.5891%
>
> decay_t: https://github.com/ken-matsui/gcc-bench/blob/main/decay_t.md#wed-feb-28-072330-am-pst-2024
> time: -42.4076%, peak memory: -29.2077%, total memory: -33.0914%
>
> rank: https://github.com/ken-matsui/gcc-bench/blob/main/rank.md#wed-feb-28-074917-am-pst-2024
> time: -33.7312%, peak memory: -27.5885%, total memory: -34.5736%
>
> rank_v: https://github.com/ken-matsui/gcc-bench/blob/main/rank_v.md#wed-feb-28-073632-am-pst-2024
> time: -40.7174%, peak memory: -16.4653%, total memory: -23.0131%
>
> is_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_invocable.md#wed-feb-28-111001-am-pst-2024
> time: -58.8307%, peak memory: -59.4966%, total memory: -59.8871%
> (This benchmark is not exhaustive as my laptop crashed with larger benchmarks)
>
> is_nothrow_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_nothrow_invocable.md#wed-feb-28-112414-am-pst-2024
> time: -70.4102%, peak memory: -62.5516%, total memory: -65.5853%
> (This benchmark is not exhaustive as my laptop crashed with larger benchmarks)
>
> Sincerely,
> Ken Matsui
>
> Ken Matsui (26):
>   c++: Implement __is_const built-in trait
>   libstdc++: Optimize std::is_const compilation performance
>   c++: Implement __is_volatile built-in trait
>   libstdc++: Optimize std::is_volatile compilation performance
>   c++: Implement __is_pointer built-in trait
>   libstdc++: Optimize std::is_pointer compilation performance
>   c++: Implement __is_unbounded_array built-in trait
>   libstdc++: Optimize std::is_unbounded_array compilation performance
>   c++: Implement __add_pointer built-in trait
>   libstdc++: Optimize std::add_pointer compilation performance
>   c++: Implement __remove_extent built-in trait
>   libstdc++: Optimize std::remove_extent compilation performance
>   c++: Implement __remove_all_extents built-in trait
>   libstdc++: Optimize std::remove_all_extents compilation performance
>   c++: Implement __add_lvalue_reference built-in trait
>   libstdc++: Optimize std::add_lvalue_reference compilation performance
>   c++: Implement __add_rvalue_reference built-in trait
>   libstdc++: Optimize std::add_rvalue_reference compilation performance
>   c++: Implement __decay built-in trait
>   libstdc++: Optimize std::decay compilation performance
>   c++: Implement __rank built-in trait
>   libstdc++: Optimize std::rank compilation performance
>   c++: Implement __is_invocable built-in trait
>   libstdc++: Optimize std::is_invocable compilation performance
>   c++: Implement __is_nothrow_invocable built-in trait
>   libstdc++: Optimize std::is_nothrow_invocable compilation performance
>
>  gcc/cp/constraint.cc                          |  27 ++
>  gcc/cp/cp-trait.def                           |  13 +
>  gcc/cp/cp-tree.h                              |   2 +
>  gcc/cp/method.cc                              | 132 +++++++
>  gcc/cp/semantics.cc                           |  92 ++++-
>  .../g++.dg/ext/add_lvalue_reference.C         |  21 ++
>  gcc/testsuite/g++.dg/ext/add_pointer.C        |  39 ++
>  .../g++.dg/ext/add_rvalue_reference.C         |  20 +
>  gcc/testsuite/g++.dg/ext/decay.C              |  22 ++
>  gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  39 ++
>  gcc/testsuite/g++.dg/ext/is_const.C           |  20 +
>  gcc/testsuite/g++.dg/ext/is_invocable1.C      | 349 ++++++++++++++++++
>  gcc/testsuite/g++.dg/ext/is_invocable2.C      | 139 +++++++
>  gcc/testsuite/g++.dg/ext/is_invocable3.C      |  51 +++
>  gcc/testsuite/g++.dg/ext/is_invocable4.C      |  33 ++
>  .../g++.dg/ext/is_nothrow_invocable.C         |  62 ++++
>  gcc/testsuite/g++.dg/ext/is_pointer.C         |  51 +++
>  gcc/testsuite/g++.dg/ext/is_unbounded_array.C |  37 ++
>  gcc/testsuite/g++.dg/ext/is_volatile.C        |  20 +
>  gcc/testsuite/g++.dg/ext/rank.C               |  24 ++
>  gcc/testsuite/g++.dg/ext/remove_all_extents.C |  16 +
>  gcc/testsuite/g++.dg/ext/remove_extent.C      |  16 +
>  libstdc++-v3/include/bits/cpp_type_traits.h   |  31 +-
>  libstdc++-v3/include/std/type_traits          | 142 ++++++-
>  .../is_invocable/incomplete_args_neg.cc       |   1 +
>  .../20_util/is_invocable/incomplete_neg.cc    |   1 +
>  .../incomplete_args_neg.cc                    |   1 +
>  .../is_nothrow_invocable/incomplete_neg.cc    |   1 +
>  28 files changed, 1389 insertions(+), 13 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/decay.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C
>
> --
> 2.44.0
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 23/26] c++: Implement __is_invocable built-in trait
  2024-02-28 19:26                 ` [PATCH v14 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-03-08 17:17                   ` Patrick Palka
  2024-03-08 17:38                     ` Ken Matsui
  2024-03-15  1:53                     ` Ken Matsui
  0 siblings, 2 replies; 244+ messages in thread
From: Patrick Palka @ 2024-03-08 17:17 UTC (permalink / raw)
  To: Ken Matsui; +Cc: gcc-patches, libstdc++

On Wed, 28 Feb 2024, Ken Matsui wrote:

> This patch implements built-in trait for std::is_invocable.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_invocable.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 	* cp-tree.h (build_invoke): New function.
> 	* method.cc (build_invoke): New function.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> 	* g++.dg/ext/is_invocable1.C: New test.
> 	* g++.dg/ext/is_invocable2.C: New test.
> 	* g++.dg/ext/is_invocable3.C: New test.
> 	* g++.dg/ext/is_invocable4.C: New test.

Thanks, this looks great!  This generic build_invoke function could be
used for invoke_result etc as well, and it could also cache the built-up
call across __is_invocable and __is_nothrow_invocable checks on the same
arguments (which is a common pattern in the standard library).  LGTM

> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>  gcc/cp/constraint.cc                     |   6 +
>  gcc/cp/cp-trait.def                      |   1 +
>  gcc/cp/cp-tree.h                         |   2 +
>  gcc/cp/method.cc                         | 132 +++++++++
>  gcc/cp/semantics.cc                      |   4 +
>  gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
>  gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
>  gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
>  gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
>  gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
>  10 files changed, 720 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 23ea66d9c12..c87b126fdb1 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
>      case CPTK_IS_FUNCTION:
>        inform (loc, "  %qT is not a function", t1);
>        break;
> +    case CPTK_IS_INVOCABLE:
> +      if (!t2)
> +    inform (loc, "  %qT is not invocable", t1);
> +      else
> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> +      break;
>      case CPTK_IS_LAYOUT_COMPATIBLE:
>        inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
>        break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 85056c8140b..6cb2b55f4ea 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
>  DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
>  DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
>  DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
>  DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
>  DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
>  DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 334c11396c2..261d3a71faa 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7334,6 +7334,8 @@ extern tree get_copy_assign			(tree);
>  extern tree get_default_ctor			(tree);
>  extern tree get_dtor				(tree, tsubst_flags_t);
>  extern tree build_stub_object			(tree);
> +extern tree build_invoke			(tree, const_tree,
> +						 tsubst_flags_t);
>  extern tree strip_inheriting_ctors		(tree);
>  extern tree inherited_ctor_binfo		(tree);
>  extern bool base_ctor_omit_inherited_parms	(tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 98c10e6a8b5..953f1bed6fc 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1928,6 +1928,138 @@ build_trait_object (tree type)
>    return build_stub_object (type);
>  }
>  
> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> +   given is not invocable, returns error_mark_node.  */
> +
> +tree
> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> +{
> +  if (fn_type == error_mark_node || arg_types == error_mark_node)
> +    return error_mark_node;
> +
> +  gcc_assert (TYPE_P (fn_type));
> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> +
> +  /* Access check is required to determine if the given is invocable.  */
> +  deferring_access_check_sentinel acs (dk_no_deferred);
> +
> +  /* INVOKE is an unevaluated context.  */
> +  cp_unevaluated cp_uneval_guard;
> +
> +  bool is_ptrdatamem;
> +  bool is_ptrmemfunc;
> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> +    {
> +      tree deref_fn_type = TREE_TYPE (fn_type);
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> +
> +      /* Dereference fn_type if it is a pointer to member.  */
> +      if (is_ptrdatamem || is_ptrmemfunc)
> +	fn_type = deref_fn_type;
> +    }
> +  else
> +    {
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> +    }
> +
> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> +    /* Only a pointer to data member with one argument is invocable.  */

Just one note, if/when build_invoke gets a caller that passes tf_error
(for e.g. elaborating why is_invocable is false, or why invoke_result
is ill-formed) these error_mark_node code paths will need to issue
diagnostics b.

> +    return error_mark_node;
> +
> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> +    /* A pointer to member function with no arguments is not invocable.  */
> +    return error_mark_node;
> +
> +  /* Construct an expression of a pointer to member.  */
> +  tree ptrmem_expr;
> +  if (is_ptrdatamem || is_ptrmemfunc)
> +    {
> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> +
> +      /* datum must be a class type or a reference/pointer to a class type.  */
> +      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> +        {
> +	  if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
> +	    return error_mark_node;
> +        }
> +      else if (!CLASS_TYPE_P (datum_type))
> +	return error_mark_node;
> +
> +      bool is_refwrap = false;
> +      if (CLASS_TYPE_P (datum_type))
> +	{
> +	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> +	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> +	  if (decl_in_std_namespace_p (datum_decl))
> +	    {
> +	      const_tree name = DECL_NAME (datum_decl);
> +	      if (name && (id_equal (name, "reference_wrapper")))
> +		{
> +		  /* Retrieve T from std::reference_wrapper<T>,
> +		     i.e., decltype(datum.get()).  */
> +		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> +		  is_refwrap = true;
> +		}
> +	    }
> +	}
> +
> +      tree datum_expr = build_trait_object (datum_type);
> +      tree fn_expr = build_trait_object (fn_type);
> +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> +
> +      if (error_operand_p (ptrmem_expr) && !is_refwrap)
> +	{
> +	  tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> +	  const bool ptrmem_is_base_of_datum =
> +	    (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> +	     && NON_UNION_CLASS_TYPE_P (datum_type)
> +	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> +							    datum_type)
> +		 || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> +
> +	  if (!ptrmem_is_base_of_datum)
> +	    {
> +	      /* 1.3 & 1.6: Try to dereference datum_expr.  */
> +	      datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> +						 RO_UNARY_STAR, NULL_TREE,
> +						 complain);
> +	      /* Rebuild ptrmem_expr.  */
> +	      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
> +						   complain);
> +	    }
> +	}
> +      /* 1.1 & 1.4: Otherwise.  */
> +
> +      if (error_operand_p (ptrmem_expr))
> +	return error_mark_node;
> +
> +      if (is_ptrdatamem)
> +	return ptrmem_expr;
> +    }
> +
> +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> +     function, the first argument, which is the object, is not arguments to
> +     the function.  */
> +  releasing_vec args;
> +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> +    {
> +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> +      tree arg = build_trait_object (arg_type);
> +      vec_safe_push (args, arg);
> +    }
> +
> +  tree invoke_expr;
> +  if (is_ptrmemfunc)
> +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> +						   complain);
> +  else  /* 1.7.  */
> +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> +				    false, complain);
> +  return invoke_expr;
> +}
> +
>  /* Determine which function will be called when looking up NAME in TYPE,
>     called with a single ARGTYPE argument, or no argument if ARGTYPE is
>     null.  FLAGS and COMPLAIN are as for build_new_method_call.
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 7242db75248..149c0631d62 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>      case CPTK_IS_FUNCTION:
>        return type_code1 == FUNCTION_TYPE;
>  
> +    case CPTK_IS_INVOCABLE:
> +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> +
>      case CPTK_IS_LAYOUT_COMPATIBLE:
>        return layout_compatible_type_p (type1, type2);
>  
> @@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>        break;
>  
>      case CPTK_IS_CONVERTIBLE:
> +    case CPTK_IS_INVOCABLE:
>      case CPTK_IS_NOTHROW_ASSIGNABLE:
>      case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>      case CPTK_IS_NOTHROW_CONVERTIBLE:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 7f7b27f7aa7..d2a7ebdf25c 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -104,6 +104,9 @@
>  #if !__has_builtin (__is_function)
>  # error "__has_builtin (__is_function) failed"
>  #endif
> +#if !__has_builtin (__is_invocable)
> +# error "__has_builtin (__is_invocable) failed"
> +#endif
>  #if !__has_builtin (__is_layout_compatible)
>  # error "__has_builtin (__is_layout_compatible) failed"
>  #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> new file mode 100644
> index 00000000000..d21ae1d1958
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> @@ -0,0 +1,349 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using func_type_v0 = void(*)();
> +
> +SA(   __is_invocable( func_type_v0 ) );
> +SA( ! __is_invocable( func_type_v0, int ) );
> +
> +using func_type_i0 = int(*)();
> +
> +SA(   __is_invocable( func_type_i0 ) );
> +SA( ! __is_invocable( func_type_i0, int ) );
> +
> +using func_type_l0 = int&(*)();
> +
> +SA(   __is_invocable( func_type_l0 ) );
> +SA( ! __is_invocable( func_type_l0(int) ) );
> +
> +using func_type_ii = int(*)(int);
> +
> +SA( ! __is_invocable( func_type_ii ) );
> +SA(   __is_invocable( func_type_ii, int ) );
> +
> +using func_type_il = int(*)(int&);
> +
> +SA( ! __is_invocable( func_type_il ) );
> +SA( ! __is_invocable( func_type_il, int ) );
> +SA(   __is_invocable( func_type_il, int& ) );
> +
> +using func_type_ir = int(*)(int&&);
> +
> +SA( ! __is_invocable( func_type_ir ) );
> +SA( ! __is_invocable( func_type_ir, int& ) );
> +SA(   __is_invocable( func_type_ir, int ) );
> +SA(   __is_invocable( func_type_ir, int&& ) );
> +
> +struct A { };
> +
> +using mem_type_i = int A::*;
> +
> +SA( ! __is_invocable( mem_type_i ) );
> +SA( ! __is_invocable( mem_type_i, int ) );
> +SA( ! __is_invocable( mem_type_i, int* ) );
> +SA( ! __is_invocable( mem_type_i, int& ) );
> +SA( ! __is_invocable( mem_type_i, int&& ) );
> +SA(   __is_invocable( mem_type_i, A ) );
> +SA(   __is_invocable( mem_type_i, A* ) );
> +SA(   __is_invocable( mem_type_i, A& ) );
> +SA(   __is_invocable( mem_type_i, A&& ) );
> +SA(   __is_invocable( mem_type_i, const A& ) );
> +SA( ! __is_invocable( mem_type_i, A&, int ) );
> +
> +using memfun_type_i = int (A::*)();
> +
> +SA( ! __is_invocable( memfun_type_i ) );
> +SA( ! __is_invocable( memfun_type_i, int ) );
> +SA( ! __is_invocable( memfun_type_i, int* ) );
> +SA( ! __is_invocable( memfun_type_i, int& ) );
> +SA( ! __is_invocable( memfun_type_i, int&& ) );
> +SA(   __is_invocable( memfun_type_i, A ) );
> +SA(   __is_invocable( memfun_type_i, A* ) );
> +SA(   __is_invocable( memfun_type_i, A& ) );
> +SA(   __is_invocable( memfun_type_i, A&& ) );
> +SA( ! __is_invocable( memfun_type_i, const A& ) );
> +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> +
> +using memfun_type_ic = int (A::*)() const;
> +
> +SA( ! __is_invocable( memfun_type_ic ) );
> +SA( ! __is_invocable( memfun_type_ic, int ) );
> +SA( ! __is_invocable( memfun_type_ic, int& ) );
> +SA(   __is_invocable( memfun_type_ic, A& ) );
> +SA(   __is_invocable( memfun_type_ic, A* ) );
> +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> +SA(   __is_invocable( memfun_type_ic, const A& ) );
> +SA(   __is_invocable( memfun_type_ic, const A* ) );
> +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> +
> +using memfun_type_iic = int& (A::*)(int&) const;
> +
> +SA( ! __is_invocable( memfun_type_iic ) );
> +SA( ! __is_invocable( memfun_type_iic, int ) );
> +SA( ! __is_invocable( memfun_type_iic, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> +
> +struct B {
> +  int& operator()();
> +  long& operator()() const;
> +  bool& operator()(int);
> +private:
> +  void operator()(int, int);
> +};
> +using CB = const B;
> +
> +SA(   __is_invocable( B ) );
> +SA(   __is_invocable( B& ) );
> +SA(   __is_invocable( B&& ) );
> +SA( ! __is_invocable( B* ) );
> +SA(   __is_invocable( CB ) );
> +SA(   __is_invocable( CB& ) );
> +SA( ! __is_invocable( CB* ) );
> +
> +SA(   __is_invocable( B, int ) );
> +SA(   __is_invocable( B&, int ) );
> +SA(   __is_invocable( B&&, int ) );
> +SA( ! __is_invocable( B*, int ) );
> +SA( ! __is_invocable( CB, int ) );
> +SA( ! __is_invocable( CB&, int ) );
> +SA( ! __is_invocable( CB*, int ) );
> +
> +SA( ! __is_invocable( B, int, int ) );
> +SA( ! __is_invocable( B&, int, int ) );
> +SA( ! __is_invocable( B&&, int, int ) );
> +SA( ! __is_invocable( B*, int, int ) );
> +SA( ! __is_invocable( CB, int, int ) );
> +SA( ! __is_invocable( CB&, int, int ) );
> +SA( ! __is_invocable( CB*, int, int ) );
> +
> +struct C : B { int& operator()() = delete; };
> +using CC = const C;
> +
> +SA( ! __is_invocable( C ) );
> +SA( ! __is_invocable( C& ) );
> +SA( ! __is_invocable( C&& ) );
> +SA( ! __is_invocable( C* ) );
> +SA( ! __is_invocable( CC ) );
> +SA( ! __is_invocable( CC& ) );
> +SA( ! __is_invocable( CC* ) );
> +
> +struct D { B operator*(); };
> +using CD = const D;
> +
> +SA( ! __is_invocable( D ) );
> +
> +struct E { void v(); };
> +using CE = const E;
> +
> +SA( ! __is_invocable( E ) );
> +SA( ! __is_invocable( void (E::*)() ) );
> +SA(   __is_invocable( void (E::*)(), E ) );
> +SA(   __is_invocable( void (E::*)(), E* ) );
> +SA( ! __is_invocable( void (E::*)(), CE ) );
> +
> +struct F : E {};
> +using CF = const F;
> +
> +SA( ! __is_invocable( F ) );
> +SA(   __is_invocable( void (E::*)(), F ) );
> +SA(   __is_invocable( void (E::*)(), F* ) );
> +SA( ! __is_invocable( void (E::*)(), CF ) );
> +
> +struct G { E operator*(); };
> +using CG = const G;
> +
> +SA( ! __is_invocable( G ) );
> +SA(   __is_invocable( void (E::*)(), G ) );
> +SA( ! __is_invocable( void (E::*)(), G* ) );
> +SA( ! __is_invocable( void (E::*)(), CG ) );
> +
> +struct H { E& operator*(); };
> +using CH = const H;
> +
> +SA( ! __is_invocable( H ) );
> +SA(   __is_invocable( void (E::*)(), H ) );
> +SA( ! __is_invocable( void (E::*)(), H* ) );
> +SA( ! __is_invocable( void (E::*)(), CH ) );
> +
> +struct I { E&& operator*(); };
> +using CI = const I;
> +
> +SA( ! __is_invocable( I ) );
> +SA(   __is_invocable( void (E::*)(), I ) );
> +SA( ! __is_invocable( void (E::*)(), I* ) );
> +SA( ! __is_invocable( void (E::*)(), CI ) );
> +
> +struct K { E* operator*(); };
> +using CK = const K;
> +
> +SA( ! __is_invocable( K ) );
> +SA( ! __is_invocable( void (E::*)(), K ) );
> +SA( ! __is_invocable( void (E::*)(), K* ) );
> +SA( ! __is_invocable( void (E::*)(), CK ) );
> +
> +struct L { CE operator*(); };
> +using CL = const L;
> +
> +SA( ! __is_invocable( L ) );
> +SA( ! __is_invocable( void (E::*)(), L ) );
> +SA( ! __is_invocable( void (E::*)(), L* ) );
> +SA( ! __is_invocable( void (E::*)(), CL ) );
> +
> +struct M {
> +  int i;
> +private:
> +  long l;
> +};
> +using CM = const M;
> +
> +SA( ! __is_invocable( M ) );
> +SA( ! __is_invocable( M& ) );
> +SA( ! __is_invocable( M&& ) );
> +SA( ! __is_invocable( M* ) );
> +SA( ! __is_invocable( CM ) );
> +SA( ! __is_invocable( CM& ) );
> +SA( ! __is_invocable( CM* ) );
> +
> +SA( ! __is_invocable( int M::* ) );
> +SA(   __is_invocable( int M::*, M ) );
> +SA(   __is_invocable( int M::*, M& ) );
> +SA(   __is_invocable( int M::*, M&& ) );
> +SA(   __is_invocable( int M::*, M* ) );
> +SA(   __is_invocable( int M::*, CM ) );
> +SA(   __is_invocable( int M::*, CM& ) );
> +SA(   __is_invocable( int M::*, CM* ) );
> +SA( ! __is_invocable( int M::*, int ) );
> +
> +SA( ! __is_invocable( int CM::* ) );
> +SA(   __is_invocable( int CM::*, M ) );
> +SA(   __is_invocable( int CM::*, M& ) );
> +SA(   __is_invocable( int CM::*, M&& ) );
> +SA(   __is_invocable( int CM::*, M* ) );
> +SA(   __is_invocable( int CM::*, CM ) );
> +SA(   __is_invocable( int CM::*, CM& ) );
> +SA(   __is_invocable( int CM::*, CM* ) );
> +SA( ! __is_invocable( int CM::*, int ) );
> +
> +SA( ! __is_invocable( long M::* ) );
> +SA(   __is_invocable( long M::*, M ) );
> +SA(   __is_invocable( long M::*, M& ) );
> +SA(   __is_invocable( long M::*, M&& ) );
> +SA(   __is_invocable( long M::*, M* ) );
> +SA(   __is_invocable( long M::*, CM ) );
> +SA(   __is_invocable( long M::*, CM& ) );
> +SA(   __is_invocable( long M::*, CM* ) );
> +SA( ! __is_invocable( long M::*, long ) );
> +
> +SA( ! __is_invocable( long CM::* ) );
> +SA(   __is_invocable( long CM::*, M ) );
> +SA(   __is_invocable( long CM::*, M& ) );
> +SA(   __is_invocable( long CM::*, M&& ) );
> +SA(   __is_invocable( long CM::*, M* ) );
> +SA(   __is_invocable( long CM::*, CM ) );
> +SA(   __is_invocable( long CM::*, CM& ) );
> +SA(   __is_invocable( long CM::*, CM* ) );
> +SA( ! __is_invocable( long CM::*, long ) );
> +
> +SA( ! __is_invocable( short M::* ) );
> +SA(   __is_invocable( short M::*, M ) );
> +SA(   __is_invocable( short M::*, M& ) );
> +SA(   __is_invocable( short M::*, M&& ) );
> +SA(   __is_invocable( short M::*, M* ) );
> +SA(   __is_invocable( short M::*, CM ) );
> +SA(   __is_invocable( short M::*, CM& ) );
> +SA(   __is_invocable( short M::*, CM* ) );
> +SA( ! __is_invocable( short M::*, short ) );
> +
> +SA( ! __is_invocable( short CM::* ) );
> +SA(   __is_invocable( short CM::*, M ) );
> +SA(   __is_invocable( short CM::*, M& ) );
> +SA(   __is_invocable( short CM::*, M&& ) );
> +SA(   __is_invocable( short CM::*, M* ) );
> +SA(   __is_invocable( short CM::*, CM ) );
> +SA(   __is_invocable( short CM::*, CM& ) );
> +SA(   __is_invocable( short CM::*, CM* ) );
> +SA( ! __is_invocable( short CM::*, short ) );
> +
> +struct N { M operator*(); };
> +SA(   __is_invocable( int M::*, N ) );
> +SA( ! __is_invocable( int M::*, N* ) );
> +
> +struct O { M& operator*(); };
> +SA(   __is_invocable( int M::*, O ) );
> +SA( ! __is_invocable( int M::*, O* ) );
> +
> +struct P { M&& operator*(); };
> +SA(   __is_invocable( int M::*, P ) );
> +SA( ! __is_invocable( int M::*, P* ) );
> +
> +struct Q { M* operator*(); };
> +SA( ! __is_invocable( int M::*, Q ) );
> +SA( ! __is_invocable( int M::*, Q* ) );
> +
> +struct R { void operator()(int = 0); };
> +
> +SA(   __is_invocable( R ) );
> +SA(   __is_invocable( R, int ) );
> +SA( ! __is_invocable( R, int, int ) );
> +
> +struct S { void operator()(int, ...); };
> +
> +SA( ! __is_invocable( S ) );
> +SA(   __is_invocable( S, int ) );
> +SA(   __is_invocable( S, int, int ) );
> +SA(   __is_invocable( S, int, int, int ) );
> +
> +void fn1() {}
> +
> +SA(   __is_invocable( decltype(fn1) ) );
> +
> +void fn2(int arr[10]);
> +
> +SA(   __is_invocable( decltype(fn2), int[10] ) );
> +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> +SA(   __is_invocable( decltype(fn2), int[] ) );
> +
> +auto lambda = []() {};
> +
> +SA(   __is_invocable( decltype(lambda) ) );
> +
> +template <typename Func, typename... Args>
> +struct can_invoke {
> +    static constexpr bool value = __is_invocable( Func, Args... );
> +};
> +
> +SA(   can_invoke<decltype(lambda)>::value );
> +
> +struct T {
> +  void func() const {}
> +  int data;
> +};
> +
> +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> +
> +struct U { };
> +struct V : U { U& operator*() = delete; };
> +SA(   __is_invocable( int U::*, V ) );
> +
> +struct W : private U { U& operator*(); };
> +SA( ! __is_invocable( int U::*, W ) );
> +
> +struct X { int m; };
> +struct Y { X& operator*(); };
> +struct Z : Y { };
> +SA(   __is_invocable(int X::*, Z) );
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> new file mode 100644
> index 00000000000..a68aefd3e13
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> @@ -0,0 +1,139 @@
> +// { dg-do compile { target c++11 } }
> +// __is_invocable should handle std::reference_wrapper correctly.
> +
> +#include <functional>
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using std::reference_wrapper;
> +
> +using func_type_v0 = void(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> +
> +using func_type_i0 = int(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> +
> +using func_type_l0 = int&(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> +
> +using func_type_ii = int(*)(int);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> +
> +using func_type_il = int(*)(int&);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> +
> +using func_type_ir = int(*)(int&&);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> +
> +struct A { };
> +
> +using mem_type_i = int A::*;
> +
> +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> +
> +using memfun_type_i = int (A::*)();
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> +
> +using memfun_type_ic = int (A::*)() const;
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> +
> +using memfun_type_iic = int& (A::*)(int&) const;
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> +
> +struct B {
> +  int& operator()();
> +  long& operator()() const;
> +  bool& operator()(int);
> +private:
> +  void operator()(int, int);
> +};
> +using CB = const B;
> +
> +SA(   __is_invocable( reference_wrapper<B> ) );
> +SA(   __is_invocable( reference_wrapper<B>& ) );
> +SA(   __is_invocable( reference_wrapper<B>&& ) );
> +SA(   __is_invocable( reference_wrapper<CB> ) );
> +SA(   __is_invocable( reference_wrapper<CB>& ) );
> +SA(   __is_invocable( reference_wrapper<B>, int ) );
> +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> +
> +struct C : B { int& operator()() = delete; };
> +using CC = const C;
> +
> +SA( ! __is_invocable( reference_wrapper<C> ) );
> +SA( ! __is_invocable( reference_wrapper<C>& ) );
> +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> +SA( ! __is_invocable( reference_wrapper<CC> ) );
> +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> +
> +struct D { B operator*(); };
> +using CD = const D;
> +
> +SA( ! __is_invocable( reference_wrapper<D> ) );
> +SA( ! __is_invocable( reference_wrapper<D>& ) );
> +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> +SA( ! __is_invocable( reference_wrapper<D>* ) );
> +SA( ! __is_invocable( reference_wrapper<D*> ) );
> +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> +
> +std::function<void()> fn = []() {};
> +auto refwrap = std::ref(fn);
> +
> +SA(   __is_invocable( decltype(fn) ) );
> +SA(   __is_invocable( decltype(refwrap) ) );
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> new file mode 100644
> index 00000000000..8699b0a53ca
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> @@ -0,0 +1,51 @@
> +// { dg-do compile { target c++11 } }
> +// __is_invocable should handle incomplete class correctly.
> +
> +#define SA(X) static_assert((X),#X)
> +
> +struct Incomplete;
> +
> +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> +
> +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> +
> +template <typename T>
> +struct Holder { T t; };
> +
> +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> +
> +// Define Incomplete, which is now not incomplete.
> +struct Incomplete { void operator()(); };
> +
> +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> new file mode 100644
> index 00000000000..d1efccf08f8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> @@ -0,0 +1,33 @@
> +// { dg-do compile { target c++11 } }
> +// Failed access check should be a substitution failure, not an error.
> +
> +#define SA(X) static_assert((X),#X)
> +
> +template<bool B>
> +struct bool_constant { static constexpr bool value = B; };
> +
> +template<typename _Fn, typename... _ArgTypes>
> +struct is_invocable
> +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> +{ };
> +
> +#if __cpp_variable_templates
> +template<typename _Fn, typename... _ArgTypes>
> +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> +#endif
> +
> +class Private
> +{
> +  void operator()() const
> +  {
> +    SA( ! is_invocable<Private>::value );
> +#if __cpp_variable_templates
> +    SA( ! is_invocable_v<Private> );
> +#endif
> +  }
> +};
> +
> +SA( ! is_invocable<Private>::value );
> +#if __cpp_variable_templates
> +SA( ! is_invocable_v<Private> );
> +#endif
> -- 
> 2.44.0
> 
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-02-28 19:26                 ` [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
@ 2024-03-08 17:22                   ` Patrick Palka
  2024-03-08 17:35                     ` Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Patrick Palka @ 2024-03-08 17:22 UTC (permalink / raw)
  To: Ken Matsui; +Cc: gcc-patches, libstdc++

On Wed, 28 Feb 2024, Ken Matsui wrote:

> This patch optimizes the compilation performance of
> std::is_nothrow_invocable by dispatching to the new
> __is_nothrow_invocable built-in trait.
> 
> libstdc++-v3/ChangeLog:
> 
> 	* include/std/type_traits (is_nothrow_invocable): Use
> 	__is_nothrow_invocable built-in trait.
> 	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
> 	Handle the new error from __is_nothrow_invocable.
> 	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
> 	Likewise.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>  libstdc++-v3/include/std/type_traits                          | 4 ++++
>  .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
>  .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
>  3 files changed, 6 insertions(+)
> 
> diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> index 9af233bcc75..093d85a51a8 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -3265,8 +3265,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    /// std::is_nothrow_invocable
>    template<typename _Fn, typename... _ArgTypes>
>      struct is_nothrow_invocable
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable)
> +    : public __bool_constant<__is_nothrow_invocable(_Fn, _ArgTypes...)>
> +#else
>      : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
>  	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
> +#endif
>      {
>        static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
>  	"_Fn must be a complete class or an unbounded array");
> diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
> index 3c225883eaf..3f8542dd366 100644
> --- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
> @@ -18,6 +18,7 @@
>  // <http://www.gnu.org/licenses/>.
>  
>  // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> +// { dg-prune-output "invalid use of incomplete type" }

Is the error coming somewhere from the new build_invoke function?  That'd
be surprising since we pass tf_none to it, which should suppress such
errors.  (You can probably break on cxx_incomplete_type_diagnostic to
find out where it's coming from.)

>  
>  #include <type_traits>
>  
> diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
> index 5a728bfa03b..d3bdf08448b 100644
> --- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
> @@ -18,6 +18,7 @@
>  // <http://www.gnu.org/licenses/>.
>  
>  // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> +// { dg-prune-output "invalid use of incomplete type" }
>  
>  #include <type_traits>
>  
> -- 
> 2.44.0
> 
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-03-08 17:22                   ` Patrick Palka
@ 2024-03-08 17:35                     ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-03-08 17:35 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Ken Matsui, gcc-patches, libstdc++

On Fri, Mar 8, 2024 at 9:22 AM Patrick Palka <ppalka@redhat.com> wrote:
>
> On Wed, 28 Feb 2024, Ken Matsui wrote:
>
> > This patch optimizes the compilation performance of
> > std::is_nothrow_invocable by dispatching to the new
> > __is_nothrow_invocable built-in trait.
> >
> > libstdc++-v3/ChangeLog:
> >
> >       * include/std/type_traits (is_nothrow_invocable): Use
> >       __is_nothrow_invocable built-in trait.
> >       * testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
> >       Handle the new error from __is_nothrow_invocable.
> >       * testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
> >       Likewise.
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >  libstdc++-v3/include/std/type_traits                          | 4 ++++
> >  .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
> >  .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
> >  3 files changed, 6 insertions(+)
> >
> > diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> > index 9af233bcc75..093d85a51a8 100644
> > --- a/libstdc++-v3/include/std/type_traits
> > +++ b/libstdc++-v3/include/std/type_traits
> > @@ -3265,8 +3265,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >    /// std::is_nothrow_invocable
> >    template<typename _Fn, typename... _ArgTypes>
> >      struct is_nothrow_invocable
> > +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable)
> > +    : public __bool_constant<__is_nothrow_invocable(_Fn, _ArgTypes...)>
> > +#else
> >      : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
> >            __call_is_nothrow_<_Fn, _ArgTypes...>>::type
> > +#endif
> >      {
> >        static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
> >       "_Fn must be a complete class or an unbounded array");
> > diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
> > index 3c225883eaf..3f8542dd366 100644
> > --- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
> > +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
> > @@ -18,6 +18,7 @@
> >  // <http://www.gnu.org/licenses/>.
> >
> >  // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> > +// { dg-prune-output "invalid use of incomplete type" }
>
> Is the error coming somewhere from the new build_invoke function?  That'd
> be surprising since we pass tf_none to it, which should suppress such
> errors.  (You can probably break on cxx_incomplete_type_diagnostic to
> find out where it's coming from.)

Hi Patrick,

Thank you for your review!

This error comes from cxx_incomplete_type_diagnostic in
check_trait_type, i.e., before the new build_invoke function.  As we
discussed previously, it would be better if we could produce
diagnostics that are in harmony with libstdc++.

>
> >
> >  #include <type_traits>
> >
> > diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
> > index 5a728bfa03b..d3bdf08448b 100644
> > --- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
> > +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
> > @@ -18,6 +18,7 @@
> >  // <http://www.gnu.org/licenses/>.
> >
> >  // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> > +// { dg-prune-output "invalid use of incomplete type" }
> >
> >  #include <type_traits>
> >
> > --
> > 2.44.0
> >
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 23/26] c++: Implement __is_invocable built-in trait
  2024-03-08 17:17                   ` Patrick Palka
@ 2024-03-08 17:38                     ` Ken Matsui
  2024-03-15  1:53                     ` Ken Matsui
  1 sibling, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-03-08 17:38 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Ken Matsui, gcc-patches, libstdc++

On Fri, Mar 8, 2024 at 9:17 AM Patrick Palka <ppalka@redhat.com> wrote:
>
> On Wed, 28 Feb 2024, Ken Matsui wrote:
>
> > This patch implements built-in trait for std::is_invocable.
> >
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __is_invocable.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >       * cp-tree.h (build_invoke): New function.
> >       * method.cc (build_invoke): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >       * g++.dg/ext/is_invocable1.C: New test.
> >       * g++.dg/ext/is_invocable2.C: New test.
> >       * g++.dg/ext/is_invocable3.C: New test.
> >       * g++.dg/ext/is_invocable4.C: New test.
>
> Thanks, this looks great!  This generic build_invoke function could be
> used for invoke_result etc as well, and it could also cache the built-up
> call across __is_invocable and __is_nothrow_invocable checks on the same
> arguments (which is a common pattern in the standard library).  LGTM

Thank you!!!  Yes, I will also work on those features!

>
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >  gcc/cp/constraint.cc                     |   6 +
> >  gcc/cp/cp-trait.def                      |   1 +
> >  gcc/cp/cp-tree.h                         |   2 +
> >  gcc/cp/method.cc                         | 132 +++++++++
> >  gcc/cp/semantics.cc                      |   4 +
> >  gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >  gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >  gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >  gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >  gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >  10 files changed, 720 insertions(+)
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index 23ea66d9c12..c87b126fdb1 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
> >      case CPTK_IS_FUNCTION:
> >        inform (loc, "  %qT is not a function", t1);
> >        break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >      case CPTK_IS_LAYOUT_COMPATIBLE:
> >        inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >        break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index 85056c8140b..6cb2b55f4ea 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >  DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >  DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >  DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >  DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >  DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >  DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 334c11396c2..261d3a71faa 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7334,6 +7334,8 @@ extern tree get_copy_assign                     (tree);
> >  extern tree get_default_ctor                 (tree);
> >  extern tree get_dtor                         (tree, tsubst_flags_t);
> >  extern tree build_stub_object                        (tree);
> > +extern tree build_invoke                     (tree, const_tree,
> > +                                              tsubst_flags_t);
> >  extern tree strip_inheriting_ctors           (tree);
> >  extern tree inherited_ctor_binfo             (tree);
> >  extern bool base_ctor_omit_inherited_parms   (tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 98c10e6a8b5..953f1bed6fc 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,138 @@ build_trait_object (tree type)
> >    return build_stub_object (type);
> >  }
> >
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (fn_type == error_mark_node || arg_types == error_mark_node)
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +     fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    /* Only a pointer to data member with one argument is invocable.  */
>
> Just one note, if/when build_invoke gets a caller that passes tf_error
> (for e.g. elaborating why is_invocable is false, or why invoke_result
> is ill-formed) these error_mark_node code paths will need to issue
> diagnostics b.

Oh I see.  I will update this patch.  Thank you!

>
> > +    return error_mark_node;
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    /* A pointer to member function with no arguments is not invocable.  */
> > +    return error_mark_node;
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +        {
> > +       if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
> > +         return error_mark_node;
> > +        }
> > +      else if (!CLASS_TYPE_P (datum_type))
> > +     return error_mark_node;
> > +
> > +      bool is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +     {
> > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +       if (decl_in_std_namespace_p (datum_decl))
> > +         {
> > +           const_tree name = DECL_NAME (datum_decl);
> > +           if (name && (id_equal (name, "reference_wrapper")))
> > +             {
> > +               /* Retrieve T from std::reference_wrapper<T>,
> > +                  i.e., decltype(datum.get()).  */
> > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +               is_refwrap = true;
> > +             }
> > +         }
> > +     }
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
> > +      tree fn_expr = build_trait_object (fn_type);
> > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > +
> > +      if (error_operand_p (ptrmem_expr) && !is_refwrap)
> > +     {
> > +       tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > +       const bool ptrmem_is_base_of_datum =
> > +         (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > +          && NON_UNION_CLASS_TYPE_P (datum_type)
> > +          && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > +                                                         datum_type)
> > +              || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > +
> > +       if (!ptrmem_is_base_of_datum)
> > +         {
> > +           /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > +           datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > +                                              RO_UNARY_STAR, NULL_TREE,
> > +                                              complain);
> > +           /* Rebuild ptrmem_expr.  */
> > +           ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
> > +                                                complain);
> > +         }
> > +     }
> > +      /* 1.1 & 1.4: Otherwise.  */
> > +
> > +      if (error_operand_p (ptrmem_expr))
> > +     return error_mark_node;
> > +
> > +      if (is_ptrdatamem)
> > +     return ptrmem_expr;
> > +    }
> > +
> > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > +     function, the first argument, which is the object, is not arguments to
> > +     the function.  */
> > +  releasing_vec args;
> > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > +    {
> > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > +      tree arg = build_trait_object (arg_type);
> > +      vec_safe_push (args, arg);
> > +    }
> > +
> > +  tree invoke_expr;
> > +  if (is_ptrmemfunc)
> > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > +                                                complain);
> > +  else  /* 1.7.  */
> > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > +                                 false, complain);
> > +  return invoke_expr;
> > +}
> > +
> >  /* Determine which function will be called when looking up NAME in TYPE,
> >     called with a single ARGTYPE argument, or no argument if ARGTYPE is
> >     null.  FLAGS and COMPLAIN are as for build_new_method_call.
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index 7242db75248..149c0631d62 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
> >      case CPTK_IS_FUNCTION:
> >        return type_code1 == FUNCTION_TYPE;
> >
> > +    case CPTK_IS_INVOCABLE:
> > +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> > +
> >      case CPTK_IS_LAYOUT_COMPATIBLE:
> >        return layout_compatible_type_p (type1, type2);
> >
> > @@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> >        break;
> >
> >      case CPTK_IS_CONVERTIBLE:
> > +    case CPTK_IS_INVOCABLE:
> >      case CPTK_IS_NOTHROW_ASSIGNABLE:
> >      case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
> >      case CPTK_IS_NOTHROW_CONVERTIBLE:
> > diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > index 7f7b27f7aa7..d2a7ebdf25c 100644
> > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > @@ -104,6 +104,9 @@
> >  #if !__has_builtin (__is_function)
> >  # error "__has_builtin (__is_function) failed"
> >  #endif
> > +#if !__has_builtin (__is_invocable)
> > +# error "__has_builtin (__is_invocable) failed"
> > +#endif
> >  #if !__has_builtin (__is_layout_compatible)
> >  # error "__has_builtin (__is_layout_compatible) failed"
> >  #endif
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > new file mode 100644
> > index 00000000000..d21ae1d1958
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > @@ -0,0 +1,349 @@
> > +// { dg-do compile { target c++11 } }
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +using func_type_v0 = void(*)();
> > +
> > +SA(   __is_invocable( func_type_v0 ) );
> > +SA( ! __is_invocable( func_type_v0, int ) );
> > +
> > +using func_type_i0 = int(*)();
> > +
> > +SA(   __is_invocable( func_type_i0 ) );
> > +SA( ! __is_invocable( func_type_i0, int ) );
> > +
> > +using func_type_l0 = int&(*)();
> > +
> > +SA(   __is_invocable( func_type_l0 ) );
> > +SA( ! __is_invocable( func_type_l0(int) ) );
> > +
> > +using func_type_ii = int(*)(int);
> > +
> > +SA( ! __is_invocable( func_type_ii ) );
> > +SA(   __is_invocable( func_type_ii, int ) );
> > +
> > +using func_type_il = int(*)(int&);
> > +
> > +SA( ! __is_invocable( func_type_il ) );
> > +SA( ! __is_invocable( func_type_il, int ) );
> > +SA(   __is_invocable( func_type_il, int& ) );
> > +
> > +using func_type_ir = int(*)(int&&);
> > +
> > +SA( ! __is_invocable( func_type_ir ) );
> > +SA( ! __is_invocable( func_type_ir, int& ) );
> > +SA(   __is_invocable( func_type_ir, int ) );
> > +SA(   __is_invocable( func_type_ir, int&& ) );
> > +
> > +struct A { };
> > +
> > +using mem_type_i = int A::*;
> > +
> > +SA( ! __is_invocable( mem_type_i ) );
> > +SA( ! __is_invocable( mem_type_i, int ) );
> > +SA( ! __is_invocable( mem_type_i, int* ) );
> > +SA( ! __is_invocable( mem_type_i, int& ) );
> > +SA( ! __is_invocable( mem_type_i, int&& ) );
> > +SA(   __is_invocable( mem_type_i, A ) );
> > +SA(   __is_invocable( mem_type_i, A* ) );
> > +SA(   __is_invocable( mem_type_i, A& ) );
> > +SA(   __is_invocable( mem_type_i, A&& ) );
> > +SA(   __is_invocable( mem_type_i, const A& ) );
> > +SA( ! __is_invocable( mem_type_i, A&, int ) );
> > +
> > +using memfun_type_i = int (A::*)();
> > +
> > +SA( ! __is_invocable( memfun_type_i ) );
> > +SA( ! __is_invocable( memfun_type_i, int ) );
> > +SA( ! __is_invocable( memfun_type_i, int* ) );
> > +SA( ! __is_invocable( memfun_type_i, int& ) );
> > +SA( ! __is_invocable( memfun_type_i, int&& ) );
> > +SA(   __is_invocable( memfun_type_i, A ) );
> > +SA(   __is_invocable( memfun_type_i, A* ) );
> > +SA(   __is_invocable( memfun_type_i, A& ) );
> > +SA(   __is_invocable( memfun_type_i, A&& ) );
> > +SA( ! __is_invocable( memfun_type_i, const A& ) );
> > +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> > +
> > +using memfun_type_ic = int (A::*)() const;
> > +
> > +SA( ! __is_invocable( memfun_type_ic ) );
> > +SA( ! __is_invocable( memfun_type_ic, int ) );
> > +SA( ! __is_invocable( memfun_type_ic, int& ) );
> > +SA(   __is_invocable( memfun_type_ic, A& ) );
> > +SA(   __is_invocable( memfun_type_ic, A* ) );
> > +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> > +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> > +SA(   __is_invocable( memfun_type_ic, const A& ) );
> > +SA(   __is_invocable( memfun_type_ic, const A* ) );
> > +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> > +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> > +
> > +using memfun_type_iic = int& (A::*)(int&) const;
> > +
> > +SA( ! __is_invocable( memfun_type_iic ) );
> > +SA( ! __is_invocable( memfun_type_iic, int ) );
> > +SA( ! __is_invocable( memfun_type_iic, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> > +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> > +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> > +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> > +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> > +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> > +
> > +struct B {
> > +  int& operator()();
> > +  long& operator()() const;
> > +  bool& operator()(int);
> > +private:
> > +  void operator()(int, int);
> > +};
> > +using CB = const B;
> > +
> > +SA(   __is_invocable( B ) );
> > +SA(   __is_invocable( B& ) );
> > +SA(   __is_invocable( B&& ) );
> > +SA( ! __is_invocable( B* ) );
> > +SA(   __is_invocable( CB ) );
> > +SA(   __is_invocable( CB& ) );
> > +SA( ! __is_invocable( CB* ) );
> > +
> > +SA(   __is_invocable( B, int ) );
> > +SA(   __is_invocable( B&, int ) );
> > +SA(   __is_invocable( B&&, int ) );
> > +SA( ! __is_invocable( B*, int ) );
> > +SA( ! __is_invocable( CB, int ) );
> > +SA( ! __is_invocable( CB&, int ) );
> > +SA( ! __is_invocable( CB*, int ) );
> > +
> > +SA( ! __is_invocable( B, int, int ) );
> > +SA( ! __is_invocable( B&, int, int ) );
> > +SA( ! __is_invocable( B&&, int, int ) );
> > +SA( ! __is_invocable( B*, int, int ) );
> > +SA( ! __is_invocable( CB, int, int ) );
> > +SA( ! __is_invocable( CB&, int, int ) );
> > +SA( ! __is_invocable( CB*, int, int ) );
> > +
> > +struct C : B { int& operator()() = delete; };
> > +using CC = const C;
> > +
> > +SA( ! __is_invocable( C ) );
> > +SA( ! __is_invocable( C& ) );
> > +SA( ! __is_invocable( C&& ) );
> > +SA( ! __is_invocable( C* ) );
> > +SA( ! __is_invocable( CC ) );
> > +SA( ! __is_invocable( CC& ) );
> > +SA( ! __is_invocable( CC* ) );
> > +
> > +struct D { B operator*(); };
> > +using CD = const D;
> > +
> > +SA( ! __is_invocable( D ) );
> > +
> > +struct E { void v(); };
> > +using CE = const E;
> > +
> > +SA( ! __is_invocable( E ) );
> > +SA( ! __is_invocable( void (E::*)() ) );
> > +SA(   __is_invocable( void (E::*)(), E ) );
> > +SA(   __is_invocable( void (E::*)(), E* ) );
> > +SA( ! __is_invocable( void (E::*)(), CE ) );
> > +
> > +struct F : E {};
> > +using CF = const F;
> > +
> > +SA( ! __is_invocable( F ) );
> > +SA(   __is_invocable( void (E::*)(), F ) );
> > +SA(   __is_invocable( void (E::*)(), F* ) );
> > +SA( ! __is_invocable( void (E::*)(), CF ) );
> > +
> > +struct G { E operator*(); };
> > +using CG = const G;
> > +
> > +SA( ! __is_invocable( G ) );
> > +SA(   __is_invocable( void (E::*)(), G ) );
> > +SA( ! __is_invocable( void (E::*)(), G* ) );
> > +SA( ! __is_invocable( void (E::*)(), CG ) );
> > +
> > +struct H { E& operator*(); };
> > +using CH = const H;
> > +
> > +SA( ! __is_invocable( H ) );
> > +SA(   __is_invocable( void (E::*)(), H ) );
> > +SA( ! __is_invocable( void (E::*)(), H* ) );
> > +SA( ! __is_invocable( void (E::*)(), CH ) );
> > +
> > +struct I { E&& operator*(); };
> > +using CI = const I;
> > +
> > +SA( ! __is_invocable( I ) );
> > +SA(   __is_invocable( void (E::*)(), I ) );
> > +SA( ! __is_invocable( void (E::*)(), I* ) );
> > +SA( ! __is_invocable( void (E::*)(), CI ) );
> > +
> > +struct K { E* operator*(); };
> > +using CK = const K;
> > +
> > +SA( ! __is_invocable( K ) );
> > +SA( ! __is_invocable( void (E::*)(), K ) );
> > +SA( ! __is_invocable( void (E::*)(), K* ) );
> > +SA( ! __is_invocable( void (E::*)(), CK ) );
> > +
> > +struct L { CE operator*(); };
> > +using CL = const L;
> > +
> > +SA( ! __is_invocable( L ) );
> > +SA( ! __is_invocable( void (E::*)(), L ) );
> > +SA( ! __is_invocable( void (E::*)(), L* ) );
> > +SA( ! __is_invocable( void (E::*)(), CL ) );
> > +
> > +struct M {
> > +  int i;
> > +private:
> > +  long l;
> > +};
> > +using CM = const M;
> > +
> > +SA( ! __is_invocable( M ) );
> > +SA( ! __is_invocable( M& ) );
> > +SA( ! __is_invocable( M&& ) );
> > +SA( ! __is_invocable( M* ) );
> > +SA( ! __is_invocable( CM ) );
> > +SA( ! __is_invocable( CM& ) );
> > +SA( ! __is_invocable( CM* ) );
> > +
> > +SA( ! __is_invocable( int M::* ) );
> > +SA(   __is_invocable( int M::*, M ) );
> > +SA(   __is_invocable( int M::*, M& ) );
> > +SA(   __is_invocable( int M::*, M&& ) );
> > +SA(   __is_invocable( int M::*, M* ) );
> > +SA(   __is_invocable( int M::*, CM ) );
> > +SA(   __is_invocable( int M::*, CM& ) );
> > +SA(   __is_invocable( int M::*, CM* ) );
> > +SA( ! __is_invocable( int M::*, int ) );
> > +
> > +SA( ! __is_invocable( int CM::* ) );
> > +SA(   __is_invocable( int CM::*, M ) );
> > +SA(   __is_invocable( int CM::*, M& ) );
> > +SA(   __is_invocable( int CM::*, M&& ) );
> > +SA(   __is_invocable( int CM::*, M* ) );
> > +SA(   __is_invocable( int CM::*, CM ) );
> > +SA(   __is_invocable( int CM::*, CM& ) );
> > +SA(   __is_invocable( int CM::*, CM* ) );
> > +SA( ! __is_invocable( int CM::*, int ) );
> > +
> > +SA( ! __is_invocable( long M::* ) );
> > +SA(   __is_invocable( long M::*, M ) );
> > +SA(   __is_invocable( long M::*, M& ) );
> > +SA(   __is_invocable( long M::*, M&& ) );
> > +SA(   __is_invocable( long M::*, M* ) );
> > +SA(   __is_invocable( long M::*, CM ) );
> > +SA(   __is_invocable( long M::*, CM& ) );
> > +SA(   __is_invocable( long M::*, CM* ) );
> > +SA( ! __is_invocable( long M::*, long ) );
> > +
> > +SA( ! __is_invocable( long CM::* ) );
> > +SA(   __is_invocable( long CM::*, M ) );
> > +SA(   __is_invocable( long CM::*, M& ) );
> > +SA(   __is_invocable( long CM::*, M&& ) );
> > +SA(   __is_invocable( long CM::*, M* ) );
> > +SA(   __is_invocable( long CM::*, CM ) );
> > +SA(   __is_invocable( long CM::*, CM& ) );
> > +SA(   __is_invocable( long CM::*, CM* ) );
> > +SA( ! __is_invocable( long CM::*, long ) );
> > +
> > +SA( ! __is_invocable( short M::* ) );
> > +SA(   __is_invocable( short M::*, M ) );
> > +SA(   __is_invocable( short M::*, M& ) );
> > +SA(   __is_invocable( short M::*, M&& ) );
> > +SA(   __is_invocable( short M::*, M* ) );
> > +SA(   __is_invocable( short M::*, CM ) );
> > +SA(   __is_invocable( short M::*, CM& ) );
> > +SA(   __is_invocable( short M::*, CM* ) );
> > +SA( ! __is_invocable( short M::*, short ) );
> > +
> > +SA( ! __is_invocable( short CM::* ) );
> > +SA(   __is_invocable( short CM::*, M ) );
> > +SA(   __is_invocable( short CM::*, M& ) );
> > +SA(   __is_invocable( short CM::*, M&& ) );
> > +SA(   __is_invocable( short CM::*, M* ) );
> > +SA(   __is_invocable( short CM::*, CM ) );
> > +SA(   __is_invocable( short CM::*, CM& ) );
> > +SA(   __is_invocable( short CM::*, CM* ) );
> > +SA( ! __is_invocable( short CM::*, short ) );
> > +
> > +struct N { M operator*(); };
> > +SA(   __is_invocable( int M::*, N ) );
> > +SA( ! __is_invocable( int M::*, N* ) );
> > +
> > +struct O { M& operator*(); };
> > +SA(   __is_invocable( int M::*, O ) );
> > +SA( ! __is_invocable( int M::*, O* ) );
> > +
> > +struct P { M&& operator*(); };
> > +SA(   __is_invocable( int M::*, P ) );
> > +SA( ! __is_invocable( int M::*, P* ) );
> > +
> > +struct Q { M* operator*(); };
> > +SA( ! __is_invocable( int M::*, Q ) );
> > +SA( ! __is_invocable( int M::*, Q* ) );
> > +
> > +struct R { void operator()(int = 0); };
> > +
> > +SA(   __is_invocable( R ) );
> > +SA(   __is_invocable( R, int ) );
> > +SA( ! __is_invocable( R, int, int ) );
> > +
> > +struct S { void operator()(int, ...); };
> > +
> > +SA( ! __is_invocable( S ) );
> > +SA(   __is_invocable( S, int ) );
> > +SA(   __is_invocable( S, int, int ) );
> > +SA(   __is_invocable( S, int, int, int ) );
> > +
> > +void fn1() {}
> > +
> > +SA(   __is_invocable( decltype(fn1) ) );
> > +
> > +void fn2(int arr[10]);
> > +
> > +SA(   __is_invocable( decltype(fn2), int[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int[] ) );
> > +
> > +auto lambda = []() {};
> > +
> > +SA(   __is_invocable( decltype(lambda) ) );
> > +
> > +template <typename Func, typename... Args>
> > +struct can_invoke {
> > +    static constexpr bool value = __is_invocable( Func, Args... );
> > +};
> > +
> > +SA(   can_invoke<decltype(lambda)>::value );
> > +
> > +struct T {
> > +  void func() const {}
> > +  int data;
> > +};
> > +
> > +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> > +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> > +
> > +struct U { };
> > +struct V : U { U& operator*() = delete; };
> > +SA(   __is_invocable( int U::*, V ) );
> > +
> > +struct W : private U { U& operator*(); };
> > +SA( ! __is_invocable( int U::*, W ) );
> > +
> > +struct X { int m; };
> > +struct Y { X& operator*(); };
> > +struct Z : Y { };
> > +SA(   __is_invocable(int X::*, Z) );
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > new file mode 100644
> > index 00000000000..a68aefd3e13
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > @@ -0,0 +1,139 @@
> > +// { dg-do compile { target c++11 } }
> > +// __is_invocable should handle std::reference_wrapper correctly.
> > +
> > +#include <functional>
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +using std::reference_wrapper;
> > +
> > +using func_type_v0 = void(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> > +
> > +using func_type_i0 = int(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> > +
> > +using func_type_l0 = int&(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> > +
> > +using func_type_ii = int(*)(int);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> > +
> > +using func_type_il = int(*)(int&);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> > +
> > +using func_type_ir = int(*)(int&&);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> > +
> > +struct A { };
> > +
> > +using mem_type_i = int A::*;
> > +
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> > +
> > +using memfun_type_i = int (A::*)();
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> > +
> > +using memfun_type_ic = int (A::*)() const;
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> > +
> > +using memfun_type_iic = int& (A::*)(int&) const;
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> > +
> > +struct B {
> > +  int& operator()();
> > +  long& operator()() const;
> > +  bool& operator()(int);
> > +private:
> > +  void operator()(int, int);
> > +};
> > +using CB = const B;
> > +
> > +SA(   __is_invocable( reference_wrapper<B> ) );
> > +SA(   __is_invocable( reference_wrapper<B>& ) );
> > +SA(   __is_invocable( reference_wrapper<B>&& ) );
> > +SA(   __is_invocable( reference_wrapper<CB> ) );
> > +SA(   __is_invocable( reference_wrapper<CB>& ) );
> > +SA(   __is_invocable( reference_wrapper<B>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> > +
> > +struct C : B { int& operator()() = delete; };
> > +using CC = const C;
> > +
> > +SA( ! __is_invocable( reference_wrapper<C> ) );
> > +SA( ! __is_invocable( reference_wrapper<C>& ) );
> > +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> > +SA( ! __is_invocable( reference_wrapper<CC> ) );
> > +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> > +
> > +struct D { B operator*(); };
> > +using CD = const D;
> > +
> > +SA( ! __is_invocable( reference_wrapper<D> ) );
> > +SA( ! __is_invocable( reference_wrapper<D>& ) );
> > +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> > +SA( ! __is_invocable( reference_wrapper<D>* ) );
> > +SA( ! __is_invocable( reference_wrapper<D*> ) );
> > +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> > +
> > +std::function<void()> fn = []() {};
> > +auto refwrap = std::ref(fn);
> > +
> > +SA(   __is_invocable( decltype(fn) ) );
> > +SA(   __is_invocable( decltype(refwrap) ) );
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > new file mode 100644
> > index 00000000000..8699b0a53ca
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > @@ -0,0 +1,51 @@
> > +// { dg-do compile { target c++11 } }
> > +// __is_invocable should handle incomplete class correctly.
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +struct Incomplete;
> > +
> > +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > +
> > +template <typename T>
> > +struct Holder { T t; };
> > +
> > +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> > +
> > +// Define Incomplete, which is now not incomplete.
> > +struct Incomplete { void operator()(); };
> > +
> > +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > new file mode 100644
> > index 00000000000..d1efccf08f8
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > @@ -0,0 +1,33 @@
> > +// { dg-do compile { target c++11 } }
> > +// Failed access check should be a substitution failure, not an error.
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +template<bool B>
> > +struct bool_constant { static constexpr bool value = B; };
> > +
> > +template<typename _Fn, typename... _ArgTypes>
> > +struct is_invocable
> > +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> > +{ };
> > +
> > +#if __cpp_variable_templates
> > +template<typename _Fn, typename... _ArgTypes>
> > +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> > +#endif
> > +
> > +class Private
> > +{
> > +  void operator()() const
> > +  {
> > +    SA( ! is_invocable<Private>::value );
> > +#if __cpp_variable_templates
> > +    SA( ! is_invocable_v<Private> );
> > +#endif
> > +  }
> > +};
> > +
> > +SA( ! is_invocable<Private>::value );
> > +#if __cpp_variable_templates
> > +SA( ! is_invocable_v<Private> );
> > +#endif
> > --
> > 2.44.0
> >
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 23/26] c++: Implement __is_invocable built-in trait
  2024-03-08 17:17                   ` Patrick Palka
  2024-03-08 17:38                     ` Ken Matsui
@ 2024-03-15  1:53                     ` Ken Matsui
  2024-03-15  6:34                       ` Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-03-15  1:53 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Fri, Mar 8, 2024 at 9:17 AM Patrick Palka <ppalka@redhat.com> wrote:
>
> On Wed, 28 Feb 2024, Ken Matsui wrote:
>
> > This patch implements built-in trait for std::is_invocable.
> >
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __is_invocable.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >       * cp-tree.h (build_invoke): New function.
> >       * method.cc (build_invoke): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >       * g++.dg/ext/is_invocable1.C: New test.
> >       * g++.dg/ext/is_invocable2.C: New test.
> >       * g++.dg/ext/is_invocable3.C: New test.
> >       * g++.dg/ext/is_invocable4.C: New test.
>
> Thanks, this looks great!  This generic build_invoke function could be
> used for invoke_result etc as well, and it could also cache the built-up
> call across __is_invocable and __is_nothrow_invocable checks on the same
> arguments (which is a common pattern in the standard library).  LGTM
>
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >  gcc/cp/constraint.cc                     |   6 +
> >  gcc/cp/cp-trait.def                      |   1 +
> >  gcc/cp/cp-tree.h                         |   2 +
> >  gcc/cp/method.cc                         | 132 +++++++++
> >  gcc/cp/semantics.cc                      |   4 +
> >  gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >  gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >  gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >  gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >  gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >  10 files changed, 720 insertions(+)
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index 23ea66d9c12..c87b126fdb1 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
> >      case CPTK_IS_FUNCTION:
> >        inform (loc, "  %qT is not a function", t1);
> >        break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >      case CPTK_IS_LAYOUT_COMPATIBLE:
> >        inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >        break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index 85056c8140b..6cb2b55f4ea 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >  DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >  DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >  DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >  DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >  DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >  DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 334c11396c2..261d3a71faa 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7334,6 +7334,8 @@ extern tree get_copy_assign                     (tree);
> >  extern tree get_default_ctor                 (tree);
> >  extern tree get_dtor                         (tree, tsubst_flags_t);
> >  extern tree build_stub_object                        (tree);
> > +extern tree build_invoke                     (tree, const_tree,
> > +                                              tsubst_flags_t);
> >  extern tree strip_inheriting_ctors           (tree);
> >  extern tree inherited_ctor_binfo             (tree);
> >  extern bool base_ctor_omit_inherited_parms   (tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 98c10e6a8b5..953f1bed6fc 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,138 @@ build_trait_object (tree type)
> >    return build_stub_object (type);
> >  }
> >
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (fn_type == error_mark_node || arg_types == error_mark_node)
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +     fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    /* Only a pointer to data member with one argument is invocable.  */
>
> Just one note, if/when build_invoke gets a caller that passes tf_error
> (for e.g. elaborating why is_invocable is false, or why invoke_result
> is ill-formed) these error_mark_node code paths will need to issue
> diagnostics b.

Which function should we use for diagnostics - error, error_at, or
something else?  If we opt for error_at, which location information do
we want to use?

>
> > +    return error_mark_node;
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    /* A pointer to member function with no arguments is not invocable.  */
> > +    return error_mark_node;
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +        {
> > +       if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
> > +         return error_mark_node;
> > +        }
> > +      else if (!CLASS_TYPE_P (datum_type))
> > +     return error_mark_node;
> > +
> > +      bool is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +     {
> > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +       if (decl_in_std_namespace_p (datum_decl))
> > +         {
> > +           const_tree name = DECL_NAME (datum_decl);
> > +           if (name && (id_equal (name, "reference_wrapper")))
> > +             {
> > +               /* Retrieve T from std::reference_wrapper<T>,
> > +                  i.e., decltype(datum.get()).  */
> > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +               is_refwrap = true;
> > +             }
> > +         }
> > +     }
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
> > +      tree fn_expr = build_trait_object (fn_type);
> > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > +
> > +      if (error_operand_p (ptrmem_expr) && !is_refwrap)
> > +     {
> > +       tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > +       const bool ptrmem_is_base_of_datum =
> > +         (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > +          && NON_UNION_CLASS_TYPE_P (datum_type)
> > +          && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > +                                                         datum_type)
> > +              || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > +
> > +       if (!ptrmem_is_base_of_datum)
> > +         {
> > +           /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > +           datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > +                                              RO_UNARY_STAR, NULL_TREE,
> > +                                              complain);
> > +           /* Rebuild ptrmem_expr.  */
> > +           ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
> > +                                                complain);
> > +         }
> > +     }
> > +      /* 1.1 & 1.4: Otherwise.  */
> > +
> > +      if (error_operand_p (ptrmem_expr))
> > +     return error_mark_node;
> > +
> > +      if (is_ptrdatamem)
> > +     return ptrmem_expr;
> > +    }
> > +
> > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > +     function, the first argument, which is the object, is not arguments to
> > +     the function.  */
> > +  releasing_vec args;
> > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > +    {
> > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > +      tree arg = build_trait_object (arg_type);
> > +      vec_safe_push (args, arg);
> > +    }
> > +
> > +  tree invoke_expr;
> > +  if (is_ptrmemfunc)
> > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > +                                                complain);
> > +  else  /* 1.7.  */
> > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > +                                 false, complain);
> > +  return invoke_expr;
> > +}
> > +
> >  /* Determine which function will be called when looking up NAME in TYPE,
> >     called with a single ARGTYPE argument, or no argument if ARGTYPE is
> >     null.  FLAGS and COMPLAIN are as for build_new_method_call.
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index 7242db75248..149c0631d62 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
> >      case CPTK_IS_FUNCTION:
> >        return type_code1 == FUNCTION_TYPE;
> >
> > +    case CPTK_IS_INVOCABLE:
> > +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> > +
> >      case CPTK_IS_LAYOUT_COMPATIBLE:
> >        return layout_compatible_type_p (type1, type2);
> >
> > @@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> >        break;
> >
> >      case CPTK_IS_CONVERTIBLE:
> > +    case CPTK_IS_INVOCABLE:
> >      case CPTK_IS_NOTHROW_ASSIGNABLE:
> >      case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
> >      case CPTK_IS_NOTHROW_CONVERTIBLE:
> > diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > index 7f7b27f7aa7..d2a7ebdf25c 100644
> > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > @@ -104,6 +104,9 @@
> >  #if !__has_builtin (__is_function)
> >  # error "__has_builtin (__is_function) failed"
> >  #endif
> > +#if !__has_builtin (__is_invocable)
> > +# error "__has_builtin (__is_invocable) failed"
> > +#endif
> >  #if !__has_builtin (__is_layout_compatible)
> >  # error "__has_builtin (__is_layout_compatible) failed"
> >  #endif
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > new file mode 100644
> > index 00000000000..d21ae1d1958
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > @@ -0,0 +1,349 @@
> > +// { dg-do compile { target c++11 } }
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +using func_type_v0 = void(*)();
> > +
> > +SA(   __is_invocable( func_type_v0 ) );
> > +SA( ! __is_invocable( func_type_v0, int ) );
> > +
> > +using func_type_i0 = int(*)();
> > +
> > +SA(   __is_invocable( func_type_i0 ) );
> > +SA( ! __is_invocable( func_type_i0, int ) );
> > +
> > +using func_type_l0 = int&(*)();
> > +
> > +SA(   __is_invocable( func_type_l0 ) );
> > +SA( ! __is_invocable( func_type_l0(int) ) );
> > +
> > +using func_type_ii = int(*)(int);
> > +
> > +SA( ! __is_invocable( func_type_ii ) );
> > +SA(   __is_invocable( func_type_ii, int ) );
> > +
> > +using func_type_il = int(*)(int&);
> > +
> > +SA( ! __is_invocable( func_type_il ) );
> > +SA( ! __is_invocable( func_type_il, int ) );
> > +SA(   __is_invocable( func_type_il, int& ) );
> > +
> > +using func_type_ir = int(*)(int&&);
> > +
> > +SA( ! __is_invocable( func_type_ir ) );
> > +SA( ! __is_invocable( func_type_ir, int& ) );
> > +SA(   __is_invocable( func_type_ir, int ) );
> > +SA(   __is_invocable( func_type_ir, int&& ) );
> > +
> > +struct A { };
> > +
> > +using mem_type_i = int A::*;
> > +
> > +SA( ! __is_invocable( mem_type_i ) );
> > +SA( ! __is_invocable( mem_type_i, int ) );
> > +SA( ! __is_invocable( mem_type_i, int* ) );
> > +SA( ! __is_invocable( mem_type_i, int& ) );
> > +SA( ! __is_invocable( mem_type_i, int&& ) );
> > +SA(   __is_invocable( mem_type_i, A ) );
> > +SA(   __is_invocable( mem_type_i, A* ) );
> > +SA(   __is_invocable( mem_type_i, A& ) );
> > +SA(   __is_invocable( mem_type_i, A&& ) );
> > +SA(   __is_invocable( mem_type_i, const A& ) );
> > +SA( ! __is_invocable( mem_type_i, A&, int ) );
> > +
> > +using memfun_type_i = int (A::*)();
> > +
> > +SA( ! __is_invocable( memfun_type_i ) );
> > +SA( ! __is_invocable( memfun_type_i, int ) );
> > +SA( ! __is_invocable( memfun_type_i, int* ) );
> > +SA( ! __is_invocable( memfun_type_i, int& ) );
> > +SA( ! __is_invocable( memfun_type_i, int&& ) );
> > +SA(   __is_invocable( memfun_type_i, A ) );
> > +SA(   __is_invocable( memfun_type_i, A* ) );
> > +SA(   __is_invocable( memfun_type_i, A& ) );
> > +SA(   __is_invocable( memfun_type_i, A&& ) );
> > +SA( ! __is_invocable( memfun_type_i, const A& ) );
> > +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> > +
> > +using memfun_type_ic = int (A::*)() const;
> > +
> > +SA( ! __is_invocable( memfun_type_ic ) );
> > +SA( ! __is_invocable( memfun_type_ic, int ) );
> > +SA( ! __is_invocable( memfun_type_ic, int& ) );
> > +SA(   __is_invocable( memfun_type_ic, A& ) );
> > +SA(   __is_invocable( memfun_type_ic, A* ) );
> > +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> > +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> > +SA(   __is_invocable( memfun_type_ic, const A& ) );
> > +SA(   __is_invocable( memfun_type_ic, const A* ) );
> > +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> > +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> > +
> > +using memfun_type_iic = int& (A::*)(int&) const;
> > +
> > +SA( ! __is_invocable( memfun_type_iic ) );
> > +SA( ! __is_invocable( memfun_type_iic, int ) );
> > +SA( ! __is_invocable( memfun_type_iic, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> > +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> > +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> > +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> > +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> > +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> > +
> > +struct B {
> > +  int& operator()();
> > +  long& operator()() const;
> > +  bool& operator()(int);
> > +private:
> > +  void operator()(int, int);
> > +};
> > +using CB = const B;
> > +
> > +SA(   __is_invocable( B ) );
> > +SA(   __is_invocable( B& ) );
> > +SA(   __is_invocable( B&& ) );
> > +SA( ! __is_invocable( B* ) );
> > +SA(   __is_invocable( CB ) );
> > +SA(   __is_invocable( CB& ) );
> > +SA( ! __is_invocable( CB* ) );
> > +
> > +SA(   __is_invocable( B, int ) );
> > +SA(   __is_invocable( B&, int ) );
> > +SA(   __is_invocable( B&&, int ) );
> > +SA( ! __is_invocable( B*, int ) );
> > +SA( ! __is_invocable( CB, int ) );
> > +SA( ! __is_invocable( CB&, int ) );
> > +SA( ! __is_invocable( CB*, int ) );
> > +
> > +SA( ! __is_invocable( B, int, int ) );
> > +SA( ! __is_invocable( B&, int, int ) );
> > +SA( ! __is_invocable( B&&, int, int ) );
> > +SA( ! __is_invocable( B*, int, int ) );
> > +SA( ! __is_invocable( CB, int, int ) );
> > +SA( ! __is_invocable( CB&, int, int ) );
> > +SA( ! __is_invocable( CB*, int, int ) );
> > +
> > +struct C : B { int& operator()() = delete; };
> > +using CC = const C;
> > +
> > +SA( ! __is_invocable( C ) );
> > +SA( ! __is_invocable( C& ) );
> > +SA( ! __is_invocable( C&& ) );
> > +SA( ! __is_invocable( C* ) );
> > +SA( ! __is_invocable( CC ) );
> > +SA( ! __is_invocable( CC& ) );
> > +SA( ! __is_invocable( CC* ) );
> > +
> > +struct D { B operator*(); };
> > +using CD = const D;
> > +
> > +SA( ! __is_invocable( D ) );
> > +
> > +struct E { void v(); };
> > +using CE = const E;
> > +
> > +SA( ! __is_invocable( E ) );
> > +SA( ! __is_invocable( void (E::*)() ) );
> > +SA(   __is_invocable( void (E::*)(), E ) );
> > +SA(   __is_invocable( void (E::*)(), E* ) );
> > +SA( ! __is_invocable( void (E::*)(), CE ) );
> > +
> > +struct F : E {};
> > +using CF = const F;
> > +
> > +SA( ! __is_invocable( F ) );
> > +SA(   __is_invocable( void (E::*)(), F ) );
> > +SA(   __is_invocable( void (E::*)(), F* ) );
> > +SA( ! __is_invocable( void (E::*)(), CF ) );
> > +
> > +struct G { E operator*(); };
> > +using CG = const G;
> > +
> > +SA( ! __is_invocable( G ) );
> > +SA(   __is_invocable( void (E::*)(), G ) );
> > +SA( ! __is_invocable( void (E::*)(), G* ) );
> > +SA( ! __is_invocable( void (E::*)(), CG ) );
> > +
> > +struct H { E& operator*(); };
> > +using CH = const H;
> > +
> > +SA( ! __is_invocable( H ) );
> > +SA(   __is_invocable( void (E::*)(), H ) );
> > +SA( ! __is_invocable( void (E::*)(), H* ) );
> > +SA( ! __is_invocable( void (E::*)(), CH ) );
> > +
> > +struct I { E&& operator*(); };
> > +using CI = const I;
> > +
> > +SA( ! __is_invocable( I ) );
> > +SA(   __is_invocable( void (E::*)(), I ) );
> > +SA( ! __is_invocable( void (E::*)(), I* ) );
> > +SA( ! __is_invocable( void (E::*)(), CI ) );
> > +
> > +struct K { E* operator*(); };
> > +using CK = const K;
> > +
> > +SA( ! __is_invocable( K ) );
> > +SA( ! __is_invocable( void (E::*)(), K ) );
> > +SA( ! __is_invocable( void (E::*)(), K* ) );
> > +SA( ! __is_invocable( void (E::*)(), CK ) );
> > +
> > +struct L { CE operator*(); };
> > +using CL = const L;
> > +
> > +SA( ! __is_invocable( L ) );
> > +SA( ! __is_invocable( void (E::*)(), L ) );
> > +SA( ! __is_invocable( void (E::*)(), L* ) );
> > +SA( ! __is_invocable( void (E::*)(), CL ) );
> > +
> > +struct M {
> > +  int i;
> > +private:
> > +  long l;
> > +};
> > +using CM = const M;
> > +
> > +SA( ! __is_invocable( M ) );
> > +SA( ! __is_invocable( M& ) );
> > +SA( ! __is_invocable( M&& ) );
> > +SA( ! __is_invocable( M* ) );
> > +SA( ! __is_invocable( CM ) );
> > +SA( ! __is_invocable( CM& ) );
> > +SA( ! __is_invocable( CM* ) );
> > +
> > +SA( ! __is_invocable( int M::* ) );
> > +SA(   __is_invocable( int M::*, M ) );
> > +SA(   __is_invocable( int M::*, M& ) );
> > +SA(   __is_invocable( int M::*, M&& ) );
> > +SA(   __is_invocable( int M::*, M* ) );
> > +SA(   __is_invocable( int M::*, CM ) );
> > +SA(   __is_invocable( int M::*, CM& ) );
> > +SA(   __is_invocable( int M::*, CM* ) );
> > +SA( ! __is_invocable( int M::*, int ) );
> > +
> > +SA( ! __is_invocable( int CM::* ) );
> > +SA(   __is_invocable( int CM::*, M ) );
> > +SA(   __is_invocable( int CM::*, M& ) );
> > +SA(   __is_invocable( int CM::*, M&& ) );
> > +SA(   __is_invocable( int CM::*, M* ) );
> > +SA(   __is_invocable( int CM::*, CM ) );
> > +SA(   __is_invocable( int CM::*, CM& ) );
> > +SA(   __is_invocable( int CM::*, CM* ) );
> > +SA( ! __is_invocable( int CM::*, int ) );
> > +
> > +SA( ! __is_invocable( long M::* ) );
> > +SA(   __is_invocable( long M::*, M ) );
> > +SA(   __is_invocable( long M::*, M& ) );
> > +SA(   __is_invocable( long M::*, M&& ) );
> > +SA(   __is_invocable( long M::*, M* ) );
> > +SA(   __is_invocable( long M::*, CM ) );
> > +SA(   __is_invocable( long M::*, CM& ) );
> > +SA(   __is_invocable( long M::*, CM* ) );
> > +SA( ! __is_invocable( long M::*, long ) );
> > +
> > +SA( ! __is_invocable( long CM::* ) );
> > +SA(   __is_invocable( long CM::*, M ) );
> > +SA(   __is_invocable( long CM::*, M& ) );
> > +SA(   __is_invocable( long CM::*, M&& ) );
> > +SA(   __is_invocable( long CM::*, M* ) );
> > +SA(   __is_invocable( long CM::*, CM ) );
> > +SA(   __is_invocable( long CM::*, CM& ) );
> > +SA(   __is_invocable( long CM::*, CM* ) );
> > +SA( ! __is_invocable( long CM::*, long ) );
> > +
> > +SA( ! __is_invocable( short M::* ) );
> > +SA(   __is_invocable( short M::*, M ) );
> > +SA(   __is_invocable( short M::*, M& ) );
> > +SA(   __is_invocable( short M::*, M&& ) );
> > +SA(   __is_invocable( short M::*, M* ) );
> > +SA(   __is_invocable( short M::*, CM ) );
> > +SA(   __is_invocable( short M::*, CM& ) );
> > +SA(   __is_invocable( short M::*, CM* ) );
> > +SA( ! __is_invocable( short M::*, short ) );
> > +
> > +SA( ! __is_invocable( short CM::* ) );
> > +SA(   __is_invocable( short CM::*, M ) );
> > +SA(   __is_invocable( short CM::*, M& ) );
> > +SA(   __is_invocable( short CM::*, M&& ) );
> > +SA(   __is_invocable( short CM::*, M* ) );
> > +SA(   __is_invocable( short CM::*, CM ) );
> > +SA(   __is_invocable( short CM::*, CM& ) );
> > +SA(   __is_invocable( short CM::*, CM* ) );
> > +SA( ! __is_invocable( short CM::*, short ) );
> > +
> > +struct N { M operator*(); };
> > +SA(   __is_invocable( int M::*, N ) );
> > +SA( ! __is_invocable( int M::*, N* ) );
> > +
> > +struct O { M& operator*(); };
> > +SA(   __is_invocable( int M::*, O ) );
> > +SA( ! __is_invocable( int M::*, O* ) );
> > +
> > +struct P { M&& operator*(); };
> > +SA(   __is_invocable( int M::*, P ) );
> > +SA( ! __is_invocable( int M::*, P* ) );
> > +
> > +struct Q { M* operator*(); };
> > +SA( ! __is_invocable( int M::*, Q ) );
> > +SA( ! __is_invocable( int M::*, Q* ) );
> > +
> > +struct R { void operator()(int = 0); };
> > +
> > +SA(   __is_invocable( R ) );
> > +SA(   __is_invocable( R, int ) );
> > +SA( ! __is_invocable( R, int, int ) );
> > +
> > +struct S { void operator()(int, ...); };
> > +
> > +SA( ! __is_invocable( S ) );
> > +SA(   __is_invocable( S, int ) );
> > +SA(   __is_invocable( S, int, int ) );
> > +SA(   __is_invocable( S, int, int, int ) );
> > +
> > +void fn1() {}
> > +
> > +SA(   __is_invocable( decltype(fn1) ) );
> > +
> > +void fn2(int arr[10]);
> > +
> > +SA(   __is_invocable( decltype(fn2), int[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int[] ) );
> > +
> > +auto lambda = []() {};
> > +
> > +SA(   __is_invocable( decltype(lambda) ) );
> > +
> > +template <typename Func, typename... Args>
> > +struct can_invoke {
> > +    static constexpr bool value = __is_invocable( Func, Args... );
> > +};
> > +
> > +SA(   can_invoke<decltype(lambda)>::value );
> > +
> > +struct T {
> > +  void func() const {}
> > +  int data;
> > +};
> > +
> > +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> > +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> > +
> > +struct U { };
> > +struct V : U { U& operator*() = delete; };
> > +SA(   __is_invocable( int U::*, V ) );
> > +
> > +struct W : private U { U& operator*(); };
> > +SA( ! __is_invocable( int U::*, W ) );
> > +
> > +struct X { int m; };
> > +struct Y { X& operator*(); };
> > +struct Z : Y { };
> > +SA(   __is_invocable(int X::*, Z) );
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > new file mode 100644
> > index 00000000000..a68aefd3e13
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > @@ -0,0 +1,139 @@
> > +// { dg-do compile { target c++11 } }
> > +// __is_invocable should handle std::reference_wrapper correctly.
> > +
> > +#include <functional>
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +using std::reference_wrapper;
> > +
> > +using func_type_v0 = void(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> > +
> > +using func_type_i0 = int(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> > +
> > +using func_type_l0 = int&(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> > +
> > +using func_type_ii = int(*)(int);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> > +
> > +using func_type_il = int(*)(int&);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> > +
> > +using func_type_ir = int(*)(int&&);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> > +
> > +struct A { };
> > +
> > +using mem_type_i = int A::*;
> > +
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> > +
> > +using memfun_type_i = int (A::*)();
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> > +
> > +using memfun_type_ic = int (A::*)() const;
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> > +
> > +using memfun_type_iic = int& (A::*)(int&) const;
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> > +
> > +struct B {
> > +  int& operator()();
> > +  long& operator()() const;
> > +  bool& operator()(int);
> > +private:
> > +  void operator()(int, int);
> > +};
> > +using CB = const B;
> > +
> > +SA(   __is_invocable( reference_wrapper<B> ) );
> > +SA(   __is_invocable( reference_wrapper<B>& ) );
> > +SA(   __is_invocable( reference_wrapper<B>&& ) );
> > +SA(   __is_invocable( reference_wrapper<CB> ) );
> > +SA(   __is_invocable( reference_wrapper<CB>& ) );
> > +SA(   __is_invocable( reference_wrapper<B>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> > +
> > +struct C : B { int& operator()() = delete; };
> > +using CC = const C;
> > +
> > +SA( ! __is_invocable( reference_wrapper<C> ) );
> > +SA( ! __is_invocable( reference_wrapper<C>& ) );
> > +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> > +SA( ! __is_invocable( reference_wrapper<CC> ) );
> > +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> > +
> > +struct D { B operator*(); };
> > +using CD = const D;
> > +
> > +SA( ! __is_invocable( reference_wrapper<D> ) );
> > +SA( ! __is_invocable( reference_wrapper<D>& ) );
> > +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> > +SA( ! __is_invocable( reference_wrapper<D>* ) );
> > +SA( ! __is_invocable( reference_wrapper<D*> ) );
> > +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> > +
> > +std::function<void()> fn = []() {};
> > +auto refwrap = std::ref(fn);
> > +
> > +SA(   __is_invocable( decltype(fn) ) );
> > +SA(   __is_invocable( decltype(refwrap) ) );
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > new file mode 100644
> > index 00000000000..8699b0a53ca
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > @@ -0,0 +1,51 @@
> > +// { dg-do compile { target c++11 } }
> > +// __is_invocable should handle incomplete class correctly.
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +struct Incomplete;
> > +
> > +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > +
> > +template <typename T>
> > +struct Holder { T t; };
> > +
> > +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> > +
> > +// Define Incomplete, which is now not incomplete.
> > +struct Incomplete { void operator()(); };
> > +
> > +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > new file mode 100644
> > index 00000000000..d1efccf08f8
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > @@ -0,0 +1,33 @@
> > +// { dg-do compile { target c++11 } }
> > +// Failed access check should be a substitution failure, not an error.
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +template<bool B>
> > +struct bool_constant { static constexpr bool value = B; };
> > +
> > +template<typename _Fn, typename... _ArgTypes>
> > +struct is_invocable
> > +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> > +{ };
> > +
> > +#if __cpp_variable_templates
> > +template<typename _Fn, typename... _ArgTypes>
> > +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> > +#endif
> > +
> > +class Private
> > +{
> > +  void operator()() const
> > +  {
> > +    SA( ! is_invocable<Private>::value );
> > +#if __cpp_variable_templates
> > +    SA( ! is_invocable_v<Private> );
> > +#endif
> > +  }
> > +};
> > +
> > +SA( ! is_invocable<Private>::value );
> > +#if __cpp_variable_templates
> > +SA( ! is_invocable_v<Private> );
> > +#endif
> > --
> > 2.44.0
> >
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 23/26] c++: Implement __is_invocable built-in trait
  2024-03-15  1:53                     ` Ken Matsui
@ 2024-03-15  6:34                       ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-03-15  6:34 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Thu, Mar 14, 2024 at 6:53 PM Ken Matsui <kmatsui@cs.washington.edu> wrote:
>
> On Fri, Mar 8, 2024 at 9:17 AM Patrick Palka <ppalka@redhat.com> wrote:
> >
> > On Wed, 28 Feb 2024, Ken Matsui wrote:
> >
> > > This patch implements built-in trait for std::is_invocable.
> > >
> > > gcc/cp/ChangeLog:
> > >
> > >       * cp-trait.def: Define __is_invocable.
> > >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> > >       * semantics.cc (trait_expr_value): Likewise.
> > >       (finish_trait_expr): Likewise.
> > >       * cp-tree.h (build_invoke): New function.
> > >       * method.cc (build_invoke): New function.
> > >
> > > gcc/testsuite/ChangeLog:
> > >
> > >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> > >       * g++.dg/ext/is_invocable1.C: New test.
> > >       * g++.dg/ext/is_invocable2.C: New test.
> > >       * g++.dg/ext/is_invocable3.C: New test.
> > >       * g++.dg/ext/is_invocable4.C: New test.
> >
> > Thanks, this looks great!  This generic build_invoke function could be
> > used for invoke_result etc as well, and it could also cache the built-up
> > call across __is_invocable and __is_nothrow_invocable checks on the same
> > arguments (which is a common pattern in the standard library).  LGTM
> >
> > >
> > > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > > ---
> > >  gcc/cp/constraint.cc                     |   6 +
> > >  gcc/cp/cp-trait.def                      |   1 +
> > >  gcc/cp/cp-tree.h                         |   2 +
> > >  gcc/cp/method.cc                         | 132 +++++++++
> > >  gcc/cp/semantics.cc                      |   4 +
> > >  gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> > >  gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> > >  gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> > >  gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> > >  gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> > >  10 files changed, 720 insertions(+)
> > >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> > >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> > >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> > >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> > >
> > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > > index 23ea66d9c12..c87b126fdb1 100644
> > > --- a/gcc/cp/constraint.cc
> > > +++ b/gcc/cp/constraint.cc
> > > @@ -3791,6 +3791,12 @@ diagnose_trait_expr (tree expr, tree args)
> > >      case CPTK_IS_FUNCTION:
> > >        inform (loc, "  %qT is not a function", t1);
> > >        break;
> > > +    case CPTK_IS_INVOCABLE:
> > > +      if (!t2)
> > > +    inform (loc, "  %qT is not invocable", t1);
> > > +      else
> > > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > > +      break;
> > >      case CPTK_IS_LAYOUT_COMPATIBLE:
> > >        inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> > >        break;
> > > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > > index 85056c8140b..6cb2b55f4ea 100644
> > > --- a/gcc/cp/cp-trait.def
> > > +++ b/gcc/cp/cp-trait.def
> > > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> > >  DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> > >  DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> > >  DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> > >  DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> > >  DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> > >  DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > > index 334c11396c2..261d3a71faa 100644
> > > --- a/gcc/cp/cp-tree.h
> > > +++ b/gcc/cp/cp-tree.h
> > > @@ -7334,6 +7334,8 @@ extern tree get_copy_assign                     (tree);
> > >  extern tree get_default_ctor                 (tree);
> > >  extern tree get_dtor                         (tree, tsubst_flags_t);
> > >  extern tree build_stub_object                        (tree);
> > > +extern tree build_invoke                     (tree, const_tree,
> > > +                                              tsubst_flags_t);
> > >  extern tree strip_inheriting_ctors           (tree);
> > >  extern tree inherited_ctor_binfo             (tree);
> > >  extern bool base_ctor_omit_inherited_parms   (tree);
> > > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > > index 98c10e6a8b5..953f1bed6fc 100644
> > > --- a/gcc/cp/method.cc
> > > +++ b/gcc/cp/method.cc
> > > @@ -1928,6 +1928,138 @@ build_trait_object (tree type)
> > >    return build_stub_object (type);
> > >  }
> > >
> > > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > > +   given is not invocable, returns error_mark_node.  */
> > > +
> > > +tree
> > > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > > +{
> > > +  if (fn_type == error_mark_node || arg_types == error_mark_node)
> > > +    return error_mark_node;
> > > +
> > > +  gcc_assert (TYPE_P (fn_type));
> > > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > > +
> > > +  /* Access check is required to determine if the given is invocable.  */
> > > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > > +
> > > +  /* INVOKE is an unevaluated context.  */
> > > +  cp_unevaluated cp_uneval_guard;
> > > +
> > > +  bool is_ptrdatamem;
> > > +  bool is_ptrmemfunc;
> > > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > > +    {
> > > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > > +
> > > +      /* Dereference fn_type if it is a pointer to member.  */
> > > +      if (is_ptrdatamem || is_ptrmemfunc)
> > > +     fn_type = deref_fn_type;
> > > +    }
> > > +  else
> > > +    {
> > > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > > +    }
> > > +
> > > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > > +    /* Only a pointer to data member with one argument is invocable.  */
> >
> > Just one note, if/when build_invoke gets a caller that passes tf_error
> > (for e.g. elaborating why is_invocable is false, or why invoke_result
> > is ill-formed) these error_mark_node code paths will need to issue
> > diagnostics b.
>
> Which function should we use for diagnostics - error, error_at, or
> something else?  If we opt for error_at, which location information do
> we want to use?
>

Never mind, I can follow your finish_type_pack_element implementation :)

> >
> > > +    return error_mark_node;
> > > +
> > > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > > +    /* A pointer to member function with no arguments is not invocable.  */
> > > +    return error_mark_node;
> > > +
> > > +  /* Construct an expression of a pointer to member.  */
> > > +  tree ptrmem_expr;
> > > +  if (is_ptrdatamem || is_ptrmemfunc)
> > > +    {
> > > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > > +
> > > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > > +      if (TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > > +        {
> > > +       if (!CLASS_TYPE_P (TREE_TYPE (datum_type)))
> > > +         return error_mark_node;
> > > +        }
> > > +      else if (!CLASS_TYPE_P (datum_type))
> > > +     return error_mark_node;
> > > +
> > > +      bool is_refwrap = false;
> > > +      if (CLASS_TYPE_P (datum_type))
> > > +     {
> > > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > > +       if (decl_in_std_namespace_p (datum_decl))
> > > +         {
> > > +           const_tree name = DECL_NAME (datum_decl);
> > > +           if (name && (id_equal (name, "reference_wrapper")))
> > > +             {
> > > +               /* Retrieve T from std::reference_wrapper<T>,
> > > +                  i.e., decltype(datum.get()).  */
> > > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > > +               is_refwrap = true;
> > > +             }
> > > +         }
> > > +     }
> > > +
> > > +      tree datum_expr = build_trait_object (datum_type);
> > > +      tree fn_expr = build_trait_object (fn_type);
> > > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > > +
> > > +      if (error_operand_p (ptrmem_expr) && !is_refwrap)
> > > +     {
> > > +       tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > > +       const bool ptrmem_is_base_of_datum =
> > > +         (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > > +          && NON_UNION_CLASS_TYPE_P (datum_type)
> > > +          && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > > +                                                         datum_type)
> > > +              || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > > +
> > > +       if (!ptrmem_is_base_of_datum)
> > > +         {
> > > +           /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > > +           datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > > +                                              RO_UNARY_STAR, NULL_TREE,
> > > +                                              complain);
> > > +           /* Rebuild ptrmem_expr.  */
> > > +           ptrmem_expr = build_m_component_ref (datum_expr, fn_expr,
> > > +                                                complain);
> > > +         }
> > > +     }
> > > +      /* 1.1 & 1.4: Otherwise.  */
> > > +
> > > +      if (error_operand_p (ptrmem_expr))
> > > +     return error_mark_node;
> > > +
> > > +      if (is_ptrdatamem)
> > > +     return ptrmem_expr;
> > > +    }
> > > +
> > > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > > +     function, the first argument, which is the object, is not arguments to
> > > +     the function.  */
> > > +  releasing_vec args;
> > > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > > +    {
> > > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > > +      tree arg = build_trait_object (arg_type);
> > > +      vec_safe_push (args, arg);
> > > +    }
> > > +
> > > +  tree invoke_expr;
> > > +  if (is_ptrmemfunc)
> > > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > > +                                                complain);
> > > +  else  /* 1.7.  */
> > > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > > +                                 false, complain);
> > > +  return invoke_expr;
> > > +}
> > > +
> > >  /* Determine which function will be called when looking up NAME in TYPE,
> > >     called with a single ARGTYPE argument, or no argument if ARGTYPE is
> > >     null.  FLAGS and COMPLAIN are as for build_new_method_call.
> > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > > index 7242db75248..149c0631d62 100644
> > > --- a/gcc/cp/semantics.cc
> > > +++ b/gcc/cp/semantics.cc
> > > @@ -12467,6 +12467,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
> > >      case CPTK_IS_FUNCTION:
> > >        return type_code1 == FUNCTION_TYPE;
> > >
> > > +    case CPTK_IS_INVOCABLE:
> > > +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> > > +
> > >      case CPTK_IS_LAYOUT_COMPATIBLE:
> > >        return layout_compatible_type_p (type1, type2);
> > >
> > > @@ -12682,6 +12685,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> > >        break;
> > >
> > >      case CPTK_IS_CONVERTIBLE:
> > > +    case CPTK_IS_INVOCABLE:
> > >      case CPTK_IS_NOTHROW_ASSIGNABLE:
> > >      case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
> > >      case CPTK_IS_NOTHROW_CONVERTIBLE:
> > > diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > index 7f7b27f7aa7..d2a7ebdf25c 100644
> > > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > @@ -104,6 +104,9 @@
> > >  #if !__has_builtin (__is_function)
> > >  # error "__has_builtin (__is_function) failed"
> > >  #endif
> > > +#if !__has_builtin (__is_invocable)
> > > +# error "__has_builtin (__is_invocable) failed"
> > > +#endif
> > >  #if !__has_builtin (__is_layout_compatible)
> > >  # error "__has_builtin (__is_layout_compatible) failed"
> > >  #endif
> > > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > > new file mode 100644
> > > index 00000000000..d21ae1d1958
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > > @@ -0,0 +1,349 @@
> > > +// { dg-do compile { target c++11 } }
> > > +
> > > +#define SA(X) static_assert((X),#X)
> > > +
> > > +using func_type_v0 = void(*)();
> > > +
> > > +SA(   __is_invocable( func_type_v0 ) );
> > > +SA( ! __is_invocable( func_type_v0, int ) );
> > > +
> > > +using func_type_i0 = int(*)();
> > > +
> > > +SA(   __is_invocable( func_type_i0 ) );
> > > +SA( ! __is_invocable( func_type_i0, int ) );
> > > +
> > > +using func_type_l0 = int&(*)();
> > > +
> > > +SA(   __is_invocable( func_type_l0 ) );
> > > +SA( ! __is_invocable( func_type_l0(int) ) );
> > > +
> > > +using func_type_ii = int(*)(int);
> > > +
> > > +SA( ! __is_invocable( func_type_ii ) );
> > > +SA(   __is_invocable( func_type_ii, int ) );
> > > +
> > > +using func_type_il = int(*)(int&);
> > > +
> > > +SA( ! __is_invocable( func_type_il ) );
> > > +SA( ! __is_invocable( func_type_il, int ) );
> > > +SA(   __is_invocable( func_type_il, int& ) );
> > > +
> > > +using func_type_ir = int(*)(int&&);
> > > +
> > > +SA( ! __is_invocable( func_type_ir ) );
> > > +SA( ! __is_invocable( func_type_ir, int& ) );
> > > +SA(   __is_invocable( func_type_ir, int ) );
> > > +SA(   __is_invocable( func_type_ir, int&& ) );
> > > +
> > > +struct A { };
> > > +
> > > +using mem_type_i = int A::*;
> > > +
> > > +SA( ! __is_invocable( mem_type_i ) );
> > > +SA( ! __is_invocable( mem_type_i, int ) );
> > > +SA( ! __is_invocable( mem_type_i, int* ) );
> > > +SA( ! __is_invocable( mem_type_i, int& ) );
> > > +SA( ! __is_invocable( mem_type_i, int&& ) );
> > > +SA(   __is_invocable( mem_type_i, A ) );
> > > +SA(   __is_invocable( mem_type_i, A* ) );
> > > +SA(   __is_invocable( mem_type_i, A& ) );
> > > +SA(   __is_invocable( mem_type_i, A&& ) );
> > > +SA(   __is_invocable( mem_type_i, const A& ) );
> > > +SA( ! __is_invocable( mem_type_i, A&, int ) );
> > > +
> > > +using memfun_type_i = int (A::*)();
> > > +
> > > +SA( ! __is_invocable( memfun_type_i ) );
> > > +SA( ! __is_invocable( memfun_type_i, int ) );
> > > +SA( ! __is_invocable( memfun_type_i, int* ) );
> > > +SA( ! __is_invocable( memfun_type_i, int& ) );
> > > +SA( ! __is_invocable( memfun_type_i, int&& ) );
> > > +SA(   __is_invocable( memfun_type_i, A ) );
> > > +SA(   __is_invocable( memfun_type_i, A* ) );
> > > +SA(   __is_invocable( memfun_type_i, A& ) );
> > > +SA(   __is_invocable( memfun_type_i, A&& ) );
> > > +SA( ! __is_invocable( memfun_type_i, const A& ) );
> > > +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> > > +
> > > +using memfun_type_ic = int (A::*)() const;
> > > +
> > > +SA( ! __is_invocable( memfun_type_ic ) );
> > > +SA( ! __is_invocable( memfun_type_ic, int ) );
> > > +SA( ! __is_invocable( memfun_type_ic, int& ) );
> > > +SA(   __is_invocable( memfun_type_ic, A& ) );
> > > +SA(   __is_invocable( memfun_type_ic, A* ) );
> > > +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> > > +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> > > +SA(   __is_invocable( memfun_type_ic, const A& ) );
> > > +SA(   __is_invocable( memfun_type_ic, const A* ) );
> > > +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> > > +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> > > +
> > > +using memfun_type_iic = int& (A::*)(int&) const;
> > > +
> > > +SA( ! __is_invocable( memfun_type_iic ) );
> > > +SA( ! __is_invocable( memfun_type_iic, int ) );
> > > +SA( ! __is_invocable( memfun_type_iic, int& ) );
> > > +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> > > +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> > > +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> > > +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> > > +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> > > +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> > > +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> > > +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> > > +
> > > +struct B {
> > > +  int& operator()();
> > > +  long& operator()() const;
> > > +  bool& operator()(int);
> > > +private:
> > > +  void operator()(int, int);
> > > +};
> > > +using CB = const B;
> > > +
> > > +SA(   __is_invocable( B ) );
> > > +SA(   __is_invocable( B& ) );
> > > +SA(   __is_invocable( B&& ) );
> > > +SA( ! __is_invocable( B* ) );
> > > +SA(   __is_invocable( CB ) );
> > > +SA(   __is_invocable( CB& ) );
> > > +SA( ! __is_invocable( CB* ) );
> > > +
> > > +SA(   __is_invocable( B, int ) );
> > > +SA(   __is_invocable( B&, int ) );
> > > +SA(   __is_invocable( B&&, int ) );
> > > +SA( ! __is_invocable( B*, int ) );
> > > +SA( ! __is_invocable( CB, int ) );
> > > +SA( ! __is_invocable( CB&, int ) );
> > > +SA( ! __is_invocable( CB*, int ) );
> > > +
> > > +SA( ! __is_invocable( B, int, int ) );
> > > +SA( ! __is_invocable( B&, int, int ) );
> > > +SA( ! __is_invocable( B&&, int, int ) );
> > > +SA( ! __is_invocable( B*, int, int ) );
> > > +SA( ! __is_invocable( CB, int, int ) );
> > > +SA( ! __is_invocable( CB&, int, int ) );
> > > +SA( ! __is_invocable( CB*, int, int ) );
> > > +
> > > +struct C : B { int& operator()() = delete; };
> > > +using CC = const C;
> > > +
> > > +SA( ! __is_invocable( C ) );
> > > +SA( ! __is_invocable( C& ) );
> > > +SA( ! __is_invocable( C&& ) );
> > > +SA( ! __is_invocable( C* ) );
> > > +SA( ! __is_invocable( CC ) );
> > > +SA( ! __is_invocable( CC& ) );
> > > +SA( ! __is_invocable( CC* ) );
> > > +
> > > +struct D { B operator*(); };
> > > +using CD = const D;
> > > +
> > > +SA( ! __is_invocable( D ) );
> > > +
> > > +struct E { void v(); };
> > > +using CE = const E;
> > > +
> > > +SA( ! __is_invocable( E ) );
> > > +SA( ! __is_invocable( void (E::*)() ) );
> > > +SA(   __is_invocable( void (E::*)(), E ) );
> > > +SA(   __is_invocable( void (E::*)(), E* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CE ) );
> > > +
> > > +struct F : E {};
> > > +using CF = const F;
> > > +
> > > +SA( ! __is_invocable( F ) );
> > > +SA(   __is_invocable( void (E::*)(), F ) );
> > > +SA(   __is_invocable( void (E::*)(), F* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CF ) );
> > > +
> > > +struct G { E operator*(); };
> > > +using CG = const G;
> > > +
> > > +SA( ! __is_invocable( G ) );
> > > +SA(   __is_invocable( void (E::*)(), G ) );
> > > +SA( ! __is_invocable( void (E::*)(), G* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CG ) );
> > > +
> > > +struct H { E& operator*(); };
> > > +using CH = const H;
> > > +
> > > +SA( ! __is_invocable( H ) );
> > > +SA(   __is_invocable( void (E::*)(), H ) );
> > > +SA( ! __is_invocable( void (E::*)(), H* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CH ) );
> > > +
> > > +struct I { E&& operator*(); };
> > > +using CI = const I;
> > > +
> > > +SA( ! __is_invocable( I ) );
> > > +SA(   __is_invocable( void (E::*)(), I ) );
> > > +SA( ! __is_invocable( void (E::*)(), I* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CI ) );
> > > +
> > > +struct K { E* operator*(); };
> > > +using CK = const K;
> > > +
> > > +SA( ! __is_invocable( K ) );
> > > +SA( ! __is_invocable( void (E::*)(), K ) );
> > > +SA( ! __is_invocable( void (E::*)(), K* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CK ) );
> > > +
> > > +struct L { CE operator*(); };
> > > +using CL = const L;
> > > +
> > > +SA( ! __is_invocable( L ) );
> > > +SA( ! __is_invocable( void (E::*)(), L ) );
> > > +SA( ! __is_invocable( void (E::*)(), L* ) );
> > > +SA( ! __is_invocable( void (E::*)(), CL ) );
> > > +
> > > +struct M {
> > > +  int i;
> > > +private:
> > > +  long l;
> > > +};
> > > +using CM = const M;
> > > +
> > > +SA( ! __is_invocable( M ) );
> > > +SA( ! __is_invocable( M& ) );
> > > +SA( ! __is_invocable( M&& ) );
> > > +SA( ! __is_invocable( M* ) );
> > > +SA( ! __is_invocable( CM ) );
> > > +SA( ! __is_invocable( CM& ) );
> > > +SA( ! __is_invocable( CM* ) );
> > > +
> > > +SA( ! __is_invocable( int M::* ) );
> > > +SA(   __is_invocable( int M::*, M ) );
> > > +SA(   __is_invocable( int M::*, M& ) );
> > > +SA(   __is_invocable( int M::*, M&& ) );
> > > +SA(   __is_invocable( int M::*, M* ) );
> > > +SA(   __is_invocable( int M::*, CM ) );
> > > +SA(   __is_invocable( int M::*, CM& ) );
> > > +SA(   __is_invocable( int M::*, CM* ) );
> > > +SA( ! __is_invocable( int M::*, int ) );
> > > +
> > > +SA( ! __is_invocable( int CM::* ) );
> > > +SA(   __is_invocable( int CM::*, M ) );
> > > +SA(   __is_invocable( int CM::*, M& ) );
> > > +SA(   __is_invocable( int CM::*, M&& ) );
> > > +SA(   __is_invocable( int CM::*, M* ) );
> > > +SA(   __is_invocable( int CM::*, CM ) );
> > > +SA(   __is_invocable( int CM::*, CM& ) );
> > > +SA(   __is_invocable( int CM::*, CM* ) );
> > > +SA( ! __is_invocable( int CM::*, int ) );
> > > +
> > > +SA( ! __is_invocable( long M::* ) );
> > > +SA(   __is_invocable( long M::*, M ) );
> > > +SA(   __is_invocable( long M::*, M& ) );
> > > +SA(   __is_invocable( long M::*, M&& ) );
> > > +SA(   __is_invocable( long M::*, M* ) );
> > > +SA(   __is_invocable( long M::*, CM ) );
> > > +SA(   __is_invocable( long M::*, CM& ) );
> > > +SA(   __is_invocable( long M::*, CM* ) );
> > > +SA( ! __is_invocable( long M::*, long ) );
> > > +
> > > +SA( ! __is_invocable( long CM::* ) );
> > > +SA(   __is_invocable( long CM::*, M ) );
> > > +SA(   __is_invocable( long CM::*, M& ) );
> > > +SA(   __is_invocable( long CM::*, M&& ) );
> > > +SA(   __is_invocable( long CM::*, M* ) );
> > > +SA(   __is_invocable( long CM::*, CM ) );
> > > +SA(   __is_invocable( long CM::*, CM& ) );
> > > +SA(   __is_invocable( long CM::*, CM* ) );
> > > +SA( ! __is_invocable( long CM::*, long ) );
> > > +
> > > +SA( ! __is_invocable( short M::* ) );
> > > +SA(   __is_invocable( short M::*, M ) );
> > > +SA(   __is_invocable( short M::*, M& ) );
> > > +SA(   __is_invocable( short M::*, M&& ) );
> > > +SA(   __is_invocable( short M::*, M* ) );
> > > +SA(   __is_invocable( short M::*, CM ) );
> > > +SA(   __is_invocable( short M::*, CM& ) );
> > > +SA(   __is_invocable( short M::*, CM* ) );
> > > +SA( ! __is_invocable( short M::*, short ) );
> > > +
> > > +SA( ! __is_invocable( short CM::* ) );
> > > +SA(   __is_invocable( short CM::*, M ) );
> > > +SA(   __is_invocable( short CM::*, M& ) );
> > > +SA(   __is_invocable( short CM::*, M&& ) );
> > > +SA(   __is_invocable( short CM::*, M* ) );
> > > +SA(   __is_invocable( short CM::*, CM ) );
> > > +SA(   __is_invocable( short CM::*, CM& ) );
> > > +SA(   __is_invocable( short CM::*, CM* ) );
> > > +SA( ! __is_invocable( short CM::*, short ) );
> > > +
> > > +struct N { M operator*(); };
> > > +SA(   __is_invocable( int M::*, N ) );
> > > +SA( ! __is_invocable( int M::*, N* ) );
> > > +
> > > +struct O { M& operator*(); };
> > > +SA(   __is_invocable( int M::*, O ) );
> > > +SA( ! __is_invocable( int M::*, O* ) );
> > > +
> > > +struct P { M&& operator*(); };
> > > +SA(   __is_invocable( int M::*, P ) );
> > > +SA( ! __is_invocable( int M::*, P* ) );
> > > +
> > > +struct Q { M* operator*(); };
> > > +SA( ! __is_invocable( int M::*, Q ) );
> > > +SA( ! __is_invocable( int M::*, Q* ) );
> > > +
> > > +struct R { void operator()(int = 0); };
> > > +
> > > +SA(   __is_invocable( R ) );
> > > +SA(   __is_invocable( R, int ) );
> > > +SA( ! __is_invocable( R, int, int ) );
> > > +
> > > +struct S { void operator()(int, ...); };
> > > +
> > > +SA( ! __is_invocable( S ) );
> > > +SA(   __is_invocable( S, int ) );
> > > +SA(   __is_invocable( S, int, int ) );
> > > +SA(   __is_invocable( S, int, int, int ) );
> > > +
> > > +void fn1() {}
> > > +
> > > +SA(   __is_invocable( decltype(fn1) ) );
> > > +
> > > +void fn2(int arr[10]);
> > > +
> > > +SA(   __is_invocable( decltype(fn2), int[10] ) );
> > > +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> > > +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> > > +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> > > +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> > > +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> > > +SA(   __is_invocable( decltype(fn2), int[] ) );
> > > +
> > > +auto lambda = []() {};
> > > +
> > > +SA(   __is_invocable( decltype(lambda) ) );
> > > +
> > > +template <typename Func, typename... Args>
> > > +struct can_invoke {
> > > +    static constexpr bool value = __is_invocable( Func, Args... );
> > > +};
> > > +
> > > +SA(   can_invoke<decltype(lambda)>::value );
> > > +
> > > +struct T {
> > > +  void func() const {}
> > > +  int data;
> > > +};
> > > +
> > > +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> > > +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> > > +
> > > +struct U { };
> > > +struct V : U { U& operator*() = delete; };
> > > +SA(   __is_invocable( int U::*, V ) );
> > > +
> > > +struct W : private U { U& operator*(); };
> > > +SA( ! __is_invocable( int U::*, W ) );
> > > +
> > > +struct X { int m; };
> > > +struct Y { X& operator*(); };
> > > +struct Z : Y { };
> > > +SA(   __is_invocable(int X::*, Z) );
> > > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > > new file mode 100644
> > > index 00000000000..a68aefd3e13
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > > @@ -0,0 +1,139 @@
> > > +// { dg-do compile { target c++11 } }
> > > +// __is_invocable should handle std::reference_wrapper correctly.
> > > +
> > > +#include <functional>
> > > +
> > > +#define SA(X) static_assert((X),#X)
> > > +
> > > +using std::reference_wrapper;
> > > +
> > > +using func_type_v0 = void(*)();
> > > +
> > > +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> > > +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> > > +
> > > +using func_type_i0 = int(*)();
> > > +
> > > +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> > > +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> > > +
> > > +using func_type_l0 = int&(*)();
> > > +
> > > +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> > > +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> > > +
> > > +using func_type_ii = int(*)(int);
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> > > +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> > > +
> > > +using func_type_il = int(*)(int&);
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> > > +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> > > +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> > > +
> > > +using func_type_ir = int(*)(int&&);
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> > > +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> > > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> > > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> > > +
> > > +struct A { };
> > > +
> > > +using mem_type_i = int A::*;
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> > > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> > > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> > > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> > > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> > > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> > > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> > > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> > > +
> > > +using memfun_type_i = int (A::*)();
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> > > +
> > > +using memfun_type_ic = int (A::*)() const;
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> > > +
> > > +using memfun_type_iic = int& (A::*)(int&) const;
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> > > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> > > +
> > > +struct B {
> > > +  int& operator()();
> > > +  long& operator()() const;
> > > +  bool& operator()(int);
> > > +private:
> > > +  void operator()(int, int);
> > > +};
> > > +using CB = const B;
> > > +
> > > +SA(   __is_invocable( reference_wrapper<B> ) );
> > > +SA(   __is_invocable( reference_wrapper<B>& ) );
> > > +SA(   __is_invocable( reference_wrapper<B>&& ) );
> > > +SA(   __is_invocable( reference_wrapper<CB> ) );
> > > +SA(   __is_invocable( reference_wrapper<CB>& ) );
> > > +SA(   __is_invocable( reference_wrapper<B>, int ) );
> > > +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> > > +
> > > +struct C : B { int& operator()() = delete; };
> > > +using CC = const C;
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<C> ) );
> > > +SA( ! __is_invocable( reference_wrapper<C>& ) );
> > > +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> > > +SA( ! __is_invocable( reference_wrapper<CC> ) );
> > > +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> > > +
> > > +struct D { B operator*(); };
> > > +using CD = const D;
> > > +
> > > +SA( ! __is_invocable( reference_wrapper<D> ) );
> > > +SA( ! __is_invocable( reference_wrapper<D>& ) );
> > > +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> > > +SA( ! __is_invocable( reference_wrapper<D>* ) );
> > > +SA( ! __is_invocable( reference_wrapper<D*> ) );
> > > +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> > > +
> > > +std::function<void()> fn = []() {};
> > > +auto refwrap = std::ref(fn);
> > > +
> > > +SA(   __is_invocable( decltype(fn) ) );
> > > +SA(   __is_invocable( decltype(refwrap) ) );
> > > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > > new file mode 100644
> > > index 00000000000..8699b0a53ca
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > > @@ -0,0 +1,51 @@
> > > +// { dg-do compile { target c++11 } }
> > > +// __is_invocable should handle incomplete class correctly.
> > > +
> > > +#define SA(X) static_assert((X),#X)
> > > +
> > > +struct Incomplete;
> > > +
> > > +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> > > +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> > > +
> > > +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> > > +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> > > +
> > > +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> > > +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> > > +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> > > +
> > > +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> > > +
> > > +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> > > +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> > > +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> > > +
> > > +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > > +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > > +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > > +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > > +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +template <typename T>
> > > +struct Holder { T t; };
> > > +
> > > +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> > > +
> > > +// Define Incomplete, which is now not incomplete.
> > > +struct Incomplete { void operator()(); };
> > > +
> > > +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> > > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > > new file mode 100644
> > > index 00000000000..d1efccf08f8
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > > @@ -0,0 +1,33 @@
> > > +// { dg-do compile { target c++11 } }
> > > +// Failed access check should be a substitution failure, not an error.
> > > +
> > > +#define SA(X) static_assert((X),#X)
> > > +
> > > +template<bool B>
> > > +struct bool_constant { static constexpr bool value = B; };
> > > +
> > > +template<typename _Fn, typename... _ArgTypes>
> > > +struct is_invocable
> > > +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> > > +{ };
> > > +
> > > +#if __cpp_variable_templates
> > > +template<typename _Fn, typename... _ArgTypes>
> > > +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> > > +#endif
> > > +
> > > +class Private
> > > +{
> > > +  void operator()() const
> > > +  {
> > > +    SA( ! is_invocable<Private>::value );
> > > +#if __cpp_variable_templates
> > > +    SA( ! is_invocable_v<Private> );
> > > +#endif
> > > +  }
> > > +};
> > > +
> > > +SA( ! is_invocable<Private>::value );
> > > +#if __cpp_variable_templates
> > > +SA( ! is_invocable_v<Private> );
> > > +#endif
> > > --
> > > 2.44.0
> > >
> > >
> >

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 01/26] c++: Implement __is_const built-in trait
  2024-02-28 19:26                 ` [PATCH v14 01/26] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-04-30 20:54                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 20:54 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::is_const.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_const.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
> 	* g++.dg/ext/is_const.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 +++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  4 ++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
>   5 files changed, 31 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 49de3211d4c..f32a1c78d63 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3767,6 +3767,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_CLASS:
>         inform (loc, "  %qT is not a class", t1);
>         break;
> +    case CPTK_IS_CONST:
> +      inform (loc, "  %qT is not a const type", t1);
> +      break;
>       case CPTK_IS_CONSTRUCTIBLE:
>         if (!t2)
>       inform (loc, "  %qT is not default constructible", t1);
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 394f006f20f..36faed9c0b3 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
>   DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
>   DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
>   DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
> +DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
>   DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
>   DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
>   DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 57840176863..0d08900492b 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12446,6 +12446,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_CLASS:
>         return NON_UNION_CLASS_TYPE_P (type1);
>   
> +    case CPTK_IS_CONST:
> +      return CP_TYPE_CONST_P (type1);
> +
>       case CPTK_IS_CONSTRUCTIBLE:
>         return is_xible (INIT_EXPR, type1, type2);
>   
> @@ -12688,6 +12691,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_ARRAY:
>       case CPTK_IS_BOUNDED_ARRAY:
>       case CPTK_IS_CLASS:
> +    case CPTK_IS_CONST:
>       case CPTK_IS_ENUM:
>       case CPTK_IS_FUNCTION:
>       case CPTK_IS_MEMBER_FUNCTION_POINTER:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 02b4b4d745d..e3640faeb96 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -71,6 +71,9 @@
>   #if !__has_builtin (__is_class)
>   # error "__has_builtin (__is_class) failed"
>   #endif
> +#if !__has_builtin (__is_const)
> +# error "__has_builtin (__is_const) failed"
> +#endif
>   #if !__has_builtin (__is_constructible)
>   # error "__has_builtin (__is_constructible) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
> new file mode 100644
> index 00000000000..8a0e8df72a9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_const.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +using cClassType = const ClassType;
> +using vClassType = volatile ClassType;
> +using cvClassType = const volatile ClassType;
> +
> +// Positive tests.
> +SA(__is_const(const int));
> +SA(__is_const(const volatile int));
> +SA(__is_const(cClassType));
> +SA(__is_const(cvClassType));
> +
> +// Negative tests.
> +SA(!__is_const(int));
> +SA(!__is_const(volatile int));
> +SA(!__is_const(ClassType));
> +SA(!__is_const(vClassType));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 03/26] c++: Implement __is_volatile built-in trait
  2024-02-28 19:26                 ` [PATCH v14 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-04-30 20:54                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 20:54 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::is_volatile.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_volatile.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
> 	* g++.dg/ext/is_volatile.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 +++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  4 ++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
>   5 files changed, 31 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index f32a1c78d63..9a7a12629e7 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3861,6 +3861,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_UNION:
>         inform (loc, "  %qT is not a union", t1);
>         break;
> +    case CPTK_IS_VOLATILE:
> +      inform (loc, "  %qT is not a volatile type", t1);
> +      break;
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>         inform (loc, "  %qT is not a reference that binds to a temporary "
>   	      "object of type %qT (direct-initialization)", t1, t2);
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 36faed9c0b3..e9347453829 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
>   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> +DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
>   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
>   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
>   DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 0d08900492b..41c25f43d27 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_UNION:
>         return type_code1 == UNION_TYPE;
>   
> +    case CPTK_IS_VOLATILE:
> +      return CP_TYPE_VOLATILE_P (type1);
> +
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>         return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
>   
> @@ -12702,6 +12705,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_SAME:
>       case CPTK_IS_SCOPED_ENUM:
>       case CPTK_IS_UNION:
> +    case CPTK_IS_VOLATILE:
>         break;
>   
>       case CPTK_IS_LAYOUT_COMPATIBLE:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index e3640faeb96..b2e2f2f694d 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -158,6 +158,9 @@
>   #if !__has_builtin (__is_union)
>   # error "__has_builtin (__is_union) failed"
>   #endif
> +#if !__has_builtin (__is_volatile)
> +# error "__has_builtin (__is_volatile) failed"
> +#endif
>   #if !__has_builtin (__reference_constructs_from_temporary)
>   # error "__has_builtin (__reference_constructs_from_temporary) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
> new file mode 100644
> index 00000000000..80a1cfc880d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +using cClassType = const ClassType;
> +using vClassType = volatile ClassType;
> +using cvClassType = const volatile ClassType;
> +
> +// Positive tests.
> +SA(__is_volatile(volatile int));
> +SA(__is_volatile(const volatile int));
> +SA(__is_volatile(vClassType));
> +SA(__is_volatile(cvClassType));
> +
> +// Negative tests.
> +SA(!__is_volatile(int));
> +SA(!__is_volatile(const int));
> +SA(!__is_volatile(ClassType));
> +SA(!__is_volatile(cClassType));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 05/26] c++: Implement __is_pointer built-in trait
  2024-02-28 19:26                 ` [PATCH v14 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-04-30 20:56                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 20:56 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::is_pointer.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_pointer.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
> 	* g++.dg/ext/is_pointer.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 ++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  4 ++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
>   gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
>   5 files changed, 62 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 9a7a12629e7..244070d93c2 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3828,6 +3828,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_POD:
>         inform (loc, "  %qT is not a POD type", t1);
>         break;
> +    case CPTK_IS_POINTER:
> +      inform (loc, "  %qT is not a pointer", t1);
> +      break;
>       case CPTK_IS_POLYMORPHIC:
>         inform (loc, "  %qT is not a polymorphic type", t1);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index e9347453829..18e2d0f3480 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
>   DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
>   DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
>   DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
> +DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
>   DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
>   DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
>   DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 41c25f43d27..9dcdb06191a 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12502,6 +12502,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_POD:
>         return pod_type_p (type1);
>   
> +    case CPTK_IS_POINTER:
> +      return TYPE_PTR_P (type1);
> +
>       case CPTK_IS_POLYMORPHIC:
>         return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
>   
> @@ -12701,6 +12704,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_MEMBER_OBJECT_POINTER:
>       case CPTK_IS_MEMBER_POINTER:
>       case CPTK_IS_OBJECT:
> +    case CPTK_IS_POINTER:
>       case CPTK_IS_REFERENCE:
>       case CPTK_IS_SAME:
>       case CPTK_IS_SCOPED_ENUM:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index b2e2f2f694d..96b7a89e4f1 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -125,6 +125,9 @@
>   #if !__has_builtin (__is_pod)
>   # error "__has_builtin (__is_pod) failed"
>   #endif
> +#if !__has_builtin (__is_pointer)
> +# error "__has_builtin (__is_pointer) failed"
> +#endif
>   #if !__has_builtin (__is_polymorphic)
>   # error "__has_builtin (__is_polymorphic) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
> new file mode 100644
> index 00000000000..d6e39565950
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
> @@ -0,0 +1,51 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +SA(!__is_pointer(int));
> +SA(__is_pointer(int*));
> +SA(__is_pointer(int**));
> +
> +SA(__is_pointer(const int*));
> +SA(__is_pointer(const int**));
> +SA(__is_pointer(int* const));
> +SA(__is_pointer(int** const));
> +SA(__is_pointer(int* const* const));
> +
> +SA(__is_pointer(volatile int*));
> +SA(__is_pointer(volatile int**));
> +SA(__is_pointer(int* volatile));
> +SA(__is_pointer(int** volatile));
> +SA(__is_pointer(int* volatile* volatile));
> +
> +SA(__is_pointer(const volatile int*));
> +SA(__is_pointer(const volatile int**));
> +SA(__is_pointer(const int* volatile));
> +SA(__is_pointer(volatile int* const));
> +SA(__is_pointer(int* const volatile));
> +SA(__is_pointer(const int** volatile));
> +SA(__is_pointer(volatile int** const));
> +SA(__is_pointer(int** const volatile));
> +SA(__is_pointer(int* const* const volatile));
> +SA(__is_pointer(int* volatile* const volatile));
> +SA(__is_pointer(int* const volatile* const volatile));
> +
> +SA(!__is_pointer(int&));
> +SA(!__is_pointer(const int&));
> +SA(!__is_pointer(volatile int&));
> +SA(!__is_pointer(const volatile int&));
> +
> +SA(!__is_pointer(int&&));
> +SA(!__is_pointer(const int&&));
> +SA(!__is_pointer(volatile int&&));
> +SA(!__is_pointer(const volatile int&&));
> +
> +SA(!__is_pointer(int[3]));
> +SA(!__is_pointer(const int[3]));
> +SA(!__is_pointer(volatile int[3]));
> +SA(!__is_pointer(const volatile int[3]));
> +
> +SA(!__is_pointer(int(int)));
> +SA(__is_pointer(int(*const)(int)));
> +SA(__is_pointer(int(*volatile)(int)));
> +SA(__is_pointer(int(*const volatile)(int)));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 07/26] c++: Implement __is_unbounded_array built-in trait
  2024-02-28 19:26                 ` [PATCH v14 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2024-04-30 20:56                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 20:56 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::is_unbounded_array.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_unbounded_array.
> 	* constraint.cc (diagnose_trait_expr): Handle
> 	CPTK_IS_UNBOUNDED_ARRAY.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of
> 	__is_unbounded_array.
> 	* g++.dg/ext/is_unbounded_array.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                          |  3 ++
>   gcc/cp/cp-trait.def                           |  1 +
>   gcc/cp/semantics.cc                           |  4 ++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
>   gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
>   5 files changed, 48 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 244070d93c2..000df847342 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3861,6 +3861,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_TRIVIALLY_COPYABLE:
>         inform (loc, "  %qT is not trivially copyable", t1);
>         break;
> +    case CPTK_IS_UNBOUNDED_ARRAY:
> +      inform (loc, "  %qT is not an unbounded array", t1);
> +      break;
>       case CPTK_IS_UNION:
>         inform (loc, "  %qT is not a union", t1);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 18e2d0f3480..05514a51c21 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
> +DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
>   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
>   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
>   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 9dcdb06191a..1794e83baa2 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12532,6 +12532,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_TRIVIALLY_COPYABLE:
>         return trivially_copyable_p (type1);
>   
> +    case CPTK_IS_UNBOUNDED_ARRAY:
> +      return array_of_unknown_bound_p (type1);
> +
>       case CPTK_IS_UNION:
>         return type_code1 == UNION_TYPE;
>   
> @@ -12708,6 +12711,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_REFERENCE:
>       case CPTK_IS_SAME:
>       case CPTK_IS_SCOPED_ENUM:
> +    case CPTK_IS_UNBOUNDED_ARRAY:
>       case CPTK_IS_UNION:
>       case CPTK_IS_VOLATILE:
>         break;
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 96b7a89e4f1..b1430e9bd8b 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -158,6 +158,9 @@
>   #if !__has_builtin (__is_trivially_copyable)
>   # error "__has_builtin (__is_trivially_copyable) failed"
>   #endif
> +#if !__has_builtin (__is_unbounded_array)
> +# error "__has_builtin (__is_unbounded_array) failed"
> +#endif
>   #if !__has_builtin (__is_union)
>   # error "__has_builtin (__is_union) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
> new file mode 100644
> index 00000000000..283a74e1a0a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
> @@ -0,0 +1,37 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
> +  SA(TRAIT(TYPE) == EXPECT);					\
> +  SA(TRAIT(const TYPE) == EXPECT);				\
> +  SA(TRAIT(volatile TYPE) == EXPECT);			\
> +  SA(TRAIT(const volatile TYPE) == EXPECT)
> +
> +class ClassType { };
> +class IncompleteClass;
> +union IncompleteUnion;
> +
> +SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
> +SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
> +SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
> +
> +// Sanity check.
> +SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
> +SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
> +SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 09/26] c++: Implement __add_pointer built-in trait
  2024-02-28 19:26                 ` [PATCH v14 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
@ 2024-04-30 20:58                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 20:58 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::add_pointer.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __add_pointer.
> 	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __add_pointer.
> 	* g++.dg/ext/add_pointer.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  9 ++++++
>   gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
>   4 files changed, 52 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C
> 
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 05514a51c21..63f879287ce 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -48,6 +48,7 @@
>   #define DEFTRAIT_TYPE_DEFAULTED
>   #endif
>   
> +DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 1794e83baa2..635441a7a90 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12776,6 +12776,15 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
>   
>     switch (kind)
>       {
> +    case CPTK_ADD_POINTER:
> +      if (FUNC_OR_METHOD_TYPE_P (type1)
> +	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
> +	      || type_memfn_rqual (type1) != REF_QUAL_NONE))
> +	return type1;
> +      if (TYPE_REF_P (type1))
> +	type1 = TREE_TYPE (type1);
> +      return build_pointer_type (type1);
> +
>       case CPTK_REMOVE_CV:
>         return cv_unqualified (type1);
>   
> diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
> new file mode 100644
> index 00000000000..c405cdd0feb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
> @@ -0,0 +1,39 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +
> +SA(__is_same(__add_pointer(int), int*));
> +SA(__is_same(__add_pointer(int*), int**));
> +SA(__is_same(__add_pointer(const int), const int*));
> +SA(__is_same(__add_pointer(int&), int*));
> +SA(__is_same(__add_pointer(ClassType*), ClassType**));
> +SA(__is_same(__add_pointer(ClassType), ClassType*));
> +SA(__is_same(__add_pointer(void), void*));
> +SA(__is_same(__add_pointer(const void), const void*));
> +SA(__is_same(__add_pointer(volatile void), volatile void*));
> +SA(__is_same(__add_pointer(const volatile void), const volatile void*));
> +
> +void f1();
> +using f1_type = decltype(f1);
> +using pf1_type = decltype(&f1);
> +SA(__is_same(__add_pointer(f1_type), pf1_type));
> +
> +void f2() noexcept; // PR libstdc++/78361
> +using f2_type = decltype(f2);
> +using pf2_type = decltype(&f2);
> +SA(__is_same(__add_pointer(f2_type), pf2_type));
> +
> +using fn_type = void();
> +using pfn_type = void(*)();
> +SA(__is_same(__add_pointer(fn_type), pfn_type));
> +
> +SA(__is_same(__add_pointer(void() &), void() &));
> +SA(__is_same(__add_pointer(void() & noexcept), void() & noexcept));
> +SA(__is_same(__add_pointer(void() const), void() const));
> +SA(__is_same(__add_pointer(void(...) &), void(...) &));
> +SA(__is_same(__add_pointer(void(...) & noexcept), void(...) & noexcept));
> +SA(__is_same(__add_pointer(void(...) const), void(...) const));
> +
> +SA(__is_same(__add_pointer(void() __restrict), void() __restrict));
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index b1430e9bd8b..9d861398bae 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -2,6 +2,9 @@
>   // { dg-do compile }
>   // Verify that __has_builtin gives the correct answer for C++ built-ins.
>   
> +#if !__has_builtin (__add_pointer)
> +# error "__has_builtin (__add_pointer) failed"
> +#endif
>   #if !__has_builtin (__builtin_addressof)
>   # error "__has_builtin (__builtin_addressof) failed"
>   #endif


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 11/26] c++: Implement __remove_extent built-in trait
  2024-02-28 19:26                 ` [PATCH v14 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
@ 2024-04-30 20:59                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 20:59 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::remove_extent.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __remove_extent.
> 	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __remove_extent.
> 	* g++.dg/ext/remove_extent.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  5 +++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
>   4 files changed, 25 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C
> 
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 63f879287ce..577c96d579b 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
>   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
>   DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
>   DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
> +DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
>   DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
>   DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
>   DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 635441a7a90..58696225fc4 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12793,6 +12793,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
>   	type1 = TREE_TYPE (type1);
>         return cv_unqualified (type1);
>   
> +    case CPTK_REMOVE_EXTENT:
> +      if (TREE_CODE (type1) == ARRAY_TYPE)
> +	type1 = TREE_TYPE (type1);
> +      return type1;
> +
>       case CPTK_REMOVE_POINTER:
>         if (TYPE_PTR_P (type1))
>   	type1 = TREE_TYPE (type1);
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 9d861398bae..5d5cbe3b019 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -182,6 +182,9 @@
>   #if !__has_builtin (__remove_cvref)
>   # error "__has_builtin (__remove_cvref) failed"
>   #endif
> +#if !__has_builtin (__remove_extent)
> +# error "__has_builtin (__remove_extent) failed"
> +#endif
>   #if !__has_builtin (__remove_pointer)
>   # error "__has_builtin (__remove_pointer) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
> new file mode 100644
> index 00000000000..6183aca5a48
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
> @@ -0,0 +1,16 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +
> +SA(__is_same(__remove_extent(int), int));
> +SA(__is_same(__remove_extent(int[2]), int));
> +SA(__is_same(__remove_extent(int[2][3]), int[3]));
> +SA(__is_same(__remove_extent(int[][3]), int[3]));
> +SA(__is_same(__remove_extent(const int[2]), const int));
> +SA(__is_same(__remove_extent(ClassType), ClassType));
> +SA(__is_same(__remove_extent(ClassType[2]), ClassType));
> +SA(__is_same(__remove_extent(ClassType[2][3]), ClassType[3]));
> +SA(__is_same(__remove_extent(ClassType[][3]), ClassType[3]));
> +SA(__is_same(__remove_extent(const ClassType[2]), const ClassType));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 13/26] c++: Implement __remove_all_extents built-in trait
  2024-02-28 19:26                 ` [PATCH v14 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
@ 2024-04-30 21:00                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 21:00 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::remove_all_extents.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __remove_all_extents.
> 	* semantics.cc (finish_trait_type): Handle
> 	CPTK_REMOVE_ALL_EXTENTS.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of
> 	__remove_all_extents.
> 	* g++.dg/ext/remove_all_extents.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/cp-trait.def                           |  1 +
>   gcc/cp/semantics.cc                           |  3 +++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
>   gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
>   4 files changed, 23 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C
> 
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 577c96d579b..933c8bcbe68 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
>   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
>   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
>   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
> +DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
>   DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
>   DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
>   DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 58696225fc4..078424dac23 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12785,6 +12785,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
>   	type1 = TREE_TYPE (type1);
>         return build_pointer_type (type1);
>   
> +    case CPTK_REMOVE_ALL_EXTENTS:
> +      return strip_array_types (type1);
> +
>       case CPTK_REMOVE_CV:
>         return cv_unqualified (type1);
>   
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 5d5cbe3b019..85b74bd676b 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -176,6 +176,9 @@
>   #if !__has_builtin (__reference_converts_from_temporary)
>   # error "__has_builtin (__reference_converts_from_temporary) failed"
>   #endif
> +#if !__has_builtin (__remove_all_extents)
> +# error "__has_builtin (__remove_all_extents) failed"
> +#endif
>   #if !__has_builtin (__remove_cv)
>   # error "__has_builtin (__remove_cv) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
> new file mode 100644
> index 00000000000..60ade2ade7f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
> @@ -0,0 +1,16 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +
> +SA(__is_same(__remove_all_extents(int), int));
> +SA(__is_same(__remove_all_extents(int[2]), int));
> +SA(__is_same(__remove_all_extents(int[2][3]), int));
> +SA(__is_same(__remove_all_extents(int[][3]), int));
> +SA(__is_same(__remove_all_extents(const int[2][3]), const int));
> +SA(__is_same(__remove_all_extents(ClassType), ClassType));
> +SA(__is_same(__remove_all_extents(ClassType[2]), ClassType));
> +SA(__is_same(__remove_all_extents(ClassType[2][3]), ClassType));
> +SA(__is_same(__remove_all_extents(ClassType[][3]), ClassType));
> +SA(__is_same(__remove_all_extents(const ClassType[2][3]), const ClassType));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 15/26] c++: Implement __add_lvalue_reference built-in trait
  2024-02-28 19:26                 ` [PATCH v14 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
@ 2024-04-30 21:00                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 21:00 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::add_lvalue_reference.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __add_lvalue_reference.
> 	* semantics.cc (finish_trait_type): Handle
> 	CPTK_ADD_LVALUE_REFERENCE.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of
> 	__add_lvalue_reference.
> 	* g++.dg/ext/add_lvalue_reference.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/cp-trait.def                           |  1 +
>   gcc/cp/semantics.cc                           |  8 +++++++
>   .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
>   4 files changed, 33 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
> 
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 933c8bcbe68..9a27dca4ea3 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -48,6 +48,7 @@
>   #define DEFTRAIT_TYPE_DEFAULTED
>   #endif
>   
> +DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
>   DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 078424dac23..05f5b62f9df 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12776,6 +12776,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
>   
>     switch (kind)
>       {
> +    case CPTK_ADD_LVALUE_REFERENCE:
> +      if (VOID_TYPE_P (type1)
> +	  || (FUNC_OR_METHOD_TYPE_P (type1)
> +	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
> +		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))
> +	return type1;
> +      return cp_build_reference_type (type1, /*rval=*/false);
> +
>       case CPTK_ADD_POINTER:
>         if (FUNC_OR_METHOD_TYPE_P (type1)
>   	  && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
> diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
> new file mode 100644
> index 00000000000..8fe1e0300e5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
> @@ -0,0 +1,21 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +
> +SA(__is_same(__add_lvalue_reference(int), int&));
> +SA(__is_same(__add_lvalue_reference(int&), int&));
> +SA(__is_same(__add_lvalue_reference(const int), const int&));
> +SA(__is_same(__add_lvalue_reference(int*), int*&));
> +SA(__is_same(__add_lvalue_reference(ClassType&), ClassType&));
> +SA(__is_same(__add_lvalue_reference(ClassType), ClassType&));
> +SA(__is_same(__add_lvalue_reference(int(int)), int(&)(int)));
> +SA(__is_same(__add_lvalue_reference(int&&), int&));
> +SA(__is_same(__add_lvalue_reference(ClassType&&), ClassType&));
> +SA(__is_same(__add_lvalue_reference(void), void));
> +SA(__is_same(__add_lvalue_reference(const void), const void));
> +SA(__is_same(__add_lvalue_reference(bool(int) const), bool(int) const));
> +SA(__is_same(__add_lvalue_reference(bool(int) &), bool(int) &));
> +SA(__is_same(__add_lvalue_reference(bool(int) const &&), bool(int) const &&));
> +SA(__is_same(__add_lvalue_reference(bool(int)), bool(&)(int)));
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 85b74bd676b..3fca9cfabcc 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -2,6 +2,9 @@
>   // { dg-do compile }
>   // Verify that __has_builtin gives the correct answer for C++ built-ins.
>   
> +#if !__has_builtin (__add_lvalue_reference)
> +# error "__has_builtin (__add_lvalue_reference) failed"
> +#endif
>   #if !__has_builtin (__add_pointer)
>   # error "__has_builtin (__add_pointer) failed"
>   #endif


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 17/26] c++: Implement __add_rvalue_reference built-in trait
  2024-02-28 19:26                 ` [PATCH v14 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
@ 2024-04-30 21:02                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 21:02 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::add_rvalue_reference.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __add_rvalue_reference.
> 	* semantics.cc (finish_trait_type): Handle
> 	CPTK_ADD_RVALUE_REFERENCE.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of
> 	__add_rvalue_reference.
> 	* g++.dg/ext/add_rvalue_reference.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/cp-trait.def                           |  1 +
>   gcc/cp/semantics.cc                           |  8 ++++++++
>   .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
>   4 files changed, 32 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
> 
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 9a27dca4ea3..173818adf79 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -50,6 +50,7 @@
>   
>   DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
>   DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
> +DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 05f5b62f9df..19d6f87a9ea 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12793,6 +12793,14 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
>   	type1 = TREE_TYPE (type1);
>         return build_pointer_type (type1);
>   
> +    case CPTK_ADD_RVALUE_REFERENCE:
> +      if (VOID_TYPE_P (type1)
> +	  || (FUNC_OR_METHOD_TYPE_P (type1)
> +	      && (type_memfn_quals (type1) != TYPE_UNQUALIFIED
> +		  || type_memfn_rqual (type1) != REF_QUAL_NONE)))

Actually, let's factor this bit out of this and the other traits into a 
referenceable_type_p function since it's getting checked so much.

> +	return type1;
> +      return cp_build_reference_type (type1, /*rval=*/true);
> +
>       case CPTK_REMOVE_ALL_EXTENTS:
>         return strip_array_types (type1);
>   
> diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
> new file mode 100644
> index 00000000000..c92fe6bfa17
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +
> +SA(__is_same(__add_rvalue_reference(int), int&&));
> +SA(__is_same(__add_rvalue_reference(int&&), int&&));
> +SA(__is_same(__add_rvalue_reference(int&), int&));
> +SA(__is_same(__add_rvalue_reference(const int), const int&&));
> +SA(__is_same(__add_rvalue_reference(int*), int*&&));
> +SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
> +SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
> +SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
> +SA(__is_same(__add_rvalue_reference(void), void));
> +SA(__is_same(__add_rvalue_reference(const void), const void));
> +SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
> +SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
> +SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
> +SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 3fca9cfabcc..c2503c5d82b 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -8,6 +8,9 @@
>   #if !__has_builtin (__add_pointer)
>   # error "__has_builtin (__add_pointer) failed"
>   #endif
> +#if !__has_builtin (__add_rvalue_reference)
> +# error "__has_builtin (__add_rvalue_reference) failed"
> +#endif
>   #if !__has_builtin (__builtin_addressof)
>   # error "__has_builtin (__builtin_addressof) failed"
>   #endif


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 19/26] c++: Implement __decay built-in trait
  2024-02-28 19:26                 ` [PATCH v14 19/26] c++: Implement __decay built-in trait Ken Matsui
@ 2024-04-30 21:03                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 21:03 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::decay.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __decay.
> 	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __decay.
> 	* g++.dg/ext/decay.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      | 12 ++++++++++++
>   gcc/testsuite/g++.dg/ext/decay.C         | 22 ++++++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   4 files changed, 38 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/decay.C
> 
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 173818adf79..2d1cb7c227c 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -51,6 +51,7 @@
>   DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
>   DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
>   DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
> +DEFTRAIT_TYPE (DECAY, "__decay", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
>   DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 19d6f87a9ea..45dc509855a 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12801,6 +12801,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
>   	return type1;
>         return cp_build_reference_type (type1, /*rval=*/true);
>   
> +    case CPTK_DECAY:
> +      if (TYPE_REF_P (type1))
> +	type1 = TREE_TYPE (type1);
> +
> +      if (TREE_CODE (type1) == ARRAY_TYPE)
> +	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
> +				  complain);
> +      else if (TREE_CODE (type1) == FUNCTION_TYPE)
> +	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
> +      else
> +	return cv_unqualified (type1);
> +
>       case CPTK_REMOVE_ALL_EXTENTS:
>         return strip_array_types (type1);
>   
> diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
> new file mode 100644
> index 00000000000..8adedfeefe6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/decay.C
> @@ -0,0 +1,22 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +// Positive tests.
> +using test1_type = __decay(bool);
> +SA(__is_same(test1_type, bool));
> +
> +// NB: DR 705.
> +using test2_type = __decay(const int);
> +SA(__is_same(test2_type, int));
> +
> +using test3_type = __decay(int[4]);
> +SA(__is_same(test3_type, __remove_extent(int[4])*));
> +
> +using fn_type = void ();
> +using test4_type = __decay(fn_type);
> +SA(__is_same(test4_type, __add_pointer(fn_type)));
> +
> +using cfn_type = void () const;
> +using test5_type = __decay(cfn_type);
> +SA(__is_same(test5_type, cfn_type));
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index c2503c5d82b..3aca273aad6 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -11,6 +11,9 @@
>   #if !__has_builtin (__add_rvalue_reference)
>   # error "__has_builtin (__add_rvalue_reference) failed"
>   #endif
> +#if !__has_builtin (__decay)
> +# error "__has_builtin (__decay) failed"
> +#endif
>   #if !__has_builtin (__builtin_addressof)
>   # error "__has_builtin (__builtin_addressof) failed"
>   #endif


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-02-28 19:26                 ` [PATCH v14 21/26] c++: Implement __rank built-in trait Ken Matsui
@ 2024-04-30 21:08                   ` Jason Merrill
  2024-05-02 15:00                     ` Ken Matsui
  2024-05-02 15:15                     ` Patrick Palka
  0 siblings, 2 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 21:08 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::rank.

__rank seems too short, maybe __array_rank?

Actually, it occurs to me that perhaps we should have been adding 
__builtin to all of these rather than just __ and the library trait 
name.  I guess it's too late to do that for the GCC 14 traits, but we 
could do it for this group?

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __rank.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __rank.
> 	* g++.dg/ext/rank.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 +++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
>   5 files changed, 51 insertions(+), 3 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 000df847342..23ea66d9c12 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_VOLATILE:
>         inform (loc, "  %qT is not a volatile type", t1);
>         break;
> +    case CPTK_RANK:
> +      inform (loc, "  %qT cannot yield a rank", t1);
> +      break;
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>         inform (loc, "  %qT is not a reference that binds to a temporary "
>   	      "object of type %qT (direct-initialization)", t1, t2);
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 2d1cb7c227c..85056c8140b 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
>   DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
>   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
>   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
> +DEFTRAIT_EXPR (RANK, "__rank", 1)
>   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
>   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
>   DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 45dc509855a..7242db75248 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_DEDUCIBLE:
>         return type_targs_deducible_from (type1, type2);
>   
> +    /* __rank is handled in finish_trait_expr. */
> +    case CPTK_RANK:

This should have a gcc_unreachable.

> +
>   #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
>       case CPTK_##CODE:
>   #include "cp-trait.def"
> @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>     if (processing_template_decl)
>       {
>         tree trait_expr = make_node (TRAIT_EXPR);
> -      TREE_TYPE (trait_expr) = boolean_type_node;
> +      if (kind == CPTK_RANK)
> +	TREE_TYPE (trait_expr) = size_type_node;
> +      else
> +	TREE_TYPE (trait_expr) = boolean_type_node;
>         TRAIT_EXPR_TYPE1 (trait_expr) = type1;
>         TRAIT_EXPR_TYPE2 (trait_expr) = type2;
>         TRAIT_EXPR_KIND (trait_expr) = kind;
> @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_UNBOUNDED_ARRAY:
>       case CPTK_IS_UNION:
>       case CPTK_IS_VOLATILE:
> +    case CPTK_RANK:
>         break;
>   
>       case CPTK_IS_LAYOUT_COMPATIBLE:
> @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>         gcc_unreachable ();
>       }
>   
> -  tree val = (trait_expr_value (kind, type1, type2)
> -	      ? boolean_true_node : boolean_false_node);
> +  tree val;
> +  if (kind == CPTK_RANK)
> +    {
> +      size_t rank = 0;
> +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
> +	++rank;
> +      val = build_int_cst (size_type_node, rank);
> +    }
> +  else
> +    val = (trait_expr_value (kind, type1, type2)
> +	   ? boolean_true_node : boolean_false_node);
> +
>     return maybe_wrap_with_location (val, loc);
>   }
>   
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 3aca273aad6..7f7b27f7aa7 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -179,6 +179,9 @@
>   #if !__has_builtin (__is_volatile)
>   # error "__has_builtin (__is_volatile) failed"
>   #endif
> +#if !__has_builtin (__rank)
> +# error "__has_builtin (__rank) failed"
> +#endif
>   #if !__has_builtin (__reference_constructs_from_temporary)
>   # error "__has_builtin (__reference_constructs_from_temporary) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
> new file mode 100644
> index 00000000000..28894184387
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/rank.C
> @@ -0,0 +1,24 @@
> +// { dg-do compile { target c++11 } }
> +
> +#include <cstddef>
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +
> +SA(__rank(int) == 0);
> +SA(__rank(int[2]) == 1);
> +SA(__rank(int[][4]) == 2);
> +SA(__rank(int[2][2][4][4][6][6]) == 6);
> +SA(__rank(ClassType) == 0);
> +SA(__rank(ClassType[2]) == 1);
> +SA(__rank(ClassType[][4]) == 2);
> +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
> +
> +template<class T> void f(T) = delete;
> +void f(size_t);
> +
> +template<class T>
> +void g() { f(__rank(T)); }
> +
> +template void g<int>();


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 25/26] c++: Implement __is_nothrow_invocable built-in trait
  2024-02-28 19:26                 ` [PATCH v14 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
@ 2024-04-30 21:09                   ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-04-30 21:09 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 2/28/24 11:26, Ken Matsui wrote:
> This patch implements built-in trait for std::is_nothrow_invocable.

OK after addressing comments on other traits.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_nothrow_invocable.
> 	* constraint.cc (diagnose_trait_expr): Handle
> 	CPTK_IS_NOTHROW_INVOCABLE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of
> 	__is_nothrow_invocable.
> 	* g++.dg/ext/is_nothrow_invocable.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                          |  6 ++
>   gcc/cp/cp-trait.def                           |  1 +
>   gcc/cp/semantics.cc                           |  4 ++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +
>   .../g++.dg/ext/is_nothrow_invocable.C         | 62 +++++++++++++++++++
>   5 files changed, 76 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index c87b126fdb1..43d4f2102d6 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3824,6 +3824,12 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_NOTHROW_CONVERTIBLE:
>   	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
>         break;
> +    case CPTK_IS_NOTHROW_INVOCABLE:
> +	if (!t2)
> +	  inform (loc, "  %qT is not nothrow invocable", t1);
> +	else
> +	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
> +	break;
>       case CPTK_IS_OBJECT:
>         inform (loc, "  %qT is not an object type", t1);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 6cb2b55f4ea..a9714921e94 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
>   DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
>   DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
>   DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
> +DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1)
>   DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
>   DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
>   DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 149c0631d62..dba7b43a109 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12494,6 +12494,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_NOTHROW_CONVERTIBLE:
>         return is_nothrow_convertible (type1, type2);
>   
> +    case CPTK_IS_NOTHROW_INVOCABLE:
> +      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
> +
>       case CPTK_IS_OBJECT:
>         return (type_code1 != FUNCTION_TYPE
>   	      && type_code1 != REFERENCE_TYPE
> @@ -12689,6 +12692,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_NOTHROW_ASSIGNABLE:
>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>       case CPTK_IS_NOTHROW_CONVERTIBLE:
> +    case CPTK_IS_NOTHROW_INVOCABLE:
>       case CPTK_IS_TRIVIALLY_ASSIGNABLE:
>       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index d2a7ebdf25c..624d3525f27 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -131,6 +131,9 @@
>   #if !__has_builtin (__is_nothrow_convertible)
>   # error "__has_builtin (__is_nothrow_convertible) failed"
>   #endif
> +#if !__has_builtin (__is_nothrow_invocable)
> +# error "__has_builtin (__is_nothrow_invocable) failed"
> +#endif
>   #if !__has_builtin (__is_object)
>   # error "__has_builtin (__is_object) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
> new file mode 100644
> index 00000000000..2f9b40e5538
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
> @@ -0,0 +1,62 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using func_type = void(*)();
> +SA( ! __is_nothrow_invocable(func_type) );
> +
> +#if __cpp_noexcept_function_type
> +using func_type_nt = void(*)() noexcept;
> +SA(   __is_nothrow_invocable(func_type_nt) );
> +#endif
> +
> +struct X { };
> +using mem_type = int X::*;
> +
> +SA( ! __is_nothrow_invocable(mem_type) );
> +SA( ! __is_nothrow_invocable(mem_type, int) );
> +SA( ! __is_nothrow_invocable(mem_type, int&) );
> +SA(   __is_nothrow_invocable(mem_type, X&) );
> +
> +using memfun_type = int (X::*)();
> +
> +SA( ! __is_nothrow_invocable(memfun_type) );
> +SA( ! __is_nothrow_invocable(memfun_type, int) );
> +SA( ! __is_nothrow_invocable(memfun_type, int&) );
> +SA( ! __is_nothrow_invocable(memfun_type, X&) );
> +SA( ! __is_nothrow_invocable(memfun_type, X*) );
> +
> +#if __cpp_noexcept_function_type
> +using memfun_type_nt = int (X::*)() noexcept;
> +
> +SA( ! __is_nothrow_invocable(memfun_type_nt) );
> +SA( ! __is_nothrow_invocable(memfun_type_nt, int) );
> +SA( ! __is_nothrow_invocable(memfun_type_nt, int&) );
> +SA(   __is_nothrow_invocable(memfun_type_nt, X&) );
> +SA(   __is_nothrow_invocable(memfun_type_nt, X*) );
> +#endif
> +
> +struct F {
> +  int& operator()();
> +  long& operator()() const noexcept;
> +  short& operator()(int) &&;
> +  char& operator()(int) const& noexcept;
> +private:
> +  void operator()(int, int) noexcept;
> +};
> +using CF = const F;
> +
> +SA( ! __is_nothrow_invocable(F ) );
> +SA(   __is_nothrow_invocable(CF) );
> +
> +SA( ! __is_nothrow_invocable(F,   int) );
> +SA(   __is_nothrow_invocable(F&,  int) );
> +
> +SA(   __is_nothrow_invocable(CF,   int) );
> +SA(   __is_nothrow_invocable(CF&,  int) );
> +SA( ! __is_nothrow_invocable(F, int, int) );
> +
> +struct FX {
> +  X operator()() const noexcept { return {}; }
> +};
> +SA(   __is_nothrow_invocable(FX) );


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-04-30 21:08                   ` Jason Merrill
@ 2024-05-02 15:00                     ` Ken Matsui
  2024-05-02 15:15                     ` Patrick Palka
  1 sibling, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 15:00 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Tue, Apr 30, 2024 at 2:08 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 2/28/24 11:26, Ken Matsui wrote:
> > This patch implements built-in trait for std::rank.
>
> __rank seems too short, maybe __array_rank?
>
> Actually, it occurs to me that perhaps we should have been adding
> __builtin to all of these rather than just __ and the library trait
> name.  I guess it's too late to do that for the GCC 14 traits, but we
> could do it for this group?

Let me add __builtin.  I think it would be better to add __builtin for
all existing traits for consistency, but is there any reason we want
to avoid that?

>
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __rank.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __rank.
> >       * g++.dg/ext/rank.C: New test.
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |  3 +++
> >   gcc/cp/cp-trait.def                      |  1 +
> >   gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
> >   gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
> >   5 files changed, 51 insertions(+), 3 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index 000df847342..23ea66d9c12 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_VOLATILE:
> >         inform (loc, "  %qT is not a volatile type", t1);
> >         break;
> > +    case CPTK_RANK:
> > +      inform (loc, "  %qT cannot yield a rank", t1);
> > +      break;
> >       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> >         inform (loc, "  %qT is not a reference that binds to a temporary "
> >             "object of type %qT (direct-initialization)", t1, t2);
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index 2d1cb7c227c..85056c8140b 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
> >   DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
> >   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> >   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
> > +DEFTRAIT_EXPR (RANK, "__rank", 1)
> >   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
> >   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
> >   DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index 45dc509855a..7242db75248 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
> >       case CPTK_IS_DEDUCIBLE:
> >         return type_targs_deducible_from (type1, type2);
> >
> > +    /* __rank is handled in finish_trait_expr. */
> > +    case CPTK_RANK:
>
> This should have a gcc_unreachable.
>
> > +
> >   #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
> >       case CPTK_##CODE:
> >   #include "cp-trait.def"
> > @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> >     if (processing_template_decl)
> >       {
> >         tree trait_expr = make_node (TRAIT_EXPR);
> > -      TREE_TYPE (trait_expr) = boolean_type_node;
> > +      if (kind == CPTK_RANK)
> > +     TREE_TYPE (trait_expr) = size_type_node;
> > +      else
> > +     TREE_TYPE (trait_expr) = boolean_type_node;
> >         TRAIT_EXPR_TYPE1 (trait_expr) = type1;
> >         TRAIT_EXPR_TYPE2 (trait_expr) = type2;
> >         TRAIT_EXPR_KIND (trait_expr) = kind;
> > @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> >       case CPTK_IS_UNBOUNDED_ARRAY:
> >       case CPTK_IS_UNION:
> >       case CPTK_IS_VOLATILE:
> > +    case CPTK_RANK:
> >         break;
> >
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> > @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> >         gcc_unreachable ();
> >       }
> >
> > -  tree val = (trait_expr_value (kind, type1, type2)
> > -           ? boolean_true_node : boolean_false_node);
> > +  tree val;
> > +  if (kind == CPTK_RANK)
> > +    {
> > +      size_t rank = 0;
> > +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
> > +     ++rank;
> > +      val = build_int_cst (size_type_node, rank);
> > +    }
> > +  else
> > +    val = (trait_expr_value (kind, type1, type2)
> > +        ? boolean_true_node : boolean_false_node);
> > +
> >     return maybe_wrap_with_location (val, loc);
> >   }
> >
> > diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > index 3aca273aad6..7f7b27f7aa7 100644
> > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > @@ -179,6 +179,9 @@
> >   #if !__has_builtin (__is_volatile)
> >   # error "__has_builtin (__is_volatile) failed"
> >   #endif
> > +#if !__has_builtin (__rank)
> > +# error "__has_builtin (__rank) failed"
> > +#endif
> >   #if !__has_builtin (__reference_constructs_from_temporary)
> >   # error "__has_builtin (__reference_constructs_from_temporary) failed"
> >   #endif
> > diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
> > new file mode 100644
> > index 00000000000..28894184387
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/rank.C
> > @@ -0,0 +1,24 @@
> > +// { dg-do compile { target c++11 } }
> > +
> > +#include <cstddef>
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +class ClassType { };
> > +
> > +SA(__rank(int) == 0);
> > +SA(__rank(int[2]) == 1);
> > +SA(__rank(int[][4]) == 2);
> > +SA(__rank(int[2][2][4][4][6][6]) == 6);
> > +SA(__rank(ClassType) == 0);
> > +SA(__rank(ClassType[2]) == 1);
> > +SA(__rank(ClassType[][4]) == 2);
> > +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
> > +
> > +template<class T> void f(T) = delete;
> > +void f(size_t);
> > +
> > +template<class T>
> > +void g() { f(__rank(T)); }
> > +
> > +template void g<int>();
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-04-30 21:08                   ` Jason Merrill
  2024-05-02 15:00                     ` Ken Matsui
@ 2024-05-02 15:15                     ` Patrick Palka
  2024-05-02 15:34                       ` Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Patrick Palka @ 2024-05-02 15:15 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Tue, 30 Apr 2024, Jason Merrill wrote:

> On 2/28/24 11:26, Ken Matsui wrote:
> > This patch implements built-in trait for std::rank.
> 
> __rank seems too short, maybe __array_rank?
> 
> Actually, it occurs to me that perhaps we should have been adding __builtin to
> all of these rather than just __ and the library trait name.  I guess it's too
> late to do that for the GCC 14 traits, but we could do it for this group?

Clang already implements many of these built-in, without using
"__builtin" in their name.  Shouldn't we be consistent with Clang where
we can?

> 
> > gcc/cp/ChangeLog:
> > 
> > 	* cp-trait.def: Define __rank.
> > 	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
> > 	* semantics.cc (trait_expr_value): Likewise.
> > 	(finish_trait_expr): Likewise.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/ext/has-builtin-1.C: Test existence of __rank.
> > 	* g++.dg/ext/rank.C: New test.
> > 
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |  3 +++
> >   gcc/cp/cp-trait.def                      |  1 +
> >   gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
> >   gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
> >   5 files changed, 51 insertions(+), 3 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
> > 
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index 000df847342..23ea66d9c12 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_VOLATILE:
> >         inform (loc, "  %qT is not a volatile type", t1);
> >         break;
> > +    case CPTK_RANK:
> > +      inform (loc, "  %qT cannot yield a rank", t1);
> > +      break;
> >       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> >         inform (loc, "  %qT is not a reference that binds to a temporary "
> >   	      "object of type %qT (direct-initialization)", t1, t2);
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index 2d1cb7c227c..85056c8140b 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE,
> > "__is_trivially_copyable", 1)
> >   DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
> >   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> >   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
> > +DEFTRAIT_EXPR (RANK, "__rank", 1)
> >   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY,
> > "__reference_constructs_from_temporary", 2)
> >   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY,
> > "__reference_converts_from_temporary", 2)
> >   DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index 45dc509855a..7242db75248 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1,
> > tree type2)
> >       case CPTK_IS_DEDUCIBLE:
> >         return type_targs_deducible_from (type1, type2);
> >   +    /* __rank is handled in finish_trait_expr. */
> > +    case CPTK_RANK:
> 
> This should have a gcc_unreachable.
> 
> > +
> >   #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
> >       case CPTK_##CODE:
> >   #include "cp-trait.def"
> > @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > kind, tree type1, tree type2)
> >     if (processing_template_decl)
> >       {
> >         tree trait_expr = make_node (TRAIT_EXPR);
> > -      TREE_TYPE (trait_expr) = boolean_type_node;
> > +      if (kind == CPTK_RANK)
> > +	TREE_TYPE (trait_expr) = size_type_node;
> > +      else
> > +	TREE_TYPE (trait_expr) = boolean_type_node;
> >         TRAIT_EXPR_TYPE1 (trait_expr) = type1;
> >         TRAIT_EXPR_TYPE2 (trait_expr) = type2;
> >         TRAIT_EXPR_KIND (trait_expr) = kind;
> > @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > kind, tree type1, tree type2)
> >       case CPTK_IS_UNBOUNDED_ARRAY:
> >       case CPTK_IS_UNION:
> >       case CPTK_IS_VOLATILE:
> > +    case CPTK_RANK:
> >         break;
> >         case CPTK_IS_LAYOUT_COMPATIBLE:
> > @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > kind, tree type1, tree type2)
> >         gcc_unreachable ();
> >       }
> >   -  tree val = (trait_expr_value (kind, type1, type2)
> > -	      ? boolean_true_node : boolean_false_node);
> > +  tree val;
> > +  if (kind == CPTK_RANK)
> > +    {
> > +      size_t rank = 0;
> > +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
> > +	++rank;
> > +      val = build_int_cst (size_type_node, rank);
> > +    }
> > +  else
> > +    val = (trait_expr_value (kind, type1, type2)
> > +	   ? boolean_true_node : boolean_false_node);
> > +
> >     return maybe_wrap_with_location (val, loc);
> >   }
> >   diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > index 3aca273aad6..7f7b27f7aa7 100644
> > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > @@ -179,6 +179,9 @@
> >   #if !__has_builtin (__is_volatile)
> >   # error "__has_builtin (__is_volatile) failed"
> >   #endif
> > +#if !__has_builtin (__rank)
> > +# error "__has_builtin (__rank) failed"
> > +#endif
> >   #if !__has_builtin (__reference_constructs_from_temporary)
> >   # error "__has_builtin (__reference_constructs_from_temporary) failed"
> >   #endif
> > diff --git a/gcc/testsuite/g++.dg/ext/rank.C
> > b/gcc/testsuite/g++.dg/ext/rank.C
> > new file mode 100644
> > index 00000000000..28894184387
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/rank.C
> > @@ -0,0 +1,24 @@
> > +// { dg-do compile { target c++11 } }
> > +
> > +#include <cstddef>
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +class ClassType { };
> > +
> > +SA(__rank(int) == 0);
> > +SA(__rank(int[2]) == 1);
> > +SA(__rank(int[][4]) == 2);
> > +SA(__rank(int[2][2][4][4][6][6]) == 6);
> > +SA(__rank(ClassType) == 0);
> > +SA(__rank(ClassType[2]) == 1);
> > +SA(__rank(ClassType[][4]) == 2);
> > +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
> > +
> > +template<class T> void f(T) = delete;
> > +void f(size_t);
> > +
> > +template<class T>
> > +void g() { f(__rank(T)); }
> > +
> > +template void g<int>();
> 
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-05-02 15:15                     ` Patrick Palka
@ 2024-05-02 15:34                       ` Ken Matsui
  2024-05-02 16:20                         ` Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 15:34 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Jason Merrill, Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 8:16 AM Patrick Palka <ppalka@redhat.com> wrote:
>
> On Tue, 30 Apr 2024, Jason Merrill wrote:
>
> > On 2/28/24 11:26, Ken Matsui wrote:
> > > This patch implements built-in trait for std::rank.
> >
> > __rank seems too short, maybe __array_rank?
> >
> > Actually, it occurs to me that perhaps we should have been adding __builtin to
> > all of these rather than just __ and the library trait name.  I guess it's too
> > late to do that for the GCC 14 traits, but we could do it for this group?
>
> Clang already implements many of these built-in, without using
> "__builtin" in their name.  Shouldn't we be consistent with Clang where
> we can?

Oh, then, would using __array_rank and keeping existing built-in
traits as-is sound like a better choice?

https://github.com/llvm/llvm-project/blob/4aca302f5a82ee65847c88500b39a2530dfeceb4/libcxx/include/__type_traits/rank.h#L23

>
> >
> > > gcc/cp/ChangeLog:
> > >
> > >     * cp-trait.def: Define __rank.
> > >     * constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
> > >     * semantics.cc (trait_expr_value): Likewise.
> > >     (finish_trait_expr): Likewise.
> > >
> > > gcc/testsuite/ChangeLog:
> > >
> > >     * g++.dg/ext/has-builtin-1.C: Test existence of __rank.
> > >     * g++.dg/ext/rank.C: New test.
> > >
> > > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > > ---
> > >   gcc/cp/constraint.cc                     |  3 +++
> > >   gcc/cp/cp-trait.def                      |  1 +
> > >   gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
> > >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
> > >   gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
> > >   5 files changed, 51 insertions(+), 3 deletions(-)
> > >   create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
> > >
> > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > > index 000df847342..23ea66d9c12 100644
> > > --- a/gcc/cp/constraint.cc
> > > +++ b/gcc/cp/constraint.cc
> > > @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
> > >       case CPTK_IS_VOLATILE:
> > >         inform (loc, "  %qT is not a volatile type", t1);
> > >         break;
> > > +    case CPTK_RANK:
> > > +      inform (loc, "  %qT cannot yield a rank", t1);
> > > +      break;
> > >       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> > >         inform (loc, "  %qT is not a reference that binds to a temporary "
> > >           "object of type %qT (direct-initialization)", t1, t2);
> > > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > > index 2d1cb7c227c..85056c8140b 100644
> > > --- a/gcc/cp/cp-trait.def
> > > +++ b/gcc/cp/cp-trait.def
> > > @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE,
> > > "__is_trivially_copyable", 1)
> > >   DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
> > >   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> > >   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
> > > +DEFTRAIT_EXPR (RANK, "__rank", 1)
> > >   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY,
> > > "__reference_constructs_from_temporary", 2)
> > >   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY,
> > > "__reference_converts_from_temporary", 2)
> > >   DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
> > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > > index 45dc509855a..7242db75248 100644
> > > --- a/gcc/cp/semantics.cc
> > > +++ b/gcc/cp/semantics.cc
> > > @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1,
> > > tree type2)
> > >       case CPTK_IS_DEDUCIBLE:
> > >         return type_targs_deducible_from (type1, type2);
> > >   +    /* __rank is handled in finish_trait_expr. */
> > > +    case CPTK_RANK:
> >
> > This should have a gcc_unreachable.
> >
> > > +
> > >   #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
> > >       case CPTK_##CODE:
> > >   #include "cp-trait.def"
> > > @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > > kind, tree type1, tree type2)
> > >     if (processing_template_decl)
> > >       {
> > >         tree trait_expr = make_node (TRAIT_EXPR);
> > > -      TREE_TYPE (trait_expr) = boolean_type_node;
> > > +      if (kind == CPTK_RANK)
> > > +   TREE_TYPE (trait_expr) = size_type_node;
> > > +      else
> > > +   TREE_TYPE (trait_expr) = boolean_type_node;
> > >         TRAIT_EXPR_TYPE1 (trait_expr) = type1;
> > >         TRAIT_EXPR_TYPE2 (trait_expr) = type2;
> > >         TRAIT_EXPR_KIND (trait_expr) = kind;
> > > @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > > kind, tree type1, tree type2)
> > >       case CPTK_IS_UNBOUNDED_ARRAY:
> > >       case CPTK_IS_UNION:
> > >       case CPTK_IS_VOLATILE:
> > > +    case CPTK_RANK:
> > >         break;
> > >         case CPTK_IS_LAYOUT_COMPATIBLE:
> > > @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > > kind, tree type1, tree type2)
> > >         gcc_unreachable ();
> > >       }
> > >   -  tree val = (trait_expr_value (kind, type1, type2)
> > > -         ? boolean_true_node : boolean_false_node);
> > > +  tree val;
> > > +  if (kind == CPTK_RANK)
> > > +    {
> > > +      size_t rank = 0;
> > > +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
> > > +   ++rank;
> > > +      val = build_int_cst (size_type_node, rank);
> > > +    }
> > > +  else
> > > +    val = (trait_expr_value (kind, type1, type2)
> > > +      ? boolean_true_node : boolean_false_node);
> > > +
> > >     return maybe_wrap_with_location (val, loc);
> > >   }
> > >   diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > index 3aca273aad6..7f7b27f7aa7 100644
> > > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > @@ -179,6 +179,9 @@
> > >   #if !__has_builtin (__is_volatile)
> > >   # error "__has_builtin (__is_volatile) failed"
> > >   #endif
> > > +#if !__has_builtin (__rank)
> > > +# error "__has_builtin (__rank) failed"
> > > +#endif
> > >   #if !__has_builtin (__reference_constructs_from_temporary)
> > >   # error "__has_builtin (__reference_constructs_from_temporary) failed"
> > >   #endif
> > > diff --git a/gcc/testsuite/g++.dg/ext/rank.C
> > > b/gcc/testsuite/g++.dg/ext/rank.C
> > > new file mode 100644
> > > index 00000000000..28894184387
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/ext/rank.C
> > > @@ -0,0 +1,24 @@
> > > +// { dg-do compile { target c++11 } }
> > > +
> > > +#include <cstddef>
> > > +
> > > +#define SA(X) static_assert((X),#X)
> > > +
> > > +class ClassType { };
> > > +
> > > +SA(__rank(int) == 0);
> > > +SA(__rank(int[2]) == 1);
> > > +SA(__rank(int[][4]) == 2);
> > > +SA(__rank(int[2][2][4][4][6][6]) == 6);
> > > +SA(__rank(ClassType) == 0);
> > > +SA(__rank(ClassType[2]) == 1);
> > > +SA(__rank(ClassType[][4]) == 2);
> > > +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
> > > +
> > > +template<class T> void f(T) = delete;
> > > +void f(size_t);
> > > +
> > > +template<class T>
> > > +void g() { f(__rank(T)); }
> > > +
> > > +template void g<int>();
> >
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 00/26] Optimize more type traits
  2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
                                   ` (26 preceding siblings ...)
  2024-03-07 21:33                 ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
@ 2024-05-02 16:13                 ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 01/26] c++: Implement __builtin_is_const trait Ken Matsui
                                     ` (26 more replies)
  27 siblings, 27 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

Changes from v15:

* Rebased on top of trunk
* Fixed build_invoke
* Implemented object_type_p & referenceable_type_p
* Prefixed all built-in traits with __builtin instead of __

--

This patch series implements __is_const, __is_volatile, __is_pointer,
and __is_unbounded_array built-in traits, which were isolated from my
previous patch series "Optimize type traits compilation performance"
because they contained performance regression.  I confirmed that this
patch series does not cause any performance regression.  The main reason
of the performance regression were the exhaustiveness of the benchmarks
and the instability of the benchmark results.  Also, this patch series
includes built-ins for add_pointer, remove_extent, remove_all_extents,
add_lvalue_reference, add_rvalue_reference, decay, rank, is_invocable,
and is_nothrow_invocable.  Here are the benchmark results:

is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023
time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%

is_const_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_const_v.md#sat-jun-24-044815-am-pdt-2023
time: -2.86467%, peak memory: -1.0654%, total memory: -1.62369%

is_volatile: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile.md#sun-oct-22-091644-pm-pdt-2023
time: -5.25164%, peak memory: -0.337971%, total memory: -0.247934%

is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023
time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%

is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023
time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%

is_pointer_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer_v.md#sun-oct-22-122257-am-pdt-2023
time: -4.71336%, peak memory: -2.25026%, total memory: -3.125%

is_unbounded_array: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array.md#sun-oct-22-091644-pm-pdt-2023
time: -6.33287%, peak memory: -0.602494%, total memory: -1.56035%

is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023
time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%

add_pointer_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_pointer_t.md#wed-feb-28-060044-am-pst-2024
time: -21.6673%, peak memory: -14.6666%, total memory: -17.4716%

remove_extent_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_extent_t.md#wed-feb-28-063021-am-pst-2024
time: -14.4089%, peak memory: -2.71836%, total memory: -9.87013%

remove_all_extents_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_all_extents_t.md#wed-feb-28-064716-am-pst-2024
time: -28.8941%, peak memory: -16.6981%, total memory: -23.6088%

add_lvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_lvalue_reference_t.md#wed-feb-28-070023-am-pst-2024
time: -33.8827%, peak memory: -24.9292%, total memory: -25.3043%

add_rvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_rvalue_reference_t.md#wed-feb-28-070701-am-pst-2024
time: -23.9186%, peak memory: -17.1311%, total memory: -19.5891%

decay_t: https://github.com/ken-matsui/gcc-bench/blob/main/decay_t.md#wed-feb-28-072330-am-pst-2024
time: -42.4076%, peak memory: -29.2077%, total memory: -33.0914%

rank: https://github.com/ken-matsui/gcc-bench/blob/main/rank.md#wed-feb-28-074917-am-pst-2024
time: -33.7312%, peak memory: -27.5885%, total memory: -34.5736%

rank_v: https://github.com/ken-matsui/gcc-bench/blob/main/rank_v.md#wed-feb-28-073632-am-pst-2024
time: -40.7174%, peak memory: -16.4653%, total memory: -23.0131%

is_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_invocable.md#wed-feb-28-111001-am-pst-2024
time: -58.8307%, peak memory: -59.4966%, total memory: -59.8871%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

is_nothrow_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_nothrow_invocable.md#wed-feb-28-112414-am-pst-2024
time: -70.4102%, peak memory: -62.5516%, total memory: -65.5853%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

Ken Matsui (26):
  c++: Implement __builtin_is_const trait
  libstdc++: Optimize std::is_const compilation performance
  c++: Implement __builtin_is_volatile trait
  libstdc++: Optimize std::is_volatile compilation performance
  c++: Implement __builtin_is_pointer trait
  libstdc++: Optimize std::is_pointer compilation performance
  c++: Implement __builtin_is_unbounded_array trait
  libstdc++: Optimize std::is_unbounded_array compilation performance
  c++: Implement __builtin_add_pointer trait
  libstdc++: Optimize std::add_pointer compilation performance
  c++: Implement __builtin_remove_extent trait
  libstdc++: Optimize std::remove_extent compilation performance
  c++: Implement __builtin_remove_all_extents trait
  libstdc++: Optimize std::remove_all_extents compilation performance
  c++: Implement __builtin_add_lvalue_reference trait
  libstdc++: Optimize std::add_lvalue_reference compilation performance
  c++: Implement __builtin_add_rvalue_reference trait
  libstdc++: Optimize std::add_rvalue_reference compilation performance
  c++: Implement __builtin_decay trait
  libstdc++: Optimize std::decay compilation performance
  c++: Implement __builtin_rank trait
  libstdc++: Optimize std::rank compilation performance
  c++: Implement __builtin_is_invocable trait
  libstdc++: Optimize std::is_invocable compilation performance
  c++: Implement __builtin_is_nothrow_invocable trait
  libstdc++: Optimize std::is_nothrow_invocable compilation performance

 gcc/cp/constraint.cc                          |  27 ++
 gcc/cp/cp-trait.def                           |  13 +
 gcc/cp/cp-tree.h                              |   2 +
 gcc/cp/method.cc                              | 134 +++++++
 gcc/cp/semantics.cc                           | 118 +++++-
 .../g++.dg/ext/add_lvalue_reference.C         |  21 ++
 gcc/testsuite/g++.dg/ext/add_pointer.C        |  39 ++
 .../g++.dg/ext/add_rvalue_reference.C         |  20 +
 gcc/testsuite/g++.dg/ext/decay.C              |  22 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  39 ++
 gcc/testsuite/g++.dg/ext/is_const.C           |  20 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C      | 349 ++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C      | 139 +++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C      |  51 +++
 gcc/testsuite/g++.dg/ext/is_invocable4.C      |  33 ++
 .../g++.dg/ext/is_nothrow_invocable.C         |  62 ++++
 gcc/testsuite/g++.dg/ext/is_pointer.C         |  51 +++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C |  37 ++
 gcc/testsuite/g++.dg/ext/is_volatile.C        |  20 +
 gcc/testsuite/g++.dg/ext/rank.C               |  24 ++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C |  16 +
 gcc/testsuite/g++.dg/ext/remove_extent.C      |  16 +
 libstdc++-v3/include/bits/cpp_type_traits.h   |  31 +-
 libstdc++-v3/include/std/type_traits          | 143 ++++++-
 .../is_invocable/incomplete_args_neg.cc       |   1 +
 .../20_util/is_invocable/incomplete_neg.cc    |   1 +
 .../incomplete_args_neg.cc                    |   1 +
 .../is_nothrow_invocable/incomplete_neg.cc    |   1 +
 28 files changed, 1415 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 01/26] c++: Implement __builtin_is_const trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                                     ` (25 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 8a3b5d80ba7..eaf17a50877 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3768,6 +3768,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..4cc11818a66 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__builtin_is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 2dde65a970b..fa7ba6a9edc 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12530,6 +12530,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12812,6 +12815,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..29199442ea1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -8,6 +8,9 @@
 #if !__has_builtin (__builtin_bit_cast)
 # error "__has_builtin (__builtin_bit_cast) failed"
 #endif
+#if !__has_builtin (__builtin_is_const)
+# error "__has_builtin (__builtin_is_const) failed"
+#endif
 #if !__has_builtin (__builtin_is_constant_evaluated)
 # error "__has_builtin (__builtin_is_constant_evaluated) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..0dc9027c589
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__builtin_is_const(const int));
+SA(__builtin_is_const(const volatile int));
+SA(__builtin_is_const(cClassType));
+SA(__builtin_is_const(cvClassType));
+
+// Negative tests.
+SA(!__builtin_is_const(int));
+SA(!__builtin_is_const(volatile int));
+SA(!__builtin_is_const(ClassType));
+SA(!__builtin_is_const(vClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 02/26] libstdc++: Optimize std::is_const compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 01/26] c++: Implement __builtin_is_const trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 03/26] c++: Implement __builtin_is_volatile trait Ken Matsui
                                     ` (24 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __builtin_is_const trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __builtin_is_const
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b441bf9908f..b2a696ccd2e 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__builtin_is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3331,10 +3338,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __builtin_is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 03/26] c++: Implement __builtin_is_volatile trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 01/26] c++: Implement __builtin_is_const trait Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                                     ` (23 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index eaf17a50877..d9caf546423 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 4cc11818a66..a7ea3caff4e 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__builtin_is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fa7ba6a9edc..351235a639a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12826,6 +12829,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 29199442ea1..398719fe8d6 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -20,6 +20,9 @@
 #if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
 # error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
 #endif
+#if !__has_builtin (__builtin_is_volatile)
+# error "__has_builtin (__builtin_is_volatile) failed"
+#endif
 #if !__has_builtin (__builtin_launder)
 # error "__has_builtin (__builtin_launder) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..b04f028bd25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__builtin_is_volatile(volatile int));
+SA(__builtin_is_volatile(const volatile int));
+SA(__builtin_is_volatile(vClassType));
+SA(__builtin_is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__builtin_is_volatile(int));
+SA(!__builtin_is_volatile(const int));
+SA(!__builtin_is_volatile(ClassType));
+SA(!__builtin_is_volatile(cClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 04/26] libstdc++: Optimize std::is_volatile compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (2 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 03/26] c++: Implement __builtin_is_volatile trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 05/26] c++: Implement __builtin_is_pointer trait Ken Matsui
                                     ` (22 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __builtin_is_volatile trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __builtin_is_volatile
	trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b2a696ccd2e..e9674ff87d1 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__builtin_is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3360,10 +3367,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __builtin_is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 05/26] c++: Implement __builtin_is_pointer trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (3 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                                     ` (21 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d9caf546423..5a8aaa70fa6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3829,6 +3829,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index a7ea3caff4e..a6a3c195529 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__builtin_is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 351235a639a..86372ea0aba 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12586,6 +12586,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12825,6 +12828,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 398719fe8d6..7954b42df97 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -17,6 +17,9 @@
 #if !__has_builtin (__builtin_is_corresponding_member)
 # error "__has_builtin (__builtin_is_corresponding_member) failed"
 #endif
+#if !__has_builtin (__builtin_is_pointer)
+# error "__has_builtin (__builtin_is_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
 # error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..9a7a4939bbe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__builtin_is_pointer(int));
+SA(__builtin_is_pointer(int*));
+SA(__builtin_is_pointer(int**));
+
+SA(__builtin_is_pointer(const int*));
+SA(__builtin_is_pointer(const int**));
+SA(__builtin_is_pointer(int* const));
+SA(__builtin_is_pointer(int** const));
+SA(__builtin_is_pointer(int* const* const));
+
+SA(__builtin_is_pointer(volatile int*));
+SA(__builtin_is_pointer(volatile int**));
+SA(__builtin_is_pointer(int* volatile));
+SA(__builtin_is_pointer(int** volatile));
+SA(__builtin_is_pointer(int* volatile* volatile));
+
+SA(__builtin_is_pointer(const volatile int*));
+SA(__builtin_is_pointer(const volatile int**));
+SA(__builtin_is_pointer(const int* volatile));
+SA(__builtin_is_pointer(volatile int* const));
+SA(__builtin_is_pointer(int* const volatile));
+SA(__builtin_is_pointer(const int** volatile));
+SA(__builtin_is_pointer(volatile int** const));
+SA(__builtin_is_pointer(int** const volatile));
+SA(__builtin_is_pointer(int* const* const volatile));
+SA(__builtin_is_pointer(int* volatile* const volatile));
+SA(__builtin_is_pointer(int* const volatile* const volatile));
+
+SA(!__builtin_is_pointer(int&));
+SA(!__builtin_is_pointer(const int&));
+SA(!__builtin_is_pointer(volatile int&));
+SA(!__builtin_is_pointer(const volatile int&));
+
+SA(!__builtin_is_pointer(int&&));
+SA(!__builtin_is_pointer(const int&&));
+SA(!__builtin_is_pointer(volatile int&&));
+SA(!__builtin_is_pointer(const volatile int&&));
+
+SA(!__builtin_is_pointer(int[3]));
+SA(!__builtin_is_pointer(const int[3]));
+SA(!__builtin_is_pointer(volatile int[3]));
+SA(!__builtin_is_pointer(const volatile int[3]));
+
+SA(!__builtin_is_pointer(int(int)));
+SA(__builtin_is_pointer(int(*const)(int)));
+SA(__builtin_is_pointer(int(*volatile)(int)));
+SA(__builtin_is_pointer(int(*const volatile)(int)));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 06/26] libstdc++: Optimize std::is_pointer compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (4 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 05/26] c++: Implement __builtin_is_pointer trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 07/26] c++: Implement __builtin_is_unbounded_array trait Ken Matsui
                                     ` (20 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __builtin_is_pointer trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__builtin_is_pointer trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..a780c205fd2 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_pointer)
+  template<typename _Tp, bool _IsPtr = __builtin_is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index e9674ff87d1..812619803f4 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__builtin_is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3268,8 +3282,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __builtin_is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 07/26] c++: Implement __builtin_is_unbounded_array trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (5 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                                     ` (19 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 5a8aaa70fa6..d4cc8850486 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index a6a3c195529..7a3cbbebde6 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__builtin_is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__builtin_is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 86372ea0aba..138b180d9fb 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12832,6 +12835,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7954b42df97..7eb341a8068 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -23,6 +23,9 @@
 #if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
 # error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
 #endif
+#if !__has_builtin (__builtin_is_unbounded_array)
+# error "__has_builtin (__builtin_is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__builtin_is_volatile)
 # error "__has_builtin (__builtin_is_volatile) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..ac593b99aa7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteUnion, false);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (6 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 07/26] c++: Implement __builtin_is_unbounded_array trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 09/26] c++: Implement __builtin_add_pointer trait Ken Matsui
                                     ` (18 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__builtin_is_unbounded_array trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__builtin_is_unbounded_array trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 812619803f4..3d7d00c3bb8 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3710,11 +3710,17 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v =
+      __builtin_is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 09/26] c++: Implement __builtin_add_pointer trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (7 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
                                     ` (17 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_add_pointer.
	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.
	(object_type_p): New function.
	(referenceable_type_p): Likewise.
	(trait_expr_value): Use object_type_p.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_add_pointer.
	* g++.dg/ext/add_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 36 ++++++++++++++++++++--
 gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 7a3cbbebde6..7847ce748f6 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_POINTER, "__builtin_add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 138b180d9fb..29c1dbe2a85 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12434,6 +12434,16 @@ fold_builtin_is_corresponding_member (location_t loc, int nargs,
 				   fold_convert (TREE_TYPE (arg1), arg2)));
 }
 
+/* [basic.types] 8.  True iff TYPE is an object type.  */
+
+static bool
+object_type_p (const_tree type)
+{
+  return (TREE_CODE (type) != FUNCTION_TYPE
+          && !TYPE_REF_P (type)
+          && !VOID_TYPE_P (type));
+}
+
 /* Actually evaluates the trait.  */
 
 static bool
@@ -12576,9 +12586,7 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
       return is_nothrow_convertible (type1, type2);
 
     case CPTK_IS_OBJECT:
-      return (type_code1 != FUNCTION_TYPE
-	      && type_code1 != REFERENCE_TYPE
-	      && type_code1 != VOID_TYPE);
+      return object_type_p (type1);
 
     case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
       return pointer_interconvertible_base_of_p (type1, type2);
@@ -12733,6 +12741,18 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
 	      (non_reference (to), non_reference (from))));
 }
 
+/* [defns.referenceable] True iff TYPE is a referenceable type.  */
+
+static bool
+referenceable_type_p (const_tree type)
+{
+  return (TYPE_REF_P (type)
+	  || object_type_p (type)
+	  || (FUNC_OR_METHOD_TYPE_P (type)
+	      && (type_memfn_quals (type) == TYPE_UNQUALIFIED
+		  && type_memfn_rqual (type) == REF_QUAL_NONE)));
+}
+
 /* Process a trait expression.  */
 
 tree
@@ -12900,6 +12920,16 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_POINTER:
+      /* [meta.trans.ptr].  */
+      if (VOID_TYPE_P (type1) || referenceable_type_p (type1))
+	{
+	  if (TYPE_REF_P (type1))
+	    type1 = TREE_TYPE (type1);
+	  return build_pointer_type (type1);
+	}
+      return type1;
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
new file mode 100644
index 00000000000..1b9a318e7d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__builtin_add_pointer(int), int*));
+SA(__is_same(__builtin_add_pointer(int*), int**));
+SA(__is_same(__builtin_add_pointer(const int), const int*));
+SA(__is_same(__builtin_add_pointer(int&), int*));
+SA(__is_same(__builtin_add_pointer(ClassType*), ClassType**));
+SA(__is_same(__builtin_add_pointer(ClassType), ClassType*));
+SA(__is_same(__builtin_add_pointer(void), void*));
+SA(__is_same(__builtin_add_pointer(const void), const void*));
+SA(__is_same(__builtin_add_pointer(volatile void), volatile void*));
+SA(__is_same(__builtin_add_pointer(const volatile void), const volatile void*));
+
+void f1();
+using f1_type = decltype(f1);
+using pf1_type = decltype(&f1);
+SA(__is_same(__builtin_add_pointer(f1_type), pf1_type));
+
+void f2() noexcept; // PR libstdc++/78361
+using f2_type = decltype(f2);
+using pf2_type = decltype(&f2);
+SA(__is_same(__builtin_add_pointer(f2_type), pf2_type));
+
+using fn_type = void();
+using pfn_type = void(*)();
+SA(__is_same(__builtin_add_pointer(fn_type), pfn_type));
+
+SA(__is_same(__builtin_add_pointer(void() &), void() &));
+SA(__is_same(__builtin_add_pointer(void() & noexcept), void() & noexcept));
+SA(__is_same(__builtin_add_pointer(void() const), void() const));
+SA(__is_same(__builtin_add_pointer(void(...) &), void(...) &));
+SA(__is_same(__builtin_add_pointer(void(...) & noexcept), void(...) & noexcept));
+SA(__is_same(__builtin_add_pointer(void(...) const), void(...) const));
+
+SA(__is_same(__builtin_add_pointer(void() __restrict), void() __restrict));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7eb341a8068..509cd4294f6 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -5,6 +5,9 @@
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
+#if !__has_builtin (__builtin_add_pointer)
+# error "__has_builtin (__builtin_add_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_bit_cast)
 # error "__has_builtin (__builtin_bit_cast) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 10/26] libstdc++: Optimize std::add_pointer compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (8 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 09/26] c++: Implement __builtin_add_pointer trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 11/26] c++: Implement __builtin_remove_extent trait Ken Matsui
                                     ` (16 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::add_pointer
by dispatching to the new __builtin_add_pointer trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_pointer): Use
	__builtin_add_pointer trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 3d7d00c3bb8..59d952194af 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2149,6 +2149,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 #endif
 
+  /// add_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_add_pointer)
+  template<typename _Tp>
+    struct add_pointer
+    { using type = __builtin_add_pointer(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_pointer_helper
     { using type = _Tp; };
@@ -2157,7 +2163,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __add_pointer_helper<_Tp, __void_t<_Tp*>>
     { using type = _Tp*; };
 
-  /// add_pointer
   template<typename _Tp>
     struct add_pointer
     : public __add_pointer_helper<_Tp>
@@ -2170,6 +2175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct add_pointer<_Tp&&>
     { using type = _Tp*; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_pointer
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 11/26] c++: Implement __builtin_remove_extent trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (9 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
                                     ` (15 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::remove_extent.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_remove_extent.
	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_remove_extent.
	* g++.dg/ext/remove_extent.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  5 +++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
 4 files changed, 25 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 7847ce748f6..56b706a6127 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
+DEFTRAIT_TYPE (REMOVE_EXTENT, "__builtin_remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 29c1dbe2a85..383259fca56 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12938,6 +12938,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return cv_unqualified (type1);
 
+    case CPTK_REMOVE_EXTENT:
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	type1 = TREE_TYPE (type1);
+      return type1;
+
     case CPTK_REMOVE_POINTER:
       if (TYPE_PTR_P (type1))
 	type1 = TREE_TYPE (type1);
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 509cd4294f6..d2f91862c03 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -35,6 +35,9 @@
 #if !__has_builtin (__builtin_launder)
 # error "__has_builtin (__builtin_launder) failed"
 #endif
+#if !__has_builtin (__builtin_remove_extent)
+# error "__has_builtin (__builtin_remove_extent) failed"
+#endif
 #if !__has_builtin (__builtin_source_location)
 # error "__has_builtin (__builtin_source_location) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
new file mode 100644
index 00000000000..70c6bc3d84d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__builtin_remove_extent(int), int));
+SA(__is_same(__builtin_remove_extent(int[2]), int));
+SA(__is_same(__builtin_remove_extent(int[2][3]), int[3]));
+SA(__is_same(__builtin_remove_extent(int[][3]), int[3]));
+SA(__is_same(__builtin_remove_extent(const int[2]), const int));
+SA(__is_same(__builtin_remove_extent(ClassType), ClassType));
+SA(__is_same(__builtin_remove_extent(ClassType[2]), ClassType));
+SA(__is_same(__builtin_remove_extent(ClassType[2][3]), ClassType[3]));
+SA(__is_same(__builtin_remove_extent(ClassType[][3]), ClassType[3]));
+SA(__is_same(__builtin_remove_extent(const ClassType[2]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 12/26] libstdc++: Optimize std::remove_extent compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (10 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 11/26] c++: Implement __builtin_remove_extent trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 13/26] c++: Implement __builtin_remove_all_extents trait Ken Matsui
                                     ` (14 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::remove_extent
by dispatching to the new __builtin_remove_extent trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_extent): Use
	__builtin_remove_extent trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 59d952194af..36136440fdd 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2092,6 +2092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Array modifications.
 
   /// remove_extent
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_remove_extent)
+  template<typename _Tp>
+    struct remove_extent
+    { using type = __builtin_remove_extent(_Tp); };
+#else
   template<typename _Tp>
     struct remove_extent
     { using type = _Tp; };
@@ -2103,6 +2108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_extent<_Tp[]>
     { using type = _Tp; };
+#endif
 
   /// remove_all_extents
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 13/26] c++: Implement __builtin_remove_all_extents trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (11 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
                                     ` (13 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::remove_all_extents.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_remove_all_extents.
	* semantics.cc (finish_trait_type): Handle
	CPTK_REMOVE_ALL_EXTENTS.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_remove_all_extents.
	* g++.dg/ext/remove_all_extents.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  3 +++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 56b706a6127..4df0c63032e 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__builtin_is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__builtin_remove_all_extents", 1)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__builtin_remove_extent", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 383259fca56..07779a79d24 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12930,6 +12930,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	}
       return type1;
 
+    case CPTK_REMOVE_ALL_EXTENTS:
+      return strip_array_types (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index d2f91862c03..7ca80daf6d1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -35,6 +35,9 @@
 #if !__has_builtin (__builtin_launder)
 # error "__has_builtin (__builtin_launder) failed"
 #endif
+#if !__has_builtin (__builtin_remove_all_extents)
+# error "__has_builtin (__builtin_remove_all_extents) failed"
+#endif
 #if !__has_builtin (__builtin_remove_extent)
 # error "__has_builtin (__builtin_remove_extent) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
new file mode 100644
index 00000000000..029a029e6d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__builtin_remove_all_extents(int), int));
+SA(__is_same(__builtin_remove_all_extents(int[2]), int));
+SA(__is_same(__builtin_remove_all_extents(int[2][3]), int));
+SA(__is_same(__builtin_remove_all_extents(int[][3]), int));
+SA(__is_same(__builtin_remove_all_extents(const int[2][3]), const int));
+SA(__is_same(__builtin_remove_all_extents(ClassType), ClassType));
+SA(__is_same(__builtin_remove_all_extents(ClassType[2]), ClassType));
+SA(__is_same(__builtin_remove_all_extents(ClassType[2][3]), ClassType));
+SA(__is_same(__builtin_remove_all_extents(ClassType[][3]), ClassType));
+SA(__is_same(__builtin_remove_all_extents(const ClassType[2][3]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 14/26] libstdc++: Optimize std::remove_all_extents compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (12 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 13/26] c++: Implement __builtin_remove_all_extents trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 15/26] c++: Implement __builtin_add_lvalue_reference trait Ken Matsui
                                     ` (12 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::remove_all_extents by dispatching to the new
__builtin_remove_all_extents trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_all_extents): Use
	__builtin_remove_all_extents trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 36136440fdd..8f756712c63 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2111,6 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// remove_all_extents
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_remove_all_extents)
+  template<typename _Tp>
+    struct remove_all_extents
+    { using type = __builtin_remove_all_extents(_Tp); };
+#else
   template<typename _Tp>
     struct remove_all_extents
     { using type = _Tp; };
@@ -2122,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_all_extents<_Tp[]>
     { using type = typename remove_all_extents<_Tp>::type; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_extent
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 15/26] c++: Implement __builtin_add_lvalue_reference trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (13 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
                                     ` (11 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_lvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_add_lvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_LVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_add_lvalue_reference.
	* g++.dg/ext/add_lvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  6 ++++++
 .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 4df0c63032e..228df46e3e4 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__builtin_add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__builtin_add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 07779a79d24..9f2220a1a91 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12920,6 +12920,12 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_LVALUE_REFERENCE:
+      /* [meta.trans.ref].  */
+      if (referenceable_type_p (type1))
+	return cp_build_reference_type (type1, /*rval=*/false);
+      return type1;
+
     case CPTK_ADD_POINTER:
       /* [meta.trans.ptr].  */
       if (VOID_TYPE_P (type1) || referenceable_type_p (type1))
diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
new file mode 100644
index 00000000000..56afa5c342d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__builtin_add_lvalue_reference(int), int&));
+SA(__is_same(__builtin_add_lvalue_reference(int&), int&));
+SA(__is_same(__builtin_add_lvalue_reference(const int), const int&));
+SA(__is_same(__builtin_add_lvalue_reference(int*), int*&));
+SA(__is_same(__builtin_add_lvalue_reference(ClassType&), ClassType&));
+SA(__is_same(__builtin_add_lvalue_reference(ClassType), ClassType&));
+SA(__is_same(__builtin_add_lvalue_reference(int(int)), int(&)(int)));
+SA(__is_same(__builtin_add_lvalue_reference(int&&), int&));
+SA(__is_same(__builtin_add_lvalue_reference(ClassType&&), ClassType&));
+SA(__is_same(__builtin_add_lvalue_reference(void), void));
+SA(__is_same(__builtin_add_lvalue_reference(const void), const void));
+SA(__is_same(__builtin_add_lvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__builtin_add_lvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__builtin_add_lvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__builtin_add_lvalue_reference(bool(int)), bool(&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 7ca80daf6d1..77c65f9c920 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -5,6 +5,9 @@
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
+#if !__has_builtin (__builtin_add_lvalue_reference)
+# error "__has_builtin (__builtin_add_lvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_add_pointer)
 # error "__has_builtin (__builtin_add_pointer) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (14 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 15/26] c++: Implement __builtin_add_lvalue_reference trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 17/26] c++: Implement __builtin_add_rvalue_reference trait Ken Matsui
                                     ` (10 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::add_lvalue_reference by dispatching to the new
__builtin_add_lvalue_reference trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_lvalue_reference): Use
	__builtin_add_lvalue_reference trait.
	(__add_lvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 8f756712c63..4e97e45f130 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1157,6 +1157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_add_lvalue_reference)
+  template<typename _Tp>
+    struct __add_lvalue_reference_helper
+    { using type = __builtin_add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_lvalue_reference_helper
     { using type = _Tp; };
@@ -1164,6 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_lvalue_reference_helper<_Tp, __void_t<_Tp&>>
     { using type = _Tp&; };
+#endif
 
   template<typename _Tp>
     using __add_lval_ref_t = typename __add_lvalue_reference_helper<_Tp>::type;
@@ -1731,9 +1737,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_lvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_add_lvalue_reference)
+  template<typename _Tp>
+    struct add_lvalue_reference
+    { using type = __builtin_add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_lvalue_reference
     { using type = __add_lval_ref_t<_Tp>; };
+#endif
 
   /// add_rvalue_reference
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 17/26] c++: Implement __builtin_add_rvalue_reference trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (15 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
                                     ` (9 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_rvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_add_rvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_RVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_add_rvalue_reference.
	* g++.dg/ext/add_rvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  6 ++++++
 .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 30 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 228df46e3e4..c6fb2aa5f1e 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -50,6 +50,7 @@
 
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__builtin_add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__builtin_add_pointer", 1)
+DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__builtin_add_rvalue_reference", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9f2220a1a91..fd6d9fc1dc5 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12936,6 +12936,12 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	}
       return type1;
 
+    case CPTK_ADD_RVALUE_REFERENCE:
+      /* [meta.trans.ref].  */
+      if (referenceable_type_p (type1))
+	return cp_build_reference_type (type1, /*rval=*/true);
+      return type1;
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
new file mode 100644
index 00000000000..3c09c5b05d6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__builtin_add_rvalue_reference(int), int&&));
+SA(__is_same(__builtin_add_rvalue_reference(int&&), int&&));
+SA(__is_same(__builtin_add_rvalue_reference(int&), int&));
+SA(__is_same(__builtin_add_rvalue_reference(const int), const int&&));
+SA(__is_same(__builtin_add_rvalue_reference(int*), int*&&));
+SA(__is_same(__builtin_add_rvalue_reference(ClassType&&), ClassType&&));
+SA(__is_same(__builtin_add_rvalue_reference(ClassType), ClassType&&));
+SA(__is_same(__builtin_add_rvalue_reference(int(int)), int(&&)(int)));
+SA(__is_same(__builtin_add_rvalue_reference(void), void));
+SA(__is_same(__builtin_add_rvalue_reference(const void), const void));
+SA(__is_same(__builtin_add_rvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__builtin_add_rvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__builtin_add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__builtin_add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 77c65f9c920..fc66c816887 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -11,6 +11,9 @@
 #if !__has_builtin (__builtin_add_pointer)
 # error "__has_builtin (__builtin_add_pointer) failed"
 #endif
+#if !__has_builtin (__builtin_add_rvalue_reference)
+# error "__has_builtin (__builtin_add_rvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_bit_cast)
 # error "__has_builtin (__builtin_bit_cast) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (16 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 17/26] c++: Implement __builtin_add_rvalue_reference trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 19/26] c++: Implement __builtin_decay trait Ken Matsui
                                     ` (8 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::add_rvalue_reference by dispatching to the new
__builtin_add_rvalue_reference trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_rvalue_reference): Use
	__builtin_add_rvalue_reference trait.
	(__add_rvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 4e97e45f130..36ac900605a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1185,6 +1185,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_add_rvalue_reference)
+  template<typename _Tp>
+    struct __add_rvalue_reference_helper
+    { using type = __builtin_add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_rvalue_reference_helper
     { using type = _Tp; };
@@ -1192,6 +1197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_rvalue_reference_helper<_Tp, __void_t<_Tp&&>>
     { using type = _Tp&&; };
+#endif
 
   template<typename _Tp>
     using __add_rval_ref_t = typename __add_rvalue_reference_helper<_Tp>::type;
@@ -1748,9 +1754,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_rvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_add_rvalue_reference)
+  template<typename _Tp>
+    struct add_rvalue_reference
+    { using type = __builtin_add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_rvalue_reference
     { using type = __add_rval_ref_t<_Tp>; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_reference
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 19/26] c++: Implement __builtin_decay trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (17 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
                                     ` (7 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::decay.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_decay.
	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __builtin_decay.
	* g++.dg/ext/decay.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 12 ++++++++++++
 gcc/testsuite/g++.dg/ext/decay.C         | 22 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 4 files changed, 38 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index c6fb2aa5f1e..90cd5ff99cc 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -51,6 +51,7 @@
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__builtin_add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__builtin_add_pointer", 1)
 DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__builtin_add_rvalue_reference", 1)
+DEFTRAIT_TYPE (DECAY, "__builtin_decay", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fd6d9fc1dc5..c498191a3c8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12942,6 +12942,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	return cp_build_reference_type (type1, /*rval=*/true);
       return type1;
 
+    case CPTK_DECAY:
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
+				  complain);
+      else if (TREE_CODE (type1) == FUNCTION_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
+      else
+	return cv_unqualified (type1);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
new file mode 100644
index 00000000000..f8b161a97c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/decay.C
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+// Positive tests.
+using test1_type = __builtin_decay(bool);
+SA(__is_same(test1_type, bool));
+
+// NB: DR 705.
+using test2_type = __builtin_decay(const int);
+SA(__is_same(test2_type, int));
+
+using test3_type = __builtin_decay(int[4]);
+SA(__is_same(test3_type, __builtin_remove_extent(int[4])*));
+
+using fn_type = void ();
+using test4_type = __builtin_decay(fn_type);
+SA(__is_same(test4_type, __builtin_add_pointer(fn_type)));
+
+using cfn_type = void () const;
+using test5_type = __builtin_decay(cfn_type);
+SA(__is_same(test5_type, cfn_type));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index fc66c816887..0ea96d0ea87 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -17,6 +17,9 @@
 #if !__has_builtin (__builtin_bit_cast)
 # error "__has_builtin (__builtin_bit_cast) failed"
 #endif
+#if !__has_builtin (__builtin_decay)
+# error "__has_builtin (__builtin_decay) failed"
+#endif
 #if !__has_builtin (__builtin_is_const)
 # error "__has_builtin (__builtin_is_const) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 20/26] libstdc++: Optimize std::decay compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (18 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 19/26] c++: Implement __builtin_decay trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 21/26] c++: Implement __builtin_rank trait Ken Matsui
                                     ` (6 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::decay
by dispatching to the new __builtin_decay trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (decay): Use __builtin_decay trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 36ac900605a..45c73d477d0 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2316,6 +2316,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @cond undocumented
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_decay)
+  template<typename _Tp>
+    struct decay
+    { using type = __builtin_decay(_Tp); };
+#else
   // Decay trait for arrays and functions, used for perfect forwarding
   // in make_pair, make_tuple, etc.
   template<typename _Up>
@@ -2347,6 +2352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct decay<_Tp&&>
     { using type = typename __decay_selector<_Tp>::type; };
+#endif
 
   /// @cond undocumented
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 21/26] c++: Implement __builtin_rank trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (19 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
                                     ` (5 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::rank.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_rank.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __builtin_rank.
	* g++.dg/ext/rank.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 24 +++++++++++++++++++++---
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
 5 files changed, 52 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d4cc8850486..c28d7bf428e 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3871,6 +3871,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
+    case CPTK_RANK:
+      inform (loc, "  %qT cannot yield a rank", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 90cd5ff99cc..bf21ea4f4c6 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__builtin_is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__builtin_is_volatile", 1)
+DEFTRAIT_EXPR (RANK, "__builtin_rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__builtin_remove_all_extents", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c498191a3c8..9be3eb1e204 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12642,6 +12642,10 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
+    /* __builtin_rank is handled in finish_trait_expr. */
+    case CPTK_RANK:
+      gcc_unreachable ();
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12765,7 +12769,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
   if (processing_template_decl)
     {
       tree trait_expr = make_node (TRAIT_EXPR);
-      TREE_TYPE (trait_expr) = boolean_type_node;
+      if (kind == CPTK_RANK)
+	TREE_TYPE (trait_expr) = size_type_node;
+      else
+	TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
       TRAIT_EXPR_TYPE2 (trait_expr) = type2;
       TRAIT_EXPR_KIND (trait_expr) = kind;
@@ -12858,6 +12865,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
+    case CPTK_RANK:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -12889,8 +12897,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  tree val = (trait_expr_value (kind, type1, type2)
-	      ? boolean_true_node : boolean_false_node);
+  tree val;
+  if (kind == CPTK_RANK)
+    {
+      size_t rank = 0;
+      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+	++rank;
+      val = build_int_cst (size_type_node, rank);
+    }
+  else
+    val = (trait_expr_value (kind, type1, type2)
+	   ? boolean_true_node : boolean_false_node);
+
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 0ea96d0ea87..fdd4c143978 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -44,6 +44,9 @@
 #if !__has_builtin (__builtin_launder)
 # error "__has_builtin (__builtin_launder) failed"
 #endif
+#if !__has_builtin (__builtin_rank)
+# error "__has_builtin (__builtin_rank) failed"
+#endif
 #if !__has_builtin (__builtin_remove_all_extents)
 # error "__has_builtin (__builtin_remove_all_extents) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
new file mode 100644
index 00000000000..7d095349a8b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/rank.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <cstddef>
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__builtin_rank(int) == 0);
+SA(__builtin_rank(int[2]) == 1);
+SA(__builtin_rank(int[][4]) == 2);
+SA(__builtin_rank(int[2][2][4][4][6][6]) == 6);
+SA(__builtin_rank(ClassType) == 0);
+SA(__builtin_rank(ClassType[2]) == 1);
+SA(__builtin_rank(ClassType[][4]) == 2);
+SA(__builtin_rank(ClassType[2][2][4][4][6][6]) == 6);
+
+template<class T> void f(T) = delete;
+void f(size_t);
+
+template<class T>
+void g() { f(__builtin_rank(T)); }
+
+template void g<int>();
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 22/26] libstdc++: Optimize std::rank compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (20 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 21/26] c++: Implement __builtin_rank trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 23/26] c++: Implement __builtin_is_invocable trait Ken Matsui
                                     ` (4 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::rank
by dispatching to the new __builtin_rank trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (rank): Use __builtin_rank trait.
	(rank_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 45c73d477d0..15933a0730e 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1473,6 +1473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// rank
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_rank)
+  template<typename _Tp>
+    struct rank
+    : public integral_constant<std::size_t, __builtin_rank(_Tp)> { };
+#else
   template<typename>
     struct rank
     : public integral_constant<std::size_t, 0> { };
@@ -1484,6 +1489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct rank<_Tp[]>
     : public integral_constant<std::size_t, 1 + rank<_Tp>::value> { };
+#endif
 
   /// extent
   template<typename, unsigned _Uint = 0>
@@ -3583,12 +3589,17 @@ template <typename _Tp>
 template <typename _Tp>
   inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_rank)
+template <typename _Tp>
+  inline constexpr size_t rank_v = __builtin_rank(_Tp);
+#else
 template <typename _Tp>
   inline constexpr size_t rank_v = 0;
 template <typename _Tp, size_t _Size>
   inline constexpr size_t rank_v<_Tp[_Size]> = 1 + rank_v<_Tp>;
 template <typename _Tp>
   inline constexpr size_t rank_v<_Tp[]> = 1 + rank_v<_Tp>;
+#endif
 
 template <typename _Tp, unsigned _Idx = 0>
   inline constexpr size_t extent_v = 0;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 23/26] c++: Implement __builtin_is_invocable trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (21 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
                                     ` (3 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 134 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 723 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index bf21ea4f4c6..5ae256c56ab 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__builtin_is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1938ada0268..83dc20e1130 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..faf932258e6 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,140 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be"
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_base_of_datum =
+	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	 && NON_UNION_CLASS_TYPE_P (datum_type)
+	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							datum_type)
+	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+      tree datum_expr = build_trait_object (datum_type);
+      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9be3eb1e204..4a13a36a81e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index fdd4c143978..f4ac659e9b8 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -29,6 +29,9 @@
 #if !__has_builtin (__builtin_is_corresponding_member)
 # error "__has_builtin (__builtin_is_corresponding_member) failed"
 #endif
+#if !__has_builtin (__builtin_is_invocable)
+# error "__has_builtin (__builtin_is_invocable) failed"
+#endif
 #if !__has_builtin (__builtin_is_pointer)
 # error "__has_builtin (__builtin_is_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..7f12d5fe8d2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __builtin_is_invocable( func_type_v0 ) );
+SA( ! __builtin_is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __builtin_is_invocable( func_type_i0 ) );
+SA( ! __builtin_is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __builtin_is_invocable( func_type_l0 ) );
+SA( ! __builtin_is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __builtin_is_invocable( func_type_ii ) );
+SA(   __builtin_is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __builtin_is_invocable( func_type_il ) );
+SA( ! __builtin_is_invocable( func_type_il, int ) );
+SA(   __builtin_is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __builtin_is_invocable( func_type_ir ) );
+SA( ! __builtin_is_invocable( func_type_ir, int& ) );
+SA(   __builtin_is_invocable( func_type_ir, int ) );
+SA(   __builtin_is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __builtin_is_invocable( mem_type_i ) );
+SA( ! __builtin_is_invocable( mem_type_i, int ) );
+SA( ! __builtin_is_invocable( mem_type_i, int* ) );
+SA( ! __builtin_is_invocable( mem_type_i, int& ) );
+SA( ! __builtin_is_invocable( mem_type_i, int&& ) );
+SA(   __builtin_is_invocable( mem_type_i, A ) );
+SA(   __builtin_is_invocable( mem_type_i, A* ) );
+SA(   __builtin_is_invocable( mem_type_i, A& ) );
+SA(   __builtin_is_invocable( mem_type_i, A&& ) );
+SA(   __builtin_is_invocable( mem_type_i, const A& ) );
+SA( ! __builtin_is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __builtin_is_invocable( memfun_type_i ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int* ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int&& ) );
+SA(   __builtin_is_invocable( memfun_type_i, A ) );
+SA(   __builtin_is_invocable( memfun_type_i, A* ) );
+SA(   __builtin_is_invocable( memfun_type_i, A& ) );
+SA(   __builtin_is_invocable( memfun_type_i, A&& ) );
+SA( ! __builtin_is_invocable( memfun_type_i, const A& ) );
+SA( ! __builtin_is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __builtin_is_invocable( memfun_type_ic ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, int ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, int& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, A& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, A* ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, const A& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, const A* ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __builtin_is_invocable( memfun_type_iic ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, int ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, A&, int ) );
+SA(   __builtin_is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, A*, int ) );
+SA(   __builtin_is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __builtin_is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __builtin_is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __builtin_is_invocable( B ) );
+SA(   __builtin_is_invocable( B& ) );
+SA(   __builtin_is_invocable( B&& ) );
+SA( ! __builtin_is_invocable( B* ) );
+SA(   __builtin_is_invocable( CB ) );
+SA(   __builtin_is_invocable( CB& ) );
+SA( ! __builtin_is_invocable( CB* ) );
+
+SA(   __builtin_is_invocable( B, int ) );
+SA(   __builtin_is_invocable( B&, int ) );
+SA(   __builtin_is_invocable( B&&, int ) );
+SA( ! __builtin_is_invocable( B*, int ) );
+SA( ! __builtin_is_invocable( CB, int ) );
+SA( ! __builtin_is_invocable( CB&, int ) );
+SA( ! __builtin_is_invocable( CB*, int ) );
+
+SA( ! __builtin_is_invocable( B, int, int ) );
+SA( ! __builtin_is_invocable( B&, int, int ) );
+SA( ! __builtin_is_invocable( B&&, int, int ) );
+SA( ! __builtin_is_invocable( B*, int, int ) );
+SA( ! __builtin_is_invocable( CB, int, int ) );
+SA( ! __builtin_is_invocable( CB&, int, int ) );
+SA( ! __builtin_is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __builtin_is_invocable( C ) );
+SA( ! __builtin_is_invocable( C& ) );
+SA( ! __builtin_is_invocable( C&& ) );
+SA( ! __builtin_is_invocable( C* ) );
+SA( ! __builtin_is_invocable( CC ) );
+SA( ! __builtin_is_invocable( CC& ) );
+SA( ! __builtin_is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __builtin_is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __builtin_is_invocable( E ) );
+SA( ! __builtin_is_invocable( void (E::*)() ) );
+SA(   __builtin_is_invocable( void (E::*)(), E ) );
+SA(   __builtin_is_invocable( void (E::*)(), E* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __builtin_is_invocable( F ) );
+SA(   __builtin_is_invocable( void (E::*)(), F ) );
+SA(   __builtin_is_invocable( void (E::*)(), F* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __builtin_is_invocable( G ) );
+SA(   __builtin_is_invocable( void (E::*)(), G ) );
+SA( ! __builtin_is_invocable( void (E::*)(), G* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __builtin_is_invocable( H ) );
+SA(   __builtin_is_invocable( void (E::*)(), H ) );
+SA( ! __builtin_is_invocable( void (E::*)(), H* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __builtin_is_invocable( I ) );
+SA(   __builtin_is_invocable( void (E::*)(), I ) );
+SA( ! __builtin_is_invocable( void (E::*)(), I* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __builtin_is_invocable( K ) );
+SA( ! __builtin_is_invocable( void (E::*)(), K ) );
+SA( ! __builtin_is_invocable( void (E::*)(), K* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __builtin_is_invocable( L ) );
+SA( ! __builtin_is_invocable( void (E::*)(), L ) );
+SA( ! __builtin_is_invocable( void (E::*)(), L* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __builtin_is_invocable( M ) );
+SA( ! __builtin_is_invocable( M& ) );
+SA( ! __builtin_is_invocable( M&& ) );
+SA( ! __builtin_is_invocable( M* ) );
+SA( ! __builtin_is_invocable( CM ) );
+SA( ! __builtin_is_invocable( CM& ) );
+SA( ! __builtin_is_invocable( CM* ) );
+
+SA( ! __builtin_is_invocable( int M::* ) );
+SA(   __builtin_is_invocable( int M::*, M ) );
+SA(   __builtin_is_invocable( int M::*, M& ) );
+SA(   __builtin_is_invocable( int M::*, M&& ) );
+SA(   __builtin_is_invocable( int M::*, M* ) );
+SA(   __builtin_is_invocable( int M::*, CM ) );
+SA(   __builtin_is_invocable( int M::*, CM& ) );
+SA(   __builtin_is_invocable( int M::*, CM* ) );
+SA( ! __builtin_is_invocable( int M::*, int ) );
+
+SA( ! __builtin_is_invocable( int CM::* ) );
+SA(   __builtin_is_invocable( int CM::*, M ) );
+SA(   __builtin_is_invocable( int CM::*, M& ) );
+SA(   __builtin_is_invocable( int CM::*, M&& ) );
+SA(   __builtin_is_invocable( int CM::*, M* ) );
+SA(   __builtin_is_invocable( int CM::*, CM ) );
+SA(   __builtin_is_invocable( int CM::*, CM& ) );
+SA(   __builtin_is_invocable( int CM::*, CM* ) );
+SA( ! __builtin_is_invocable( int CM::*, int ) );
+
+SA( ! __builtin_is_invocable( long M::* ) );
+SA(   __builtin_is_invocable( long M::*, M ) );
+SA(   __builtin_is_invocable( long M::*, M& ) );
+SA(   __builtin_is_invocable( long M::*, M&& ) );
+SA(   __builtin_is_invocable( long M::*, M* ) );
+SA(   __builtin_is_invocable( long M::*, CM ) );
+SA(   __builtin_is_invocable( long M::*, CM& ) );
+SA(   __builtin_is_invocable( long M::*, CM* ) );
+SA( ! __builtin_is_invocable( long M::*, long ) );
+
+SA( ! __builtin_is_invocable( long CM::* ) );
+SA(   __builtin_is_invocable( long CM::*, M ) );
+SA(   __builtin_is_invocable( long CM::*, M& ) );
+SA(   __builtin_is_invocable( long CM::*, M&& ) );
+SA(   __builtin_is_invocable( long CM::*, M* ) );
+SA(   __builtin_is_invocable( long CM::*, CM ) );
+SA(   __builtin_is_invocable( long CM::*, CM& ) );
+SA(   __builtin_is_invocable( long CM::*, CM* ) );
+SA( ! __builtin_is_invocable( long CM::*, long ) );
+
+SA( ! __builtin_is_invocable( short M::* ) );
+SA(   __builtin_is_invocable( short M::*, M ) );
+SA(   __builtin_is_invocable( short M::*, M& ) );
+SA(   __builtin_is_invocable( short M::*, M&& ) );
+SA(   __builtin_is_invocable( short M::*, M* ) );
+SA(   __builtin_is_invocable( short M::*, CM ) );
+SA(   __builtin_is_invocable( short M::*, CM& ) );
+SA(   __builtin_is_invocable( short M::*, CM* ) );
+SA( ! __builtin_is_invocable( short M::*, short ) );
+
+SA( ! __builtin_is_invocable( short CM::* ) );
+SA(   __builtin_is_invocable( short CM::*, M ) );
+SA(   __builtin_is_invocable( short CM::*, M& ) );
+SA(   __builtin_is_invocable( short CM::*, M&& ) );
+SA(   __builtin_is_invocable( short CM::*, M* ) );
+SA(   __builtin_is_invocable( short CM::*, CM ) );
+SA(   __builtin_is_invocable( short CM::*, CM& ) );
+SA(   __builtin_is_invocable( short CM::*, CM* ) );
+SA( ! __builtin_is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __builtin_is_invocable( int M::*, N ) );
+SA( ! __builtin_is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __builtin_is_invocable( int M::*, O ) );
+SA( ! __builtin_is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __builtin_is_invocable( int M::*, P ) );
+SA( ! __builtin_is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __builtin_is_invocable( int M::*, Q ) );
+SA( ! __builtin_is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __builtin_is_invocable( R ) );
+SA(   __builtin_is_invocable( R, int ) );
+SA( ! __builtin_is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __builtin_is_invocable( S ) );
+SA(   __builtin_is_invocable( S, int ) );
+SA(   __builtin_is_invocable( S, int, int ) );
+SA(   __builtin_is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __builtin_is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __builtin_is_invocable( decltype(fn2), int[10] ) );
+SA(   __builtin_is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __builtin_is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __builtin_is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __builtin_is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __builtin_is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __builtin_is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __builtin_is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __builtin_is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __builtin_is_invocable( decltype(&T::func)&, T& ) );
+SA(   __builtin_is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __builtin_is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __builtin_is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __builtin_is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..bf4d2a58d9f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __builtin_is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __builtin_is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __builtin_is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __builtin_is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __builtin_is_invocable( reference_wrapper<B> ) );
+SA(   __builtin_is_invocable( reference_wrapper<B>& ) );
+SA(   __builtin_is_invocable( reference_wrapper<B>&& ) );
+SA(   __builtin_is_invocable( reference_wrapper<CB> ) );
+SA(   __builtin_is_invocable( reference_wrapper<CB>& ) );
+SA(   __builtin_is_invocable( reference_wrapper<B>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __builtin_is_invocable( reference_wrapper<C> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<C>& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<C>&& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<CC> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __builtin_is_invocable( reference_wrapper<D> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D>& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D>&& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D>* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D*> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __builtin_is_invocable( decltype(fn) ) );
+SA(   __builtin_is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..aa6d4d7183a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __builtin_is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __builtin_is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __builtin_is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __builtin_is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __builtin_is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __builtin_is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..6c9c6212439
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__builtin_is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __builtin_is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 24/26] libstdc++: Optimize std::is_invocable compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (22 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 23/26] c++: Implement __builtin_is_invocable trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 25/26] c++: Implement __builtin_is_nothrow_invocable trait Ken Matsui
                                     ` (2 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __builtin_is_invocable trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use
	__builtin_is_invocable trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __builtin_is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc     | 1 +
 libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 15933a0730e..1f2d3240faf 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3239,7 +3239,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_invocable)
+    : public __bool_constant<__builtin_is_invocable(_Fn, _ArgTypes...)>
+#else
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 25/26] c++: Implement __builtin_is_nothrow_invocable trait
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (23 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 16:13                   ` [PATCH v16 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_nothrow_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_nothrow_invocable.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_NOTHROW_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_nothrow_invocable.
	* g++.dg/ext/is_nothrow_invocable.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  6 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  5 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +
 .../g++.dg/ext/is_nothrow_invocable.C         | 62 +++++++++++++++++++
 5 files changed, 77 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 6d14ef7dcc7..8679d3ce658 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3825,6 +3825,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
 	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
       break;
+    case CPTK_IS_NOTHROW_INVOCABLE:
+	if (!t2)
+	  inform (loc, "  %qT is not nothrow invocable", t1);
+	else
+	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
+	break;
     case CPTK_IS_OBJECT:
       inform (loc, "  %qT is not an object type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 5ae256c56ab..fcdf009890e 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
 DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
 DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
 DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__builtin_is_nothrow_invocable", -1)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 4a13a36a81e..8b600daf5fd 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12588,6 +12588,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
       return is_nothrow_convertible (type1, type2);
 
+    case CPTK_IS_NOTHROW_INVOCABLE:
+      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
+
     case CPTK_IS_OBJECT:
       return object_type_p (type1);
 
@@ -12723,6 +12726,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_IS_INVOCABLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12828,6 +12832,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index f4ac659e9b8..d806fa7ae60 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -32,6 +32,9 @@
 #if !__has_builtin (__builtin_is_invocable)
 # error "__has_builtin (__builtin_is_invocable) failed"
 #endif
+#if !__has_builtin (__builtin_is_nothrow_invocable)
+# error "__has_builtin (__builtin_is_nothrow_invocable) failed"
+#endif
 #if !__has_builtin (__builtin_is_pointer)
 # error "__has_builtin (__builtin_is_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
new file mode 100644
index 00000000000..bb84f48d754
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
@@ -0,0 +1,62 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type = void(*)();
+SA( ! __builtin_is_nothrow_invocable(func_type) );
+
+#if __cpp_noexcept_function_type
+using func_type_nt = void(*)() noexcept;
+SA(   __builtin_is_nothrow_invocable(func_type_nt) );
+#endif
+
+struct X { };
+using mem_type = int X::*;
+
+SA( ! __builtin_is_nothrow_invocable(mem_type) );
+SA( ! __builtin_is_nothrow_invocable(mem_type, int) );
+SA( ! __builtin_is_nothrow_invocable(mem_type, int&) );
+SA(   __builtin_is_nothrow_invocable(mem_type, X&) );
+
+using memfun_type = int (X::*)();
+
+SA( ! __builtin_is_nothrow_invocable(memfun_type) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, int) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, int&) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, X&) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, X*) );
+
+#if __cpp_noexcept_function_type
+using memfun_type_nt = int (X::*)() noexcept;
+
+SA( ! __builtin_is_nothrow_invocable(memfun_type_nt) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type_nt, int) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type_nt, int&) );
+SA(   __builtin_is_nothrow_invocable(memfun_type_nt, X&) );
+SA(   __builtin_is_nothrow_invocable(memfun_type_nt, X*) );
+#endif
+
+struct F {
+  int& operator()();
+  long& operator()() const noexcept;
+  short& operator()(int) &&;
+  char& operator()(int) const& noexcept;
+private:
+  void operator()(int, int) noexcept;
+};
+using CF = const F;
+
+SA( ! __builtin_is_nothrow_invocable(F ) );
+SA(   __builtin_is_nothrow_invocable(CF) );
+
+SA( ! __builtin_is_nothrow_invocable(F,   int) );
+SA(   __builtin_is_nothrow_invocable(F&,  int) );
+
+SA(   __builtin_is_nothrow_invocable(CF,   int) );
+SA(   __builtin_is_nothrow_invocable(CF&,  int) );
+SA( ! __builtin_is_nothrow_invocable(F, int, int) );
+
+struct FX {
+  X operator()() const noexcept { return {}; }
+};
+SA(   __builtin_is_nothrow_invocable(FX) );
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v16 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (24 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 25/26] c++: Implement __builtin_is_nothrow_invocable trait Ken Matsui
@ 2024-05-02 16:13                   ` Ken Matsui
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::is_nothrow_invocable by dispatching to the new
__builtin_is_nothrow_invocable trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_nothrow_invocable): Use
	__builtin_is_nothrow_invocable trait.
	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
	Handle the new error from __builtin_is_nothrow_invocable.
	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
	Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
 .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1f2d3240faf..2a34c9bcc21 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3269,8 +3269,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_nothrow_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_nothrow_invocable)
+    : public __bool_constant<__builtin_is_nothrow_invocable(_Fn, _ArgTypes...)>
+#else
     : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
 	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
index 3c225883eaf..3f8542dd366 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
index 5a728bfa03b..d3bdf08448b 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-05-02 15:34                       ` Ken Matsui
@ 2024-05-02 16:20                         ` Ken Matsui
  2024-05-02 16:45                           ` Jason Merrill
  0 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:20 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Jason Merrill, Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 8:34 AM Ken Matsui <kmatsui@cs.washington.edu> wrote:
>
> On Thu, May 2, 2024 at 8:16 AM Patrick Palka <ppalka@redhat.com> wrote:
> >
> > On Tue, 30 Apr 2024, Jason Merrill wrote:
> >
> > > On 2/28/24 11:26, Ken Matsui wrote:
> > > > This patch implements built-in trait for std::rank.
> > >
> > > __rank seems too short, maybe __array_rank?
> > >
> > > Actually, it occurs to me that perhaps we should have been adding __builtin to
> > > all of these rather than just __ and the library trait name.  I guess it's too
> > > late to do that for the GCC 14 traits, but we could do it for this group?
> >
> > Clang already implements many of these built-in, without using
> > "__builtin" in their name.  Shouldn't we be consistent with Clang where
> > we can?

Since I had already replaced the prefix locally with __builtin, I
submitted patches addressing all other Jason's reviews with that.
Please let me know if we want to use __ and __array_rank.

>
> Oh, then, would using __array_rank and keeping existing built-in
> traits as-is sound like a better choice?
>
> https://github.com/llvm/llvm-project/blob/4aca302f5a82ee65847c88500b39a2530dfeceb4/libcxx/include/__type_traits/rank.h#L23
>
> >
> > >
> > > > gcc/cp/ChangeLog:
> > > >
> > > >     * cp-trait.def: Define __rank.
> > > >     * constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
> > > >     * semantics.cc (trait_expr_value): Likewise.
> > > >     (finish_trait_expr): Likewise.
> > > >
> > > > gcc/testsuite/ChangeLog:
> > > >
> > > >     * g++.dg/ext/has-builtin-1.C: Test existence of __rank.
> > > >     * g++.dg/ext/rank.C: New test.
> > > >
> > > > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > > > ---
> > > >   gcc/cp/constraint.cc                     |  3 +++
> > > >   gcc/cp/cp-trait.def                      |  1 +
> > > >   gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
> > > >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
> > > >   gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
> > > >   5 files changed, 51 insertions(+), 3 deletions(-)
> > > >   create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
> > > >
> > > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > > > index 000df847342..23ea66d9c12 100644
> > > > --- a/gcc/cp/constraint.cc
> > > > +++ b/gcc/cp/constraint.cc
> > > > @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
> > > >       case CPTK_IS_VOLATILE:
> > > >         inform (loc, "  %qT is not a volatile type", t1);
> > > >         break;
> > > > +    case CPTK_RANK:
> > > > +      inform (loc, "  %qT cannot yield a rank", t1);
> > > > +      break;
> > > >       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> > > >         inform (loc, "  %qT is not a reference that binds to a temporary "
> > > >           "object of type %qT (direct-initialization)", t1, t2);
> > > > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > > > index 2d1cb7c227c..85056c8140b 100644
> > > > --- a/gcc/cp/cp-trait.def
> > > > +++ b/gcc/cp/cp-trait.def
> > > > @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE,
> > > > "__is_trivially_copyable", 1)
> > > >   DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
> > > >   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> > > >   DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
> > > > +DEFTRAIT_EXPR (RANK, "__rank", 1)
> > > >   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY,
> > > > "__reference_constructs_from_temporary", 2)
> > > >   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY,
> > > > "__reference_converts_from_temporary", 2)
> > > >   DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
> > > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > > > index 45dc509855a..7242db75248 100644
> > > > --- a/gcc/cp/semantics.cc
> > > > +++ b/gcc/cp/semantics.cc
> > > > @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1,
> > > > tree type2)
> > > >       case CPTK_IS_DEDUCIBLE:
> > > >         return type_targs_deducible_from (type1, type2);
> > > >   +    /* __rank is handled in finish_trait_expr. */
> > > > +    case CPTK_RANK:
> > >
> > > This should have a gcc_unreachable.
> > >
> > > > +
> > > >   #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
> > > >       case CPTK_##CODE:
> > > >   #include "cp-trait.def"
> > > > @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > > > kind, tree type1, tree type2)
> > > >     if (processing_template_decl)
> > > >       {
> > > >         tree trait_expr = make_node (TRAIT_EXPR);
> > > > -      TREE_TYPE (trait_expr) = boolean_type_node;
> > > > +      if (kind == CPTK_RANK)
> > > > +   TREE_TYPE (trait_expr) = size_type_node;
> > > > +      else
> > > > +   TREE_TYPE (trait_expr) = boolean_type_node;
> > > >         TRAIT_EXPR_TYPE1 (trait_expr) = type1;
> > > >         TRAIT_EXPR_TYPE2 (trait_expr) = type2;
> > > >         TRAIT_EXPR_KIND (trait_expr) = kind;
> > > > @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > > > kind, tree type1, tree type2)
> > > >       case CPTK_IS_UNBOUNDED_ARRAY:
> > > >       case CPTK_IS_UNION:
> > > >       case CPTK_IS_VOLATILE:
> > > > +    case CPTK_RANK:
> > > >         break;
> > > >         case CPTK_IS_LAYOUT_COMPATIBLE:
> > > > @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind
> > > > kind, tree type1, tree type2)
> > > >         gcc_unreachable ();
> > > >       }
> > > >   -  tree val = (trait_expr_value (kind, type1, type2)
> > > > -         ? boolean_true_node : boolean_false_node);
> > > > +  tree val;
> > > > +  if (kind == CPTK_RANK)
> > > > +    {
> > > > +      size_t rank = 0;
> > > > +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
> > > > +   ++rank;
> > > > +      val = build_int_cst (size_type_node, rank);
> > > > +    }
> > > > +  else
> > > > +    val = (trait_expr_value (kind, type1, type2)
> > > > +      ? boolean_true_node : boolean_false_node);
> > > > +
> > > >     return maybe_wrap_with_location (val, loc);
> > > >   }
> > > >   diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > > b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > > index 3aca273aad6..7f7b27f7aa7 100644
> > > > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > > > @@ -179,6 +179,9 @@
> > > >   #if !__has_builtin (__is_volatile)
> > > >   # error "__has_builtin (__is_volatile) failed"
> > > >   #endif
> > > > +#if !__has_builtin (__rank)
> > > > +# error "__has_builtin (__rank) failed"
> > > > +#endif
> > > >   #if !__has_builtin (__reference_constructs_from_temporary)
> > > >   # error "__has_builtin (__reference_constructs_from_temporary) failed"
> > > >   #endif
> > > > diff --git a/gcc/testsuite/g++.dg/ext/rank.C
> > > > b/gcc/testsuite/g++.dg/ext/rank.C
> > > > new file mode 100644
> > > > index 00000000000..28894184387
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/ext/rank.C
> > > > @@ -0,0 +1,24 @@
> > > > +// { dg-do compile { target c++11 } }
> > > > +
> > > > +#include <cstddef>
> > > > +
> > > > +#define SA(X) static_assert((X),#X)
> > > > +
> > > > +class ClassType { };
> > > > +
> > > > +SA(__rank(int) == 0);
> > > > +SA(__rank(int[2]) == 1);
> > > > +SA(__rank(int[][4]) == 2);
> > > > +SA(__rank(int[2][2][4][4][6][6]) == 6);
> > > > +SA(__rank(ClassType) == 0);
> > > > +SA(__rank(ClassType[2]) == 1);
> > > > +SA(__rank(ClassType[][4]) == 2);
> > > > +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
> > > > +
> > > > +template<class T> void f(T) = delete;
> > > > +void f(size_t);
> > > > +
> > > > +template<class T>
> > > > +void g() { f(__rank(T)); }
> > > > +
> > > > +template void g<int>();
> > >
> > >
> >

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-05-02 16:20                         ` Ken Matsui
@ 2024-05-02 16:45                           ` Jason Merrill
  2024-05-02 16:56                             ` Ken Matsui
  2024-05-02 17:12                             ` Trait built-in naming convention Jason Merrill
  0 siblings, 2 replies; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 16:45 UTC (permalink / raw)
  To: Ken Matsui, Patrick Palka; +Cc: Ken Matsui, gcc-patches, libstdc++

On 5/2/24 12:20, Ken Matsui wrote:
> On Thu, May 2, 2024 at 8:34 AM Ken Matsui <kmatsui@cs.washington.edu> wrote:
>>
>> On Thu, May 2, 2024 at 8:16 AM Patrick Palka <ppalka@redhat.com> wrote:
>>>
>>> On Tue, 30 Apr 2024, Jason Merrill wrote:
>>>
>>>> On 2/28/24 11:26, Ken Matsui wrote:
>>>>> This patch implements built-in trait for std::rank.
>>>>
>>>> __rank seems too short, maybe __array_rank?
>>>>
>>>> Actually, it occurs to me that perhaps we should have been adding __builtin to
>>>> all of these rather than just __ and the library trait name.  I guess it's too
>>>> late to do that for the GCC 14 traits, but we could do it for this group?
>>>
>>> Clang already implements many of these built-in, without using
>>> "__builtin" in their name.  Shouldn't we be consistent with Clang where
>>> we can?
> 
> Since I had already replaced the prefix locally with __builtin, I
> submitted patches addressing all other Jason's reviews with that.
> Please let me know if we want to use __ and __array_rank.

If Clang already has a corresponding built-in (as with __array_rank, 
apparently), let's use the same name; otherwise let's add __builtin to 
the new ones.

>> Oh, then, would using __array_rank and keeping existing built-in
>> traits as-is sound like a better choice?
>>
>> https://github.com/llvm/llvm-project/blob/4aca302f5a82ee65847c88500b39a2530dfeceb4/libcxx/include/__type_traits/rank.h#L23
>>
>>>
>>>>
>>>>> gcc/cp/ChangeLog:
>>>>>
>>>>>      * cp-trait.def: Define __rank.
>>>>>      * constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
>>>>>      * semantics.cc (trait_expr_value): Likewise.
>>>>>      (finish_trait_expr): Likewise.
>>>>>
>>>>> gcc/testsuite/ChangeLog:
>>>>>
>>>>>      * g++.dg/ext/has-builtin-1.C: Test existence of __rank.
>>>>>      * g++.dg/ext/rank.C: New test.
>>>>>
>>>>> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
>>>>> ---
>>>>>    gcc/cp/constraint.cc                     |  3 +++
>>>>>    gcc/cp/cp-trait.def                      |  1 +
>>>>>    gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
>>>>>    gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>>>>>    gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
>>>>>    5 files changed, 51 insertions(+), 3 deletions(-)
>>>>>    create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
>>>>>
>>>>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
>>>>> index 000df847342..23ea66d9c12 100644
>>>>> --- a/gcc/cp/constraint.cc
>>>>> +++ b/gcc/cp/constraint.cc
>>>>> @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
>>>>>        case CPTK_IS_VOLATILE:
>>>>>          inform (loc, "  %qT is not a volatile type", t1);
>>>>>          break;
>>>>> +    case CPTK_RANK:
>>>>> +      inform (loc, "  %qT cannot yield a rank", t1);
>>>>> +      break;
>>>>>        case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>>>>>          inform (loc, "  %qT is not a reference that binds to a temporary "
>>>>>            "object of type %qT (direct-initialization)", t1, t2);
>>>>> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
>>>>> index 2d1cb7c227c..85056c8140b 100644
>>>>> --- a/gcc/cp/cp-trait.def
>>>>> +++ b/gcc/cp/cp-trait.def
>>>>> @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE,
>>>>> "__is_trivially_copyable", 1)
>>>>>    DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
>>>>>    DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
>>>>>    DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
>>>>> +DEFTRAIT_EXPR (RANK, "__rank", 1)
>>>>>    DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY,
>>>>> "__reference_constructs_from_temporary", 2)
>>>>>    DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY,
>>>>> "__reference_converts_from_temporary", 2)
>>>>>    DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
>>>>> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
>>>>> index 45dc509855a..7242db75248 100644
>>>>> --- a/gcc/cp/semantics.cc
>>>>> +++ b/gcc/cp/semantics.cc
>>>>> @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1,
>>>>> tree type2)
>>>>>        case CPTK_IS_DEDUCIBLE:
>>>>>          return type_targs_deducible_from (type1, type2);
>>>>>    +    /* __rank is handled in finish_trait_expr. */
>>>>> +    case CPTK_RANK:
>>>>
>>>> This should have a gcc_unreachable.
>>>>
>>>>> +
>>>>>    #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
>>>>>        case CPTK_##CODE:
>>>>>    #include "cp-trait.def"
>>>>> @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind
>>>>> kind, tree type1, tree type2)
>>>>>      if (processing_template_decl)
>>>>>        {
>>>>>          tree trait_expr = make_node (TRAIT_EXPR);
>>>>> -      TREE_TYPE (trait_expr) = boolean_type_node;
>>>>> +      if (kind == CPTK_RANK)
>>>>> +   TREE_TYPE (trait_expr) = size_type_node;
>>>>> +      else
>>>>> +   TREE_TYPE (trait_expr) = boolean_type_node;
>>>>>          TRAIT_EXPR_TYPE1 (trait_expr) = type1;
>>>>>          TRAIT_EXPR_TYPE2 (trait_expr) = type2;
>>>>>          TRAIT_EXPR_KIND (trait_expr) = kind;
>>>>> @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind
>>>>> kind, tree type1, tree type2)
>>>>>        case CPTK_IS_UNBOUNDED_ARRAY:
>>>>>        case CPTK_IS_UNION:
>>>>>        case CPTK_IS_VOLATILE:
>>>>> +    case CPTK_RANK:
>>>>>          break;
>>>>>          case CPTK_IS_LAYOUT_COMPATIBLE:
>>>>> @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind
>>>>> kind, tree type1, tree type2)
>>>>>          gcc_unreachable ();
>>>>>        }
>>>>>    -  tree val = (trait_expr_value (kind, type1, type2)
>>>>> -         ? boolean_true_node : boolean_false_node);
>>>>> +  tree val;
>>>>> +  if (kind == CPTK_RANK)
>>>>> +    {
>>>>> +      size_t rank = 0;
>>>>> +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
>>>>> +   ++rank;
>>>>> +      val = build_int_cst (size_type_node, rank);
>>>>> +    }
>>>>> +  else
>>>>> +    val = (trait_expr_value (kind, type1, type2)
>>>>> +      ? boolean_true_node : boolean_false_node);
>>>>> +
>>>>>      return maybe_wrap_with_location (val, loc);
>>>>>    }
>>>>>    diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
>>>>> b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
>>>>> index 3aca273aad6..7f7b27f7aa7 100644
>>>>> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
>>>>> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
>>>>> @@ -179,6 +179,9 @@
>>>>>    #if !__has_builtin (__is_volatile)
>>>>>    # error "__has_builtin (__is_volatile) failed"
>>>>>    #endif
>>>>> +#if !__has_builtin (__rank)
>>>>> +# error "__has_builtin (__rank) failed"
>>>>> +#endif
>>>>>    #if !__has_builtin (__reference_constructs_from_temporary)
>>>>>    # error "__has_builtin (__reference_constructs_from_temporary) failed"
>>>>>    #endif
>>>>> diff --git a/gcc/testsuite/g++.dg/ext/rank.C
>>>>> b/gcc/testsuite/g++.dg/ext/rank.C
>>>>> new file mode 100644
>>>>> index 00000000000..28894184387
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/g++.dg/ext/rank.C
>>>>> @@ -0,0 +1,24 @@
>>>>> +// { dg-do compile { target c++11 } }
>>>>> +
>>>>> +#include <cstddef>
>>>>> +
>>>>> +#define SA(X) static_assert((X),#X)
>>>>> +
>>>>> +class ClassType { };
>>>>> +
>>>>> +SA(__rank(int) == 0);
>>>>> +SA(__rank(int[2]) == 1);
>>>>> +SA(__rank(int[][4]) == 2);
>>>>> +SA(__rank(int[2][2][4][4][6][6]) == 6);
>>>>> +SA(__rank(ClassType) == 0);
>>>>> +SA(__rank(ClassType[2]) == 1);
>>>>> +SA(__rank(ClassType[][4]) == 2);
>>>>> +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
>>>>> +
>>>>> +template<class T> void f(T) = delete;
>>>>> +void f(size_t);
>>>>> +
>>>>> +template<class T>
>>>>> +void g() { f(__rank(T)); }
>>>>> +
>>>>> +template void g<int>();
>>>>
>>>>
>>>
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v14 21/26] c++: Implement __rank built-in trait
  2024-05-02 16:45                           ` Jason Merrill
@ 2024-05-02 16:56                             ` Ken Matsui
  2024-05-02 17:12                             ` Trait built-in naming convention Jason Merrill
  1 sibling, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 16:56 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 9:45 AM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 12:20, Ken Matsui wrote:
> > On Thu, May 2, 2024 at 8:34 AM Ken Matsui <kmatsui@cs.washington.edu> wrote:
> >>
> >> On Thu, May 2, 2024 at 8:16 AM Patrick Palka <ppalka@redhat.com> wrote:
> >>>
> >>> On Tue, 30 Apr 2024, Jason Merrill wrote:
> >>>
> >>>> On 2/28/24 11:26, Ken Matsui wrote:
> >>>>> This patch implements built-in trait for std::rank.
> >>>>
> >>>> __rank seems too short, maybe __array_rank?
> >>>>
> >>>> Actually, it occurs to me that perhaps we should have been adding __builtin to
> >>>> all of these rather than just __ and the library trait name.  I guess it's too
> >>>> late to do that for the GCC 14 traits, but we could do it for this group?
> >>>
> >>> Clang already implements many of these built-in, without using
> >>> "__builtin" in their name.  Shouldn't we be consistent with Clang where
> >>> we can?
> >
> > Since I had already replaced the prefix locally with __builtin, I
> > submitted patches addressing all other Jason's reviews with that.
> > Please let me know if we want to use __ and __array_rank.
>
> If Clang already has a corresponding built-in (as with __array_rank,
> apparently), let's use the same name; otherwise let's add __builtin to
> the new ones.

I see.  It looks like Clang does not have built-ins for
is_unbounded_array, is_invocable, and is_nothrow_invocable.  I will
revert other built-ins.

>
> >> Oh, then, would using __array_rank and keeping existing built-in
> >> traits as-is sound like a better choice?
> >>
> >> https://github.com/llvm/llvm-project/blob/4aca302f5a82ee65847c88500b39a2530dfeceb4/libcxx/include/__type_traits/rank.h#L23
> >>
> >>>
> >>>>
> >>>>> gcc/cp/ChangeLog:
> >>>>>
> >>>>>      * cp-trait.def: Define __rank.
> >>>>>      * constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
> >>>>>      * semantics.cc (trait_expr_value): Likewise.
> >>>>>      (finish_trait_expr): Likewise.
> >>>>>
> >>>>> gcc/testsuite/ChangeLog:
> >>>>>
> >>>>>      * g++.dg/ext/has-builtin-1.C: Test existence of __rank.
> >>>>>      * g++.dg/ext/rank.C: New test.
> >>>>>
> >>>>> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> >>>>> ---
> >>>>>    gcc/cp/constraint.cc                     |  3 +++
> >>>>>    gcc/cp/cp-trait.def                      |  1 +
> >>>>>    gcc/cp/semantics.cc                      | 23 ++++++++++++++++++++---
> >>>>>    gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
> >>>>>    gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
> >>>>>    5 files changed, 51 insertions(+), 3 deletions(-)
> >>>>>    create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
> >>>>>
> >>>>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> >>>>> index 000df847342..23ea66d9c12 100644
> >>>>> --- a/gcc/cp/constraint.cc
> >>>>> +++ b/gcc/cp/constraint.cc
> >>>>> @@ -3870,6 +3870,9 @@ diagnose_trait_expr (tree expr, tree args)
> >>>>>        case CPTK_IS_VOLATILE:
> >>>>>          inform (loc, "  %qT is not a volatile type", t1);
> >>>>>          break;
> >>>>> +    case CPTK_RANK:
> >>>>> +      inform (loc, "  %qT cannot yield a rank", t1);
> >>>>> +      break;
> >>>>>        case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> >>>>>          inform (loc, "  %qT is not a reference that binds to a temporary "
> >>>>>            "object of type %qT (direct-initialization)", t1, t2);
> >>>>> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> >>>>> index 2d1cb7c227c..85056c8140b 100644
> >>>>> --- a/gcc/cp/cp-trait.def
> >>>>> +++ b/gcc/cp/cp-trait.def
> >>>>> @@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE,
> >>>>> "__is_trivially_copyable", 1)
> >>>>>    DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
> >>>>>    DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> >>>>>    DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
> >>>>> +DEFTRAIT_EXPR (RANK, "__rank", 1)
> >>>>>    DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY,
> >>>>> "__reference_constructs_from_temporary", 2)
> >>>>>    DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY,
> >>>>> "__reference_converts_from_temporary", 2)
> >>>>>    DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
> >>>>> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> >>>>> index 45dc509855a..7242db75248 100644
> >>>>> --- a/gcc/cp/semantics.cc
> >>>>> +++ b/gcc/cp/semantics.cc
> >>>>> @@ -12550,6 +12550,9 @@ trait_expr_value (cp_trait_kind kind, tree type1,
> >>>>> tree type2)
> >>>>>        case CPTK_IS_DEDUCIBLE:
> >>>>>          return type_targs_deducible_from (type1, type2);
> >>>>>    +    /* __rank is handled in finish_trait_expr. */
> >>>>> +    case CPTK_RANK:
> >>>>
> >>>> This should have a gcc_unreachable.
> >>>>
> >>>>> +
> >>>>>    #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
> >>>>>        case CPTK_##CODE:
> >>>>>    #include "cp-trait.def"
> >>>>> @@ -12622,7 +12625,10 @@ finish_trait_expr (location_t loc, cp_trait_kind
> >>>>> kind, tree type1, tree type2)
> >>>>>      if (processing_template_decl)
> >>>>>        {
> >>>>>          tree trait_expr = make_node (TRAIT_EXPR);
> >>>>> -      TREE_TYPE (trait_expr) = boolean_type_node;
> >>>>> +      if (kind == CPTK_RANK)
> >>>>> +   TREE_TYPE (trait_expr) = size_type_node;
> >>>>> +      else
> >>>>> +   TREE_TYPE (trait_expr) = boolean_type_node;
> >>>>>          TRAIT_EXPR_TYPE1 (trait_expr) = type1;
> >>>>>          TRAIT_EXPR_TYPE2 (trait_expr) = type2;
> >>>>>          TRAIT_EXPR_KIND (trait_expr) = kind;
> >>>>> @@ -12714,6 +12720,7 @@ finish_trait_expr (location_t loc, cp_trait_kind
> >>>>> kind, tree type1, tree type2)
> >>>>>        case CPTK_IS_UNBOUNDED_ARRAY:
> >>>>>        case CPTK_IS_UNION:
> >>>>>        case CPTK_IS_VOLATILE:
> >>>>> +    case CPTK_RANK:
> >>>>>          break;
> >>>>>          case CPTK_IS_LAYOUT_COMPATIBLE:
> >>>>> @@ -12745,8 +12752,18 @@ finish_trait_expr (location_t loc, cp_trait_kind
> >>>>> kind, tree type1, tree type2)
> >>>>>          gcc_unreachable ();
> >>>>>        }
> >>>>>    -  tree val = (trait_expr_value (kind, type1, type2)
> >>>>> -         ? boolean_true_node : boolean_false_node);
> >>>>> +  tree val;
> >>>>> +  if (kind == CPTK_RANK)
> >>>>> +    {
> >>>>> +      size_t rank = 0;
> >>>>> +      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
> >>>>> +   ++rank;
> >>>>> +      val = build_int_cst (size_type_node, rank);
> >>>>> +    }
> >>>>> +  else
> >>>>> +    val = (trait_expr_value (kind, type1, type2)
> >>>>> +      ? boolean_true_node : boolean_false_node);
> >>>>> +
> >>>>>      return maybe_wrap_with_location (val, loc);
> >>>>>    }
> >>>>>    diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> >>>>> b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> >>>>> index 3aca273aad6..7f7b27f7aa7 100644
> >>>>> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> >>>>> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> >>>>> @@ -179,6 +179,9 @@
> >>>>>    #if !__has_builtin (__is_volatile)
> >>>>>    # error "__has_builtin (__is_volatile) failed"
> >>>>>    #endif
> >>>>> +#if !__has_builtin (__rank)
> >>>>> +# error "__has_builtin (__rank) failed"
> >>>>> +#endif
> >>>>>    #if !__has_builtin (__reference_constructs_from_temporary)
> >>>>>    # error "__has_builtin (__reference_constructs_from_temporary) failed"
> >>>>>    #endif
> >>>>> diff --git a/gcc/testsuite/g++.dg/ext/rank.C
> >>>>> b/gcc/testsuite/g++.dg/ext/rank.C
> >>>>> new file mode 100644
> >>>>> index 00000000000..28894184387
> >>>>> --- /dev/null
> >>>>> +++ b/gcc/testsuite/g++.dg/ext/rank.C
> >>>>> @@ -0,0 +1,24 @@
> >>>>> +// { dg-do compile { target c++11 } }
> >>>>> +
> >>>>> +#include <cstddef>
> >>>>> +
> >>>>> +#define SA(X) static_assert((X),#X)
> >>>>> +
> >>>>> +class ClassType { };
> >>>>> +
> >>>>> +SA(__rank(int) == 0);
> >>>>> +SA(__rank(int[2]) == 1);
> >>>>> +SA(__rank(int[][4]) == 2);
> >>>>> +SA(__rank(int[2][2][4][4][6][6]) == 6);
> >>>>> +SA(__rank(ClassType) == 0);
> >>>>> +SA(__rank(ClassType[2]) == 1);
> >>>>> +SA(__rank(ClassType[][4]) == 2);
> >>>>> +SA(__rank(ClassType[2][2][4][4][6][6]) == 6);
> >>>>> +
> >>>>> +template<class T> void f(T) = delete;
> >>>>> +void f(size_t);
> >>>>> +
> >>>>> +template<class T>
> >>>>> +void g() { f(__rank(T)); }
> >>>>> +
> >>>>> +template void g<int>();
> >>>>
> >>>>
> >>>
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Trait built-in naming convention
  2024-05-02 16:45                           ` Jason Merrill
  2024-05-02 16:56                             ` Ken Matsui
@ 2024-05-02 17:12                             ` Jason Merrill
  2024-05-02 17:24                               ` Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 17:12 UTC (permalink / raw)
  To: Ken Matsui, Patrick Palka; +Cc: Ken Matsui, gcc-patches, libstdc++

On 5/2/24 12:45, Jason Merrill wrote:
> On 5/2/24 12:20, Ken Matsui wrote:
>> On Thu, May 2, 2024 at 8:34 AM Ken Matsui <kmatsui@cs.washington.edu> 
>> wrote:
>>>
>>> On Thu, May 2, 2024 at 8:16 AM Patrick Palka <ppalka@redhat.com> wrote:
>>>>
>>>> On Tue, 30 Apr 2024, Jason Merrill wrote:
>>>>
>>>>> On 2/28/24 11:26, Ken Matsui wrote:
>>>>>> This patch implements built-in trait for std::rank.
>>>>>
>>>>> __rank seems too short, maybe __array_rank?
>>>>>
>>>>> Actually, it occurs to me that perhaps we should have been adding 
>>>>> __builtin to
>>>>> all of these rather than just __ and the library trait name.  I 
>>>>> guess it's too
>>>>> late to do that for the GCC 14 traits, but we could do it for this 
>>>>> group?
>>>>
>>>> Clang already implements many of these built-in, without using
>>>> "__builtin" in their name.  Shouldn't we be consistent with Clang where
>>>> we can?
>>
>> Since I had already replaced the prefix locally with __builtin, I
>> submitted patches addressing all other Jason's reviews with that.
>> Please let me know if we want to use __ and __array_rank.
> 
> If Clang already has a corresponding built-in (as with __array_rank, 
> apparently), let's use the same name; otherwise let's add __builtin to 
> the new ones.

Eh, this could probably use more discussion.

The practice of omitting __builtin from type-trait builtins goes back to 
Paolo's r123366 (cb68ec50) for 
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=26099

There was some discussion of how to name the built-ins back in 
https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
but __builtin wasn't discussed.

Apparently this naming convention follows the MSVC precedent: 
http://msdn2.microsoft.com/en-us/library/ms177194.aspx

I notice some discussion of this pattern around Clang adding various 
built-ins in https://github.com/llvm/llvm-project/issues/61852 
indicating that this is a policy based on precedent.

But I don't see any actual reason for this pattern other than that it's 
what Paolo happened to do in 2007.

I'm not sure what the right way forward is.  Perhaps we're stuck with 
the questionable choices of the past.

Jason


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 17:12                             ` Trait built-in naming convention Jason Merrill
@ 2024-05-02 17:24                               ` Ken Matsui
  2024-05-02 17:37                                 ` Ville Voutilainen
  0 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 17:24 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 10:12 AM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 12:45, Jason Merrill wrote:
> > On 5/2/24 12:20, Ken Matsui wrote:
> >> On Thu, May 2, 2024 at 8:34 AM Ken Matsui <kmatsui@cs.washington.edu>
> >> wrote:
> >>>
> >>> On Thu, May 2, 2024 at 8:16 AM Patrick Palka <ppalka@redhat.com> wrote:
> >>>>
> >>>> On Tue, 30 Apr 2024, Jason Merrill wrote:
> >>>>
> >>>>> On 2/28/24 11:26, Ken Matsui wrote:
> >>>>>> This patch implements built-in trait for std::rank.
> >>>>>
> >>>>> __rank seems too short, maybe __array_rank?
> >>>>>
> >>>>> Actually, it occurs to me that perhaps we should have been adding
> >>>>> __builtin to
> >>>>> all of these rather than just __ and the library trait name.  I
> >>>>> guess it's too
> >>>>> late to do that for the GCC 14 traits, but we could do it for this
> >>>>> group?
> >>>>
> >>>> Clang already implements many of these built-in, without using
> >>>> "__builtin" in their name.  Shouldn't we be consistent with Clang where
> >>>> we can?
> >>
> >> Since I had already replaced the prefix locally with __builtin, I
> >> submitted patches addressing all other Jason's reviews with that.
> >> Please let me know if we want to use __ and __array_rank.
> >
> > If Clang already has a corresponding built-in (as with __array_rank,
> > apparently), let's use the same name; otherwise let's add __builtin to
> > the new ones.
>
> Eh, this could probably use more discussion.
>
> The practice of omitting __builtin from type-trait builtins goes back to
> Paolo's r123366 (cb68ec50) for
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=26099
>
> There was some discussion of how to name the built-ins back in
> https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> but __builtin wasn't discussed.
>
> Apparently this naming convention follows the MSVC precedent:
> http://msdn2.microsoft.com/en-us/library/ms177194.aspx
>
> I notice some discussion of this pattern around Clang adding various
> built-ins in https://github.com/llvm/llvm-project/issues/61852
> indicating that this is a policy based on precedent.
>
> But I don't see any actual reason for this pattern other than that it's
> what Paolo happened to do in 2007.
>
> I'm not sure what the right way forward is.  Perhaps we're stuck with
> the questionable choices of the past.
>

Hmm, I personally prefer the __builtin prefix.  However, it seems that
we need to reach a consensus across MSVC, Clang, and GCC.  Would this
be realistically possible?

Until then, I think it would be better to use __ for all built-in
traits.  What do you think?

> Jason
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 17:24                               ` Ken Matsui
@ 2024-05-02 17:37                                 ` Ville Voutilainen
  2024-05-02 17:54                                   ` Marek Polacek
  2024-05-02 19:17                                   ` Jonathan Wakely
  0 siblings, 2 replies; 244+ messages in thread
From: Ville Voutilainen @ 2024-05-02 17:37 UTC (permalink / raw)
  To: Ken Matsui
  Cc: Jason Merrill, Patrick Palka, Ken Matsui, gcc-patches, libstdc++

On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> > There was some discussion of how to name the built-ins back in
> > https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> > but __builtin wasn't discussed.
> >
> > Apparently this naming convention follows the MSVC precedent:
> > http://msdn2.microsoft.com/en-us/library/ms177194.aspx
> >
> > I notice some discussion of this pattern around Clang adding various
> > built-ins in https://github.com/llvm/llvm-project/issues/61852
> > indicating that this is a policy based on precedent.
> >
> > But I don't see any actual reason for this pattern other than that it's
> > what Paolo happened to do in 2007.
> >
> > I'm not sure what the right way forward is.  Perhaps we're stuck with
> > the questionable choices of the past.
> >
>
> Hmm, I personally prefer the __builtin prefix.  However, it seems that
> we need to reach a consensus across MSVC, Clang, and GCC.  Would this
> be realistically possible?
>
> Until then, I think it would be better to use __ for all built-in
> traits.  What do you think?

My 0.02: __builtin as a prefix doesn't serve much of a purpose.
Consider __is_constructible. It doesn't add value
to make that __builtin_is_constructible. It's a built-in. Of course
it's a built-in. It's a compiler-implemented trait, and
this is just the intrinsic that implements it.

Most of the existing builtins for traits don't use a __builtin prefix.
Not in GCC, not in other compilers. They are indeed
just double-underscored versions of the traits. I think that's fine,
and consistent. There's precedent for this
across Embarcadero, Clang, MSVC, and GCC. See
https://clang.llvm.org/docs/LanguageExtensions.html

Yes, I know it's inconsistent with other built-ins that aren't C++
library traits. But the water's been flowing under
the bridge on that question for a while now.

I would also prefer at least considering mimicking a trait builtin's
name if some other compiler did it first. That's not a hill
to die on, we don't need to be 100% compatible including the naming,
but if we can, we should just use a name that was
chosen by someone else already. It's just nice to have the same name
if the traits do exactly the same thing. If they don't,
then it's good and in fact very good to give our trait a different name.

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 17:37                                 ` Ville Voutilainen
@ 2024-05-02 17:54                                   ` Marek Polacek
  2024-05-02 19:30                                     ` Ken Matsui
  2024-05-02 19:17                                   ` Jonathan Wakely
  1 sibling, 1 reply; 244+ messages in thread
From: Marek Polacek @ 2024-05-02 17:54 UTC (permalink / raw)
  To: Ville Voutilainen
  Cc: Ken Matsui, Jason Merrill, Patrick Palka, Ken Matsui,
	gcc-patches, libstdc++

On Thu, May 02, 2024 at 08:37:53PM +0300, Ville Voutilainen wrote:
> On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> > > There was some discussion of how to name the built-ins back in
> > > https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> > > but __builtin wasn't discussed.
> > >
> > > Apparently this naming convention follows the MSVC precedent:
> > > http://msdn2.microsoft.com/en-us/library/ms177194.aspx
> > >
> > > I notice some discussion of this pattern around Clang adding various
> > > built-ins in https://github.com/llvm/llvm-project/issues/61852
> > > indicating that this is a policy based on precedent.
> > >
> > > But I don't see any actual reason for this pattern other than that it's
> > > what Paolo happened to do in 2007.
> > >
> > > I'm not sure what the right way forward is.  Perhaps we're stuck with
> > > the questionable choices of the past.
> > >
> >
> > Hmm, I personally prefer the __builtin prefix.  However, it seems that
> > we need to reach a consensus across MSVC, Clang, and GCC.  Would this
> > be realistically possible?
> >
> > Until then, I think it would be better to use __ for all built-in
> > traits.  What do you think?
> 
> My 0.02: __builtin as a prefix doesn't serve much of a purpose.
> Consider __is_constructible. It doesn't add value
> to make that __builtin_is_constructible. It's a built-in. Of course
> it's a built-in. It's a compiler-implemented trait, and
> this is just the intrinsic that implements it.

FWIW, I also like __is_constructible better than __builtin_is_constructible.
 
> Most of the existing builtins for traits don't use a __builtin prefix.
> Not in GCC, not in other compilers. They are indeed
> just double-underscored versions of the traits. I think that's fine,
> and consistent. There's precedent for this
> across Embarcadero, Clang, MSVC, and GCC. See
> https://clang.llvm.org/docs/LanguageExtensions.html
> 
> Yes, I know it's inconsistent with other built-ins that aren't C++
> library traits. But the water's been flowing under
> the bridge on that question for a while now.
> 
> I would also prefer at least considering mimicking a trait builtin's
> name if some other compiler did it first. That's not a hill
> to die on, we don't need to be 100% compatible including the naming,
> but if we can, we should just use a name that was
> chosen by someone else already. It's just nice to have the same name
> if the traits do exactly the same thing. If they don't,
> then it's good and in fact very good to give our trait a different name.
> 

Marek


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 00/26] Optimize more type traits
  2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
                                     ` (25 preceding siblings ...)
  2024-05-02 16:13                   ` [PATCH v16 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
@ 2024-05-02 18:42                   ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                       ` (26 more replies)
  26 siblings, 27 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

Changes from v16:

* Replaced __builtin with __ for built-in trais other than is_unbounded_array,
is_invocable, and is_nothrow_invocable.
* Renamed __rank to __array_rank.

Changes from v15:

* Rebased on top of trunk
* Fixed build_invoke
* Implemented object_type_p & referenceable_type_p
* Prefixed all built-in traits with __builtin instead of __

--

This patch series implements __is_const, __is_volatile, __is_pointer,
and __is_unbounded_array built-in traits, which were isolated from my
previous patch series "Optimize type traits compilation performance"
because they contained performance regression.  I confirmed that this
patch series does not cause any performance regression.  The main reason
of the performance regression were the exhaustiveness of the benchmarks
and the instability of the benchmark results.  Also, this patch series
includes built-ins for add_pointer, remove_extent, remove_all_extents,
add_lvalue_reference, add_rvalue_reference, decay, rank, is_invocable,
and is_nothrow_invocable.  Here are the benchmark results:

is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023
time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%

is_const_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_const_v.md#sat-jun-24-044815-am-pdt-2023
time: -2.86467%, peak memory: -1.0654%, total memory: -1.62369%

is_volatile: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile.md#sun-oct-22-091644-pm-pdt-2023
time: -5.25164%, peak memory: -0.337971%, total memory: -0.247934%

is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023
time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%

is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023
time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%

is_pointer_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer_v.md#sun-oct-22-122257-am-pdt-2023
time: -4.71336%, peak memory: -2.25026%, total memory: -3.125%

is_unbounded_array: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array.md#sun-oct-22-091644-pm-pdt-2023
time: -6.33287%, peak memory: -0.602494%, total memory: -1.56035%

is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023
time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%

add_pointer_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_pointer_t.md#wed-feb-28-060044-am-pst-2024
time: -21.6673%, peak memory: -14.6666%, total memory: -17.4716%

remove_extent_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_extent_t.md#wed-feb-28-063021-am-pst-2024
time: -14.4089%, peak memory: -2.71836%, total memory: -9.87013%

remove_all_extents_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_all_extents_t.md#wed-feb-28-064716-am-pst-2024
time: -28.8941%, peak memory: -16.6981%, total memory: -23.6088%

add_lvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_lvalue_reference_t.md#wed-feb-28-070023-am-pst-2024
time: -33.8827%, peak memory: -24.9292%, total memory: -25.3043%

add_rvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_rvalue_reference_t.md#wed-feb-28-070701-am-pst-2024
time: -23.9186%, peak memory: -17.1311%, total memory: -19.5891%

decay_t: https://github.com/ken-matsui/gcc-bench/blob/main/decay_t.md#wed-feb-28-072330-am-pst-2024
time: -42.4076%, peak memory: -29.2077%, total memory: -33.0914%

rank: https://github.com/ken-matsui/gcc-bench/blob/main/rank.md#wed-feb-28-074917-am-pst-2024
time: -33.7312%, peak memory: -27.5885%, total memory: -34.5736%

rank_v: https://github.com/ken-matsui/gcc-bench/blob/main/rank_v.md#wed-feb-28-073632-am-pst-2024
time: -40.7174%, peak memory: -16.4653%, total memory: -23.0131%

is_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_invocable.md#wed-feb-28-111001-am-pst-2024
time: -58.8307%, peak memory: -59.4966%, total memory: -59.8871%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

is_nothrow_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_nothrow_invocable.md#wed-feb-28-112414-am-pst-2024
time: -70.4102%, peak memory: -62.5516%, total memory: -65.5853%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

Ken Matsui (26):
  c++: Implement __is_const built-in trait
  libstdc++: Optimize std::is_const compilation performance
  c++: Implement __is_volatile built-in trait
  libstdc++: Optimize std::is_volatile compilation performance
  c++: Implement __is_pointer built-in trait
  libstdc++: Optimize std::is_pointer compilation performance
  c++: Implement __builtin_is_unbounded_array trait
  libstdc++: Optimize std::is_unbounded_array compilation performance
  c++: Implement __add_pointer built-in trait
  libstdc++: Optimize std::add_pointer compilation performance
  c++: Implement __remove_extent built-in trait
  libstdc++: Optimize std::remove_extent compilation performance
  c++: Implement __remove_all_extents built-in trait
  libstdc++: Optimize std::remove_all_extents compilation performance
  c++: Implement __add_lvalue_reference built-in trait
  libstdc++: Optimize std::add_lvalue_reference compilation performance
  c++: Implement __add_rvalue_reference built-in trait
  libstdc++: Optimize std::add_rvalue_reference compilation performance
  c++: Implement __decay built-in trait
  libstdc++: Optimize std::decay compilation performance
  c++: Implement __array_rank built-in trait
  libstdc++: Optimize std::rank compilation performance
  c++: Implement __builtin_is_invocable trait
  libstdc++: Optimize std::is_invocable compilation performance
  c++: Implement __builtin_is_nothrow_invocable trait
  libstdc++: Optimize std::is_nothrow_invocable compilation performance

 gcc/cp/constraint.cc                          |  27 ++
 gcc/cp/cp-trait.def                           |  13 +
 gcc/cp/cp-tree.h                              |   2 +
 gcc/cp/method.cc                              | 134 +++++++
 gcc/cp/semantics.cc                           | 118 +++++-
 .../g++.dg/ext/add_lvalue_reference.C         |  21 ++
 gcc/testsuite/g++.dg/ext/add_pointer.C        |  39 ++
 .../g++.dg/ext/add_rvalue_reference.C         |  20 +
 gcc/testsuite/g++.dg/ext/decay.C              |  22 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  45 ++-
 gcc/testsuite/g++.dg/ext/is_const.C           |  20 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C      | 349 ++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C      | 139 +++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C      |  51 +++
 gcc/testsuite/g++.dg/ext/is_invocable4.C      |  33 ++
 .../g++.dg/ext/is_nothrow_invocable.C         |  62 ++++
 gcc/testsuite/g++.dg/ext/is_pointer.C         |  51 +++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C |  37 ++
 gcc/testsuite/g++.dg/ext/is_volatile.C        |  20 +
 gcc/testsuite/g++.dg/ext/rank.C               |  24 ++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C |  16 +
 gcc/testsuite/g++.dg/ext/remove_extent.C      |  16 +
 libstdc++-v3/include/bits/cpp_type_traits.h   |  31 +-
 libstdc++-v3/include/std/type_traits          | 143 ++++++-
 .../is_invocable/incomplete_args_neg.cc       |   1 +
 .../20_util/is_invocable/incomplete_neg.cc    |   1 +
 .../incomplete_args_neg.cc                    |   1 +
 .../is_nothrow_invocable/incomplete_neg.cc    |   1 +
 28 files changed, 1418 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 01/26] c++: Implement __is_const built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 19:49                       ` Jason Merrill
  2024-05-02 18:42                     ` [PATCH v17 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                                       ` (25 subsequent siblings)
  26 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 8a3b5d80ba7..eaf17a50877 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3768,6 +3768,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 2dde65a970b..fa7ba6a9edc 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12530,6 +12530,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12812,6 +12815,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 02/26] libstdc++: Optimize std::is_const compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 01/26] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
                                       ` (24 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b441bf9908f..8df0cf3ac3b 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3331,10 +3338,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 03/26] c++: Implement __is_volatile built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 01/26] c++: Implement __is_const built-in trait Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 19:49                       ` Jason Merrill
  2024-05-02 18:42                     ` [PATCH v17 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                                       ` (23 subsequent siblings)
  26 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index eaf17a50877..d9caf546423 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fa7ba6a9edc..351235a639a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12826,6 +12829,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 04/26] libstdc++: Optimize std::is_volatile compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (2 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
                                       ` (22 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 8df0cf3ac3b..748fa186881 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3360,10 +3367,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 05/26] c++: Implement __is_pointer built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (3 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 19:50                       ` Jason Merrill
  2024-05-02 18:42                     ` [PATCH v17 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                                       ` (21 subsequent siblings)
  26 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	Arrange the order lexically around __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  9 +++--
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 65 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d9caf546423..5a8aaa70fa6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3829,6 +3829,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 351235a639a..86372ea0aba 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12586,6 +12586,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12825,6 +12828,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..4cbe6fe8cea 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -119,12 +119,15 @@
 #if !__has_builtin (__is_object)
 # error "__has_builtin (__is_object) failed"
 #endif
-#if !__has_builtin (__is_pointer_interconvertible_base_of)
-# error "__has_builtin (__is_pointer_interconvertible_base_of) failed"
-#endif
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
+#if !__has_builtin (__is_pointer_interconvertible_base_of)
+# error "__has_builtin (__is_pointer_interconvertible_base_of) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 06/26] libstdc++: Optimize std::is_pointer compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (4 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 07/26] c++: Implement __builtin_is_unbounded_array trait Ken Matsui
                                       ` (20 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..210a9ea00da 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 748fa186881..ea013b4b7bc 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3268,8 +3282,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 07/26] c++: Implement __builtin_is_unbounded_array trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (5 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                                       ` (19 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 5a8aaa70fa6..d4cc8850486 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..00bba91f461 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__builtin_is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 86372ea0aba..138b180d9fb 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12832,6 +12835,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 4cbe6fe8cea..068d8187707 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -17,6 +17,9 @@
 #if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
 # error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
 #endif
+#if !__has_builtin (__builtin_is_unbounded_array)
+# error "__has_builtin (__builtin_is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__builtin_launder)
 # error "__has_builtin (__builtin_launder) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..ac593b99aa7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__builtin_is_unbounded_array, IncompleteUnion, false);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (6 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 07/26] c++: Implement __builtin_is_unbounded_array trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
                                       ` (18 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__builtin_is_unbounded_array trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__builtin_is_unbounded_array trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index ea013b4b7bc..0329cd4511e 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3710,11 +3710,17 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v =
+      __builtin_is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 09/26] c++: Implement __add_pointer built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (7 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
                                       ` (17 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_pointer.
	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.
	(object_type_p): New function.
	(referenceable_type_p): Likewise.
	(trait_expr_value): Use object_type_p.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __add_pointer.
	* g++.dg/ext/add_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 36 ++++++++++++++++++++--
 gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 00bba91f461..7fdb7347ce6 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 138b180d9fb..29c1dbe2a85 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12434,6 +12434,16 @@ fold_builtin_is_corresponding_member (location_t loc, int nargs,
 				   fold_convert (TREE_TYPE (arg1), arg2)));
 }
 
+/* [basic.types] 8.  True iff TYPE is an object type.  */
+
+static bool
+object_type_p (const_tree type)
+{
+  return (TREE_CODE (type) != FUNCTION_TYPE
+          && !TYPE_REF_P (type)
+          && !VOID_TYPE_P (type));
+}
+
 /* Actually evaluates the trait.  */
 
 static bool
@@ -12576,9 +12586,7 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
       return is_nothrow_convertible (type1, type2);
 
     case CPTK_IS_OBJECT:
-      return (type_code1 != FUNCTION_TYPE
-	      && type_code1 != REFERENCE_TYPE
-	      && type_code1 != VOID_TYPE);
+      return object_type_p (type1);
 
     case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
       return pointer_interconvertible_base_of_p (type1, type2);
@@ -12733,6 +12741,18 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
 	      (non_reference (to), non_reference (from))));
 }
 
+/* [defns.referenceable] True iff TYPE is a referenceable type.  */
+
+static bool
+referenceable_type_p (const_tree type)
+{
+  return (TYPE_REF_P (type)
+	  || object_type_p (type)
+	  || (FUNC_OR_METHOD_TYPE_P (type)
+	      && (type_memfn_quals (type) == TYPE_UNQUALIFIED
+		  && type_memfn_rqual (type) == REF_QUAL_NONE)));
+}
+
 /* Process a trait expression.  */
 
 tree
@@ -12900,6 +12920,16 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_POINTER:
+      /* [meta.trans.ptr].  */
+      if (VOID_TYPE_P (type1) || referenceable_type_p (type1))
+	{
+	  if (TYPE_REF_P (type1))
+	    type1 = TREE_TYPE (type1);
+	  return build_pointer_type (type1);
+	}
+      return type1;
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
new file mode 100644
index 00000000000..c405cdd0feb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_pointer(int), int*));
+SA(__is_same(__add_pointer(int*), int**));
+SA(__is_same(__add_pointer(const int), const int*));
+SA(__is_same(__add_pointer(int&), int*));
+SA(__is_same(__add_pointer(ClassType*), ClassType**));
+SA(__is_same(__add_pointer(ClassType), ClassType*));
+SA(__is_same(__add_pointer(void), void*));
+SA(__is_same(__add_pointer(const void), const void*));
+SA(__is_same(__add_pointer(volatile void), volatile void*));
+SA(__is_same(__add_pointer(const volatile void), const volatile void*));
+
+void f1();
+using f1_type = decltype(f1);
+using pf1_type = decltype(&f1);
+SA(__is_same(__add_pointer(f1_type), pf1_type));
+
+void f2() noexcept; // PR libstdc++/78361
+using f2_type = decltype(f2);
+using pf2_type = decltype(&f2);
+SA(__is_same(__add_pointer(f2_type), pf2_type));
+
+using fn_type = void();
+using pfn_type = void(*)();
+SA(__is_same(__add_pointer(fn_type), pfn_type));
+
+SA(__is_same(__add_pointer(void() &), void() &));
+SA(__is_same(__add_pointer(void() & noexcept), void() & noexcept));
+SA(__is_same(__add_pointer(void() const), void() const));
+SA(__is_same(__add_pointer(void(...) &), void(...) &));
+SA(__is_same(__add_pointer(void(...) & noexcept), void(...) & noexcept));
+SA(__is_same(__add_pointer(void(...) const), void(...) const));
+
+SA(__is_same(__add_pointer(void() __restrict), void() __restrict));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 068d8187707..c8da22091d3 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_pointer)
+# error "__has_builtin (__add_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 10/26] libstdc++: Optimize std::add_pointer compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (8 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
                                       ` (16 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::add_pointer
by dispatching to the new __add_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_pointer): Use __add_pointer
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 0329cd4511e..b1a810222c5 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2149,6 +2149,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 #endif
 
+  /// add_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_pointer)
+  template<typename _Tp>
+    struct add_pointer
+    { using type = __add_pointer(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_pointer_helper
     { using type = _Tp; };
@@ -2157,7 +2163,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __add_pointer_helper<_Tp, __void_t<_Tp*>>
     { using type = _Tp*; };
 
-  /// add_pointer
   template<typename _Tp>
     struct add_pointer
     : public __add_pointer_helper<_Tp>
@@ -2170,6 +2175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct add_pointer<_Tp&&>
     { using type = _Tp*; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_pointer
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 11/26] c++: Implement __remove_extent built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (9 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
                                       ` (15 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::remove_extent.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_extent.
	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __remove_extent.
	* g++.dg/ext/remove_extent.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  5 +++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
 4 files changed, 25 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 7fdb7347ce6..03517d8c4f6 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
+DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 29c1dbe2a85..383259fca56 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12938,6 +12938,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return cv_unqualified (type1);
 
+    case CPTK_REMOVE_EXTENT:
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	type1 = TREE_TYPE (type1);
+      return type1;
+
     case CPTK_REMOVE_POINTER:
       if (TYPE_PTR_P (type1))
 	type1 = TREE_TYPE (type1);
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index c8da22091d3..6860a1368da 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -182,6 +182,9 @@
 #if !__has_builtin (__remove_cvref)
 # error "__has_builtin (__remove_cvref) failed"
 #endif
+#if !__has_builtin (__remove_extent)
+# error "__has_builtin (__remove_extent) failed"
+#endif
 #if !__has_builtin (__remove_pointer)
 # error "__has_builtin (__remove_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
new file mode 100644
index 00000000000..6183aca5a48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_extent(int), int));
+SA(__is_same(__remove_extent(int[2]), int));
+SA(__is_same(__remove_extent(int[2][3]), int[3]));
+SA(__is_same(__remove_extent(int[][3]), int[3]));
+SA(__is_same(__remove_extent(const int[2]), const int));
+SA(__is_same(__remove_extent(ClassType), ClassType));
+SA(__is_same(__remove_extent(ClassType[2]), ClassType));
+SA(__is_same(__remove_extent(ClassType[2][3]), ClassType[3]));
+SA(__is_same(__remove_extent(ClassType[][3]), ClassType[3]));
+SA(__is_same(__remove_extent(const ClassType[2]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 12/26] libstdc++: Optimize std::remove_extent compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (10 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
                                       ` (14 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::remove_extent
by dispatching to the new __remove_extent built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_extent): Use __remove_extent
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b1a810222c5..e6c795f9367 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2092,6 +2092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Array modifications.
 
   /// remove_extent
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_extent)
+  template<typename _Tp>
+    struct remove_extent
+    { using type = __remove_extent(_Tp); };
+#else
   template<typename _Tp>
     struct remove_extent
     { using type = _Tp; };
@@ -2103,6 +2108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_extent<_Tp[]>
     { using type = _Tp; };
+#endif
 
   /// remove_all_extents
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 13/26] c++: Implement __remove_all_extents built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (11 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
                                       ` (13 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::remove_all_extents.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_all_extents.
	* semantics.cc (finish_trait_type): Handle
	CPTK_REMOVE_ALL_EXTENTS.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__remove_all_extents.
	* g++.dg/ext/remove_all_extents.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  3 +++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 03517d8c4f6..be5b977986c 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 383259fca56..07779a79d24 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12930,6 +12930,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	}
       return type1;
 
+    case CPTK_REMOVE_ALL_EXTENTS:
+      return strip_array_types (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 6860a1368da..6f10a917f63 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -176,6 +176,9 @@
 #if !__has_builtin (__reference_converts_from_temporary)
 # error "__has_builtin (__reference_converts_from_temporary) failed"
 #endif
+#if !__has_builtin (__remove_all_extents)
+# error "__has_builtin (__remove_all_extents) failed"
+#endif
 #if !__has_builtin (__remove_cv)
 # error "__has_builtin (__remove_cv) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
new file mode 100644
index 00000000000..60ade2ade7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_all_extents(int), int));
+SA(__is_same(__remove_all_extents(int[2]), int));
+SA(__is_same(__remove_all_extents(int[2][3]), int));
+SA(__is_same(__remove_all_extents(int[][3]), int));
+SA(__is_same(__remove_all_extents(const int[2][3]), const int));
+SA(__is_same(__remove_all_extents(ClassType), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2][3]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[][3]), ClassType));
+SA(__is_same(__remove_all_extents(const ClassType[2][3]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 14/26] libstdc++: Optimize std::remove_all_extents compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (12 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
                                       ` (12 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::remove_all_extents by dispatching to the new
__remove_all_extents built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_all_extents): Use
	__remove_all_extents built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index e6c795f9367..e6edb57a3dc 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2111,6 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// remove_all_extents
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_all_extents)
+  template<typename _Tp>
+    struct remove_all_extents
+    { using type = __remove_all_extents(_Tp); };
+#else
   template<typename _Tp>
     struct remove_all_extents
     { using type = _Tp; };
@@ -2122,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_all_extents<_Tp[]>
     { using type = typename remove_all_extents<_Tp>::type; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_extent
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 15/26] c++: Implement __add_lvalue_reference built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (13 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
                                       ` (11 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_lvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_lvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_LVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_lvalue_reference.
	* g++.dg/ext/add_lvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  6 ++++++
 .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index be5b977986c..665aea6e03f 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 07779a79d24..9f2220a1a91 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12920,6 +12920,12 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_LVALUE_REFERENCE:
+      /* [meta.trans.ref].  */
+      if (referenceable_type_p (type1))
+	return cp_build_reference_type (type1, /*rval=*/false);
+      return type1;
+
     case CPTK_ADD_POINTER:
       /* [meta.trans.ptr].  */
       if (VOID_TYPE_P (type1) || referenceable_type_p (type1))
diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
new file mode 100644
index 00000000000..8fe1e0300e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_lvalue_reference(int), int&));
+SA(__is_same(__add_lvalue_reference(int&), int&));
+SA(__is_same(__add_lvalue_reference(const int), const int&));
+SA(__is_same(__add_lvalue_reference(int*), int*&));
+SA(__is_same(__add_lvalue_reference(ClassType&), ClassType&));
+SA(__is_same(__add_lvalue_reference(ClassType), ClassType&));
+SA(__is_same(__add_lvalue_reference(int(int)), int(&)(int)));
+SA(__is_same(__add_lvalue_reference(int&&), int&));
+SA(__is_same(__add_lvalue_reference(ClassType&&), ClassType&));
+SA(__is_same(__add_lvalue_reference(void), void));
+SA(__is_same(__add_lvalue_reference(const void), const void));
+SA(__is_same(__add_lvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_lvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_lvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_lvalue_reference(bool(int)), bool(&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 6f10a917f63..4fcf41702f1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_lvalue_reference)
+# error "__has_builtin (__add_lvalue_reference) failed"
+#endif
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (14 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
                                       ` (10 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::add_lvalue_reference by dispatching to the new
__add_lvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_lvalue_reference): Use
	__add_lvalue_reference built-in trait.
	(__add_lvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index e6edb57a3dc..0ac8c6537c1 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1157,6 +1157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct __add_lvalue_reference_helper
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_lvalue_reference_helper
     { using type = _Tp; };
@@ -1164,6 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_lvalue_reference_helper<_Tp, __void_t<_Tp&>>
     { using type = _Tp&; };
+#endif
 
   template<typename _Tp>
     using __add_lval_ref_t = typename __add_lvalue_reference_helper<_Tp>::type;
@@ -1731,9 +1737,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_lvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct add_lvalue_reference
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_lvalue_reference
     { using type = __add_lval_ref_t<_Tp>; };
+#endif
 
   /// add_rvalue_reference
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 17/26] c++: Implement __add_rvalue_reference built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (15 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
                                       ` (9 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_rvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_rvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_RVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_rvalue_reference.
	* g++.dg/ext/add_rvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  6 ++++++
 .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 30 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 665aea6e03f..8158c368abc 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -50,6 +50,7 @@
 
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
+DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9f2220a1a91..fd6d9fc1dc5 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12936,6 +12936,12 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	}
       return type1;
 
+    case CPTK_ADD_RVALUE_REFERENCE:
+      /* [meta.trans.ref].  */
+      if (referenceable_type_p (type1))
+	return cp_build_reference_type (type1, /*rval=*/true);
+      return type1;
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
new file mode 100644
index 00000000000..c92fe6bfa17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_rvalue_reference(int), int&&));
+SA(__is_same(__add_rvalue_reference(int&&), int&&));
+SA(__is_same(__add_rvalue_reference(int&), int&));
+SA(__is_same(__add_rvalue_reference(const int), const int&&));
+SA(__is_same(__add_rvalue_reference(int*), int*&&));
+SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+SA(__is_same(__add_rvalue_reference(void), void));
+SA(__is_same(__add_rvalue_reference(const void), const void));
+SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 4fcf41702f1..11b82714808 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -8,6 +8,9 @@
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
+#if !__has_builtin (__add_rvalue_reference)
+# error "__has_builtin (__add_rvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (16 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 19/26] c++: Implement __decay built-in trait Ken Matsui
                                       ` (8 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::add_rvalue_reference by dispatching to the new
__add_rvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_rvalue_reference): Use
	__add_rvalue_reference built-in trait.
	(__add_rvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 0ac8c6537c1..ffaf177e511 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1185,6 +1185,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct __add_rvalue_reference_helper
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_rvalue_reference_helper
     { using type = _Tp; };
@@ -1192,6 +1197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_rvalue_reference_helper<_Tp, __void_t<_Tp&&>>
     { using type = _Tp&&; };
+#endif
 
   template<typename _Tp>
     using __add_rval_ref_t = typename __add_rvalue_reference_helper<_Tp>::type;
@@ -1748,9 +1754,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_rvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct add_rvalue_reference
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_rvalue_reference
     { using type = __add_rval_ref_t<_Tp>; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_reference
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 19/26] c++: Implement __decay built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (17 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
                                       ` (7 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::decay.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __decay.
	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __decay.
	* g++.dg/ext/decay.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 12 ++++++++++++
 gcc/testsuite/g++.dg/ext/decay.C         | 22 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 4 files changed, 38 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 8158c368abc..085fd022cf9 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -51,6 +51,7 @@
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
+DEFTRAIT_TYPE (DECAY, "__decay", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fd6d9fc1dc5..c498191a3c8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12942,6 +12942,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	return cp_build_reference_type (type1, /*rval=*/true);
       return type1;
 
+    case CPTK_DECAY:
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
+				  complain);
+      else if (TREE_CODE (type1) == FUNCTION_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
+      else
+	return cv_unqualified (type1);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
new file mode 100644
index 00000000000..8adedfeefe6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/decay.C
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+// Positive tests.
+using test1_type = __decay(bool);
+SA(__is_same(test1_type, bool));
+
+// NB: DR 705.
+using test2_type = __decay(const int);
+SA(__is_same(test2_type, int));
+
+using test3_type = __decay(int[4]);
+SA(__is_same(test3_type, __remove_extent(int[4])*));
+
+using fn_type = void ();
+using test4_type = __decay(fn_type);
+SA(__is_same(test4_type, __add_pointer(fn_type)));
+
+using cfn_type = void () const;
+using test5_type = __decay(cfn_type);
+SA(__is_same(test5_type, cfn_type));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 11b82714808..cde8b264dd1 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -35,6 +35,9 @@
 #if !__has_builtin (__builtin_source_location)
 # error "__has_builtin (__builtin_source_location) failed"
 #endif
+#if !__has_builtin (__decay)
+# error "__has_builtin (__decay) failed"
+#endif
 #if !__has_builtin (__has_nothrow_assign)
 # error "__has_builtin (__has_nothrow_assign) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 20/26] libstdc++: Optimize std::decay compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (18 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 19/26] c++: Implement __decay built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 21/26] c++: Implement __array_rank built-in trait Ken Matsui
                                       ` (6 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::decay
by dispatching to the new __decay built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (decay): Use __decay built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index ffaf177e511..0c3175e5347 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2316,6 +2316,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @cond undocumented
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__decay)
+  template<typename _Tp>
+    struct decay
+    { using type = __decay(_Tp); };
+#else
   // Decay trait for arrays and functions, used for perfect forwarding
   // in make_pair, make_tuple, etc.
   template<typename _Up>
@@ -2347,6 +2352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct decay<_Tp&&>
     { using type = typename __decay_selector<_Tp>::type; };
+#endif
 
   /// @cond undocumented
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 21/26] c++: Implement __array_rank built-in trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (19 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
                                       ` (5 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::rank.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __array_rank.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __array_rank.
	* g++.dg/ext/rank.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 24 +++++++++++++++++++++---
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
 5 files changed, 52 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d4cc8850486..c28d7bf428e 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3871,6 +3871,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
+    case CPTK_RANK:
+      inform (loc, "  %qT cannot yield a rank", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 085fd022cf9..09e75f20443 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__builtin_is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
+DEFTRAIT_EXPR (RANK, "__array_rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c498191a3c8..a293e0fc23e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12642,6 +12642,10 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
+    /* __array_rank is handled in finish_trait_expr. */
+    case CPTK_RANK:
+      gcc_unreachable ();
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12765,7 +12769,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
   if (processing_template_decl)
     {
       tree trait_expr = make_node (TRAIT_EXPR);
-      TREE_TYPE (trait_expr) = boolean_type_node;
+      if (kind == CPTK_RANK)
+	TREE_TYPE (trait_expr) = size_type_node;
+      else
+	TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
       TRAIT_EXPR_TYPE2 (trait_expr) = type2;
       TRAIT_EXPR_KIND (trait_expr) = kind;
@@ -12858,6 +12865,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
+    case CPTK_RANK:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -12889,8 +12897,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  tree val = (trait_expr_value (kind, type1, type2)
-	      ? boolean_true_node : boolean_false_node);
+  tree val;
+  if (kind == CPTK_RANK)
+    {
+      size_t __array_rank = 0;
+      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+	++__array_rank;
+      val = build_int_cst (size_type_node, __array_rank);
+    }
+  else
+    val = (trait_expr_value (kind, type1, type2)
+	   ? boolean_true_node : boolean_false_node);
+
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index cde8b264dd1..5b79229696d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -11,6 +11,9 @@
 #if !__has_builtin (__add_rvalue_reference)
 # error "__has_builtin (__add_rvalue_reference) failed"
 #endif
+#if !__has_builtin (__array_rank)
+# error "__has_builtin (__array_rank) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
new file mode 100644
index 00000000000..0ab2f235036
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/rank.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <cstddef>
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__array_rank(int) == 0);
+SA(__array_rank(int[2]) == 1);
+SA(__array_rank(int[][4]) == 2);
+SA(__array_rank(int[2][2][4][4][6][6]) == 6);
+SA(__array_rank(ClassType) == 0);
+SA(__array_rank(ClassType[2]) == 1);
+SA(__array_rank(ClassType[][4]) == 2);
+SA(__array_rank(ClassType[2][2][4][4][6][6]) == 6);
+
+template<class T> void f(T) = delete;
+void f(size_t);
+
+template<class T>
+void g() { f(__array_rank(T)); }
+
+template void g<int>();
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 22/26] libstdc++: Optimize std::rank compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (20 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 21/26] c++: Implement __array_rank built-in trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 23/26] c++: Implement __builtin_is_invocable trait Ken Matsui
                                       ` (4 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::rank
by dispatching to the new __array_rank built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (rank): Use __array_rank built-in
	trait.
	(rank_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 0c3175e5347..30ee9ee0e8a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1473,6 +1473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// rank
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__array_rank)
+  template<typename _Tp>
+    struct rank
+    : public integral_constant<std::size_t, __array_rank(_Tp)> { };
+#else
   template<typename>
     struct rank
     : public integral_constant<std::size_t, 0> { };
@@ -1484,6 +1489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct rank<_Tp[]>
     : public integral_constant<std::size_t, 1 + rank<_Tp>::value> { };
+#endif
 
   /// extent
   template<typename, unsigned _Uint = 0>
@@ -3583,12 +3589,17 @@ template <typename _Tp>
 template <typename _Tp>
   inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__array_rank)
+template <typename _Tp>
+  inline constexpr size_t rank_v = __array_rank(_Tp);
+#else
 template <typename _Tp>
   inline constexpr size_t rank_v = 0;
 template <typename _Tp, size_t _Size>
   inline constexpr size_t rank_v<_Tp[_Size]> = 1 + rank_v<_Tp>;
 template <typename _Tp>
   inline constexpr size_t rank_v<_Tp[]> = 1 + rank_v<_Tp>;
+#endif
 
 template <typename _Tp, unsigned _Idx = 0>
   inline constexpr size_t extent_v = 0;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 23/26] c++: Implement __builtin_is_invocable trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (21 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
                                       ` (3 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 134 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 723 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 09e75f20443..ca6a797e64d 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__builtin_is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1938ada0268..83dc20e1130 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..faf932258e6 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,140 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be"
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_base_of_datum =
+	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	 && NON_UNION_CLASS_TYPE_P (datum_type)
+	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							datum_type)
+	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+      tree datum_expr = build_trait_object (datum_type);
+      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index a293e0fc23e..55c2ec4b5b1 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 5b79229696d..49e1cd982f5 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -26,6 +26,9 @@
 #if !__has_builtin (__builtin_is_corresponding_member)
 # error "__has_builtin (__builtin_is_corresponding_member) failed"
 #endif
+#if !__has_builtin (__builtin_is_invocable)
+# error "__has_builtin (__builtin_is_invocable) failed"
+#endif
 #if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
 # error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..7f12d5fe8d2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __builtin_is_invocable( func_type_v0 ) );
+SA( ! __builtin_is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __builtin_is_invocable( func_type_i0 ) );
+SA( ! __builtin_is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __builtin_is_invocable( func_type_l0 ) );
+SA( ! __builtin_is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __builtin_is_invocable( func_type_ii ) );
+SA(   __builtin_is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __builtin_is_invocable( func_type_il ) );
+SA( ! __builtin_is_invocable( func_type_il, int ) );
+SA(   __builtin_is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __builtin_is_invocable( func_type_ir ) );
+SA( ! __builtin_is_invocable( func_type_ir, int& ) );
+SA(   __builtin_is_invocable( func_type_ir, int ) );
+SA(   __builtin_is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __builtin_is_invocable( mem_type_i ) );
+SA( ! __builtin_is_invocable( mem_type_i, int ) );
+SA( ! __builtin_is_invocable( mem_type_i, int* ) );
+SA( ! __builtin_is_invocable( mem_type_i, int& ) );
+SA( ! __builtin_is_invocable( mem_type_i, int&& ) );
+SA(   __builtin_is_invocable( mem_type_i, A ) );
+SA(   __builtin_is_invocable( mem_type_i, A* ) );
+SA(   __builtin_is_invocable( mem_type_i, A& ) );
+SA(   __builtin_is_invocable( mem_type_i, A&& ) );
+SA(   __builtin_is_invocable( mem_type_i, const A& ) );
+SA( ! __builtin_is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __builtin_is_invocable( memfun_type_i ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int* ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_i, int&& ) );
+SA(   __builtin_is_invocable( memfun_type_i, A ) );
+SA(   __builtin_is_invocable( memfun_type_i, A* ) );
+SA(   __builtin_is_invocable( memfun_type_i, A& ) );
+SA(   __builtin_is_invocable( memfun_type_i, A&& ) );
+SA( ! __builtin_is_invocable( memfun_type_i, const A& ) );
+SA( ! __builtin_is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __builtin_is_invocable( memfun_type_ic ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, int ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, int& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, A& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, A* ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, const A& ) );
+SA(   __builtin_is_invocable( memfun_type_ic, const A* ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __builtin_is_invocable( memfun_type_iic ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, int ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, A&, int ) );
+SA(   __builtin_is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, A*, int ) );
+SA(   __builtin_is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __builtin_is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __builtin_is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __builtin_is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __builtin_is_invocable( B ) );
+SA(   __builtin_is_invocable( B& ) );
+SA(   __builtin_is_invocable( B&& ) );
+SA( ! __builtin_is_invocable( B* ) );
+SA(   __builtin_is_invocable( CB ) );
+SA(   __builtin_is_invocable( CB& ) );
+SA( ! __builtin_is_invocable( CB* ) );
+
+SA(   __builtin_is_invocable( B, int ) );
+SA(   __builtin_is_invocable( B&, int ) );
+SA(   __builtin_is_invocable( B&&, int ) );
+SA( ! __builtin_is_invocable( B*, int ) );
+SA( ! __builtin_is_invocable( CB, int ) );
+SA( ! __builtin_is_invocable( CB&, int ) );
+SA( ! __builtin_is_invocable( CB*, int ) );
+
+SA( ! __builtin_is_invocable( B, int, int ) );
+SA( ! __builtin_is_invocable( B&, int, int ) );
+SA( ! __builtin_is_invocable( B&&, int, int ) );
+SA( ! __builtin_is_invocable( B*, int, int ) );
+SA( ! __builtin_is_invocable( CB, int, int ) );
+SA( ! __builtin_is_invocable( CB&, int, int ) );
+SA( ! __builtin_is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __builtin_is_invocable( C ) );
+SA( ! __builtin_is_invocable( C& ) );
+SA( ! __builtin_is_invocable( C&& ) );
+SA( ! __builtin_is_invocable( C* ) );
+SA( ! __builtin_is_invocable( CC ) );
+SA( ! __builtin_is_invocable( CC& ) );
+SA( ! __builtin_is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __builtin_is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __builtin_is_invocable( E ) );
+SA( ! __builtin_is_invocable( void (E::*)() ) );
+SA(   __builtin_is_invocable( void (E::*)(), E ) );
+SA(   __builtin_is_invocable( void (E::*)(), E* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __builtin_is_invocable( F ) );
+SA(   __builtin_is_invocable( void (E::*)(), F ) );
+SA(   __builtin_is_invocable( void (E::*)(), F* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __builtin_is_invocable( G ) );
+SA(   __builtin_is_invocable( void (E::*)(), G ) );
+SA( ! __builtin_is_invocable( void (E::*)(), G* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __builtin_is_invocable( H ) );
+SA(   __builtin_is_invocable( void (E::*)(), H ) );
+SA( ! __builtin_is_invocable( void (E::*)(), H* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __builtin_is_invocable( I ) );
+SA(   __builtin_is_invocable( void (E::*)(), I ) );
+SA( ! __builtin_is_invocable( void (E::*)(), I* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __builtin_is_invocable( K ) );
+SA( ! __builtin_is_invocable( void (E::*)(), K ) );
+SA( ! __builtin_is_invocable( void (E::*)(), K* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __builtin_is_invocable( L ) );
+SA( ! __builtin_is_invocable( void (E::*)(), L ) );
+SA( ! __builtin_is_invocable( void (E::*)(), L* ) );
+SA( ! __builtin_is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __builtin_is_invocable( M ) );
+SA( ! __builtin_is_invocable( M& ) );
+SA( ! __builtin_is_invocable( M&& ) );
+SA( ! __builtin_is_invocable( M* ) );
+SA( ! __builtin_is_invocable( CM ) );
+SA( ! __builtin_is_invocable( CM& ) );
+SA( ! __builtin_is_invocable( CM* ) );
+
+SA( ! __builtin_is_invocable( int M::* ) );
+SA(   __builtin_is_invocable( int M::*, M ) );
+SA(   __builtin_is_invocable( int M::*, M& ) );
+SA(   __builtin_is_invocable( int M::*, M&& ) );
+SA(   __builtin_is_invocable( int M::*, M* ) );
+SA(   __builtin_is_invocable( int M::*, CM ) );
+SA(   __builtin_is_invocable( int M::*, CM& ) );
+SA(   __builtin_is_invocable( int M::*, CM* ) );
+SA( ! __builtin_is_invocable( int M::*, int ) );
+
+SA( ! __builtin_is_invocable( int CM::* ) );
+SA(   __builtin_is_invocable( int CM::*, M ) );
+SA(   __builtin_is_invocable( int CM::*, M& ) );
+SA(   __builtin_is_invocable( int CM::*, M&& ) );
+SA(   __builtin_is_invocable( int CM::*, M* ) );
+SA(   __builtin_is_invocable( int CM::*, CM ) );
+SA(   __builtin_is_invocable( int CM::*, CM& ) );
+SA(   __builtin_is_invocable( int CM::*, CM* ) );
+SA( ! __builtin_is_invocable( int CM::*, int ) );
+
+SA( ! __builtin_is_invocable( long M::* ) );
+SA(   __builtin_is_invocable( long M::*, M ) );
+SA(   __builtin_is_invocable( long M::*, M& ) );
+SA(   __builtin_is_invocable( long M::*, M&& ) );
+SA(   __builtin_is_invocable( long M::*, M* ) );
+SA(   __builtin_is_invocable( long M::*, CM ) );
+SA(   __builtin_is_invocable( long M::*, CM& ) );
+SA(   __builtin_is_invocable( long M::*, CM* ) );
+SA( ! __builtin_is_invocable( long M::*, long ) );
+
+SA( ! __builtin_is_invocable( long CM::* ) );
+SA(   __builtin_is_invocable( long CM::*, M ) );
+SA(   __builtin_is_invocable( long CM::*, M& ) );
+SA(   __builtin_is_invocable( long CM::*, M&& ) );
+SA(   __builtin_is_invocable( long CM::*, M* ) );
+SA(   __builtin_is_invocable( long CM::*, CM ) );
+SA(   __builtin_is_invocable( long CM::*, CM& ) );
+SA(   __builtin_is_invocable( long CM::*, CM* ) );
+SA( ! __builtin_is_invocable( long CM::*, long ) );
+
+SA( ! __builtin_is_invocable( short M::* ) );
+SA(   __builtin_is_invocable( short M::*, M ) );
+SA(   __builtin_is_invocable( short M::*, M& ) );
+SA(   __builtin_is_invocable( short M::*, M&& ) );
+SA(   __builtin_is_invocable( short M::*, M* ) );
+SA(   __builtin_is_invocable( short M::*, CM ) );
+SA(   __builtin_is_invocable( short M::*, CM& ) );
+SA(   __builtin_is_invocable( short M::*, CM* ) );
+SA( ! __builtin_is_invocable( short M::*, short ) );
+
+SA( ! __builtin_is_invocable( short CM::* ) );
+SA(   __builtin_is_invocable( short CM::*, M ) );
+SA(   __builtin_is_invocable( short CM::*, M& ) );
+SA(   __builtin_is_invocable( short CM::*, M&& ) );
+SA(   __builtin_is_invocable( short CM::*, M* ) );
+SA(   __builtin_is_invocable( short CM::*, CM ) );
+SA(   __builtin_is_invocable( short CM::*, CM& ) );
+SA(   __builtin_is_invocable( short CM::*, CM* ) );
+SA( ! __builtin_is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __builtin_is_invocable( int M::*, N ) );
+SA( ! __builtin_is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __builtin_is_invocable( int M::*, O ) );
+SA( ! __builtin_is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __builtin_is_invocable( int M::*, P ) );
+SA( ! __builtin_is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __builtin_is_invocable( int M::*, Q ) );
+SA( ! __builtin_is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __builtin_is_invocable( R ) );
+SA(   __builtin_is_invocable( R, int ) );
+SA( ! __builtin_is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __builtin_is_invocable( S ) );
+SA(   __builtin_is_invocable( S, int ) );
+SA(   __builtin_is_invocable( S, int, int ) );
+SA(   __builtin_is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __builtin_is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __builtin_is_invocable( decltype(fn2), int[10] ) );
+SA(   __builtin_is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __builtin_is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __builtin_is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __builtin_is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __builtin_is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __builtin_is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __builtin_is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __builtin_is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __builtin_is_invocable( decltype(&T::func)&, T& ) );
+SA(   __builtin_is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __builtin_is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __builtin_is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __builtin_is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..bf4d2a58d9f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __builtin_is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __builtin_is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __builtin_is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __builtin_is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __builtin_is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __builtin_is_invocable( reference_wrapper<B> ) );
+SA(   __builtin_is_invocable( reference_wrapper<B>& ) );
+SA(   __builtin_is_invocable( reference_wrapper<B>&& ) );
+SA(   __builtin_is_invocable( reference_wrapper<CB> ) );
+SA(   __builtin_is_invocable( reference_wrapper<CB>& ) );
+SA(   __builtin_is_invocable( reference_wrapper<B>, int ) );
+SA( ! __builtin_is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __builtin_is_invocable( reference_wrapper<C> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<C>& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<C>&& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<CC> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __builtin_is_invocable( reference_wrapper<D> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D>& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D>&& ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D>* ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D*> ) );
+SA( ! __builtin_is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __builtin_is_invocable( decltype(fn) ) );
+SA(   __builtin_is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..aa6d4d7183a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __builtin_is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __builtin_is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __builtin_is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __builtin_is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __builtin_is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __builtin_is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __builtin_is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __builtin_is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __builtin_is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __builtin_is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..6c9c6212439
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__builtin_is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __builtin_is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 24/26] libstdc++: Optimize std::is_invocable compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (22 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 23/26] c++: Implement __builtin_is_invocable trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 25/26] c++: Implement __builtin_is_nothrow_invocable trait Ken Matsui
                                       ` (2 subsequent siblings)
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __builtin_is_invocable trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use
	__builtin_is_invocable trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __builtin_is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc     | 1 +
 libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 30ee9ee0e8a..95e31d19907 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3239,7 +3239,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_invocable)
+    : public __bool_constant<__builtin_is_invocable(_Fn, _ArgTypes...)>
+#else
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 25/26] c++: Implement __builtin_is_nothrow_invocable trait
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (23 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 18:42                     ` [PATCH v17 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_nothrow_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __builtin_is_nothrow_invocable.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_NOTHROW_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__builtin_is_nothrow_invocable.
	* g++.dg/ext/is_nothrow_invocable.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  6 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  5 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +
 .../g++.dg/ext/is_nothrow_invocable.C         | 62 +++++++++++++++++++
 5 files changed, 77 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 6d14ef7dcc7..8679d3ce658 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3825,6 +3825,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
 	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
       break;
+    case CPTK_IS_NOTHROW_INVOCABLE:
+	if (!t2)
+	  inform (loc, "  %qT is not nothrow invocable", t1);
+	else
+	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
+	break;
     case CPTK_IS_OBJECT:
       inform (loc, "  %qT is not an object type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index ca6a797e64d..52f8d05ea9f 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
 DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
 DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
 DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__builtin_is_nothrow_invocable", -1)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 55c2ec4b5b1..5d1acba7bc8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12588,6 +12588,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
       return is_nothrow_convertible (type1, type2);
 
+    case CPTK_IS_NOTHROW_INVOCABLE:
+      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
+
     case CPTK_IS_OBJECT:
       return object_type_p (type1);
 
@@ -12723,6 +12726,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_IS_INVOCABLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12828,6 +12832,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 49e1cd982f5..3395f3f2710 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -29,6 +29,9 @@
 #if !__has_builtin (__builtin_is_invocable)
 # error "__has_builtin (__builtin_is_invocable) failed"
 #endif
+#if !__has_builtin (__builtin_is_nothrow_invocable)
+# error "__has_builtin (__builtin_is_nothrow_invocable) failed"
+#endif
 #if !__has_builtin (__builtin_is_pointer_interconvertible_with_class)
 # error "__has_builtin (__builtin_is_pointer_interconvertible_with_class) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
new file mode 100644
index 00000000000..bb84f48d754
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
@@ -0,0 +1,62 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type = void(*)();
+SA( ! __builtin_is_nothrow_invocable(func_type) );
+
+#if __cpp_noexcept_function_type
+using func_type_nt = void(*)() noexcept;
+SA(   __builtin_is_nothrow_invocable(func_type_nt) );
+#endif
+
+struct X { };
+using mem_type = int X::*;
+
+SA( ! __builtin_is_nothrow_invocable(mem_type) );
+SA( ! __builtin_is_nothrow_invocable(mem_type, int) );
+SA( ! __builtin_is_nothrow_invocable(mem_type, int&) );
+SA(   __builtin_is_nothrow_invocable(mem_type, X&) );
+
+using memfun_type = int (X::*)();
+
+SA( ! __builtin_is_nothrow_invocable(memfun_type) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, int) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, int&) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, X&) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type, X*) );
+
+#if __cpp_noexcept_function_type
+using memfun_type_nt = int (X::*)() noexcept;
+
+SA( ! __builtin_is_nothrow_invocable(memfun_type_nt) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type_nt, int) );
+SA( ! __builtin_is_nothrow_invocable(memfun_type_nt, int&) );
+SA(   __builtin_is_nothrow_invocable(memfun_type_nt, X&) );
+SA(   __builtin_is_nothrow_invocable(memfun_type_nt, X*) );
+#endif
+
+struct F {
+  int& operator()();
+  long& operator()() const noexcept;
+  short& operator()(int) &&;
+  char& operator()(int) const& noexcept;
+private:
+  void operator()(int, int) noexcept;
+};
+using CF = const F;
+
+SA( ! __builtin_is_nothrow_invocable(F ) );
+SA(   __builtin_is_nothrow_invocable(CF) );
+
+SA( ! __builtin_is_nothrow_invocable(F,   int) );
+SA(   __builtin_is_nothrow_invocable(F&,  int) );
+
+SA(   __builtin_is_nothrow_invocable(CF,   int) );
+SA(   __builtin_is_nothrow_invocable(CF&,  int) );
+SA( ! __builtin_is_nothrow_invocable(F, int, int) );
+
+struct FX {
+  X operator()() const noexcept { return {}; }
+};
+SA(   __builtin_is_nothrow_invocable(FX) );
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v17 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (24 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 25/26] c++: Implement __builtin_is_nothrow_invocable trait Ken Matsui
@ 2024-05-02 18:42                     ` Ken Matsui
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
  26 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 18:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::is_nothrow_invocable by dispatching to the new
__builtin_is_nothrow_invocable trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_nothrow_invocable): Use
	__builtin_is_nothrow_invocable trait.
	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
	Handle the new error from __builtin_is_nothrow_invocable.
	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
	Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
 .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 95e31d19907..33a37423ead 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3269,8 +3269,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_nothrow_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_nothrow_invocable)
+    : public __bool_constant<__builtin_is_nothrow_invocable(_Fn, _ArgTypes...)>
+#else
     : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
 	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
index 3c225883eaf..3f8542dd366 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
index 5a728bfa03b..d3bdf08448b 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 17:37                                 ` Ville Voutilainen
  2024-05-02 17:54                                   ` Marek Polacek
@ 2024-05-02 19:17                                   ` Jonathan Wakely
  2024-05-02 19:25                                     ` Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Jonathan Wakely @ 2024-05-02 19:17 UTC (permalink / raw)
  To: Ville Voutilainen
  Cc: Ken Matsui, Jason Merrill, Patrick Palka, Ken Matsui,
	gcc-patches, libstdc++

On Thu, 2 May 2024 at 18:38, Ville Voutilainen
<ville.voutilainen@gmail.com> wrote:
>
> On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> > > There was some discussion of how to name the built-ins back in
> > > https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> > > but __builtin wasn't discussed.
> > >
> > > Apparently this naming convention follows the MSVC precedent:
> > > http://msdn2.microsoft.com/en-us/library/ms177194.aspx
> > >
> > > I notice some discussion of this pattern around Clang adding various
> > > built-ins in https://github.com/llvm/llvm-project/issues/61852
> > > indicating that this is a policy based on precedent.
> > >
> > > But I don't see any actual reason for this pattern other than that it's
> > > what Paolo happened to do in 2007.
> > >
> > > I'm not sure what the right way forward is.  Perhaps we're stuck with
> > > the questionable choices of the past.
> > >
> >
> > Hmm, I personally prefer the __builtin prefix.  However, it seems that
> > we need to reach a consensus across MSVC, Clang, and GCC.  Would this
> > be realistically possible?
> >
> > Until then, I think it would be better to use __ for all built-in
> > traits.  What do you think?
>
> My 0.02: __builtin as a prefix doesn't serve much of a purpose.

The main advantage is that it avoids clashes with std::lib components
that previously used the __is_foo name and which need to be renamed to
support a built-in of that name.

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 19:17                                   ` Jonathan Wakely
@ 2024-05-02 19:25                                     ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 19:25 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Ville Voutilainen, Jason Merrill, Patrick Palka, Ken Matsui,
	gcc-patches, libstdc++

On Thu, May 2, 2024 at 12:18 PM Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>
> On Thu, 2 May 2024 at 18:38, Ville Voutilainen
> <ville.voutilainen@gmail.com> wrote:
> >
> > On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> > > > There was some discussion of how to name the built-ins back in
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> > > > but __builtin wasn't discussed.
> > > >
> > > > Apparently this naming convention follows the MSVC precedent:
> > > > http://msdn2.microsoft.com/en-us/library/ms177194.aspx
> > > >
> > > > I notice some discussion of this pattern around Clang adding various
> > > > built-ins in https://github.com/llvm/llvm-project/issues/61852
> > > > indicating that this is a policy based on precedent.
> > > >
> > > > But I don't see any actual reason for this pattern other than that it's
> > > > what Paolo happened to do in 2007.
> > > >
> > > > I'm not sure what the right way forward is.  Perhaps we're stuck with
> > > > the questionable choices of the past.
> > > >
> > >
> > > Hmm, I personally prefer the __builtin prefix.  However, it seems that
> > > we need to reach a consensus across MSVC, Clang, and GCC.  Would this
> > > be realistically possible?
> > >
> > > Until then, I think it would be better to use __ for all built-in
> > > traits.  What do you think?
> >
> > My 0.02: __builtin as a prefix doesn't serve much of a purpose.
>
> The main advantage is that it avoids clashes with std::lib components
> that previously used the __is_foo name and which need to be renamed to
> support a built-in of that name.

I updated the parser to ignore the use of the same identifiers in
existing codes, such as struct __is_pointer in bits/cpp_type_traits.h.
It is working correctly so far, but for example, if there's a
constructor, there would be clashes.

https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=df3559d951ba6572e254a1bd1ef9a34b6e543325

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 17:54                                   ` Marek Polacek
@ 2024-05-02 19:30                                     ` Ken Matsui
  2024-05-02 19:36                                       ` Iain Sandoe
  0 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 19:30 UTC (permalink / raw)
  To: Marek Polacek
  Cc: Ville Voutilainen, Jason Merrill, Patrick Palka, Ken Matsui,
	gcc-patches, libstdc++

On Thu, May 2, 2024 at 10:54 AM Marek Polacek <polacek@redhat.com> wrote:
>
> On Thu, May 02, 2024 at 08:37:53PM +0300, Ville Voutilainen wrote:
> > On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> > > > There was some discussion of how to name the built-ins back in
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> > > > but __builtin wasn't discussed.
> > > >
> > > > Apparently this naming convention follows the MSVC precedent:
> > > > http://msdn2.microsoft.com/en-us/library/ms177194.aspx
> > > >
> > > > I notice some discussion of this pattern around Clang adding various
> > > > built-ins in https://github.com/llvm/llvm-project/issues/61852
> > > > indicating that this is a policy based on precedent.
> > > >
> > > > But I don't see any actual reason for this pattern other than that it's
> > > > what Paolo happened to do in 2007.
> > > >
> > > > I'm not sure what the right way forward is.  Perhaps we're stuck with
> > > > the questionable choices of the past.
> > > >
> > >
> > > Hmm, I personally prefer the __builtin prefix.  However, it seems that
> > > we need to reach a consensus across MSVC, Clang, and GCC.  Would this
> > > be realistically possible?
> > >
> > > Until then, I think it would be better to use __ for all built-in
> > > traits.  What do you think?
> >
> > My 0.02: __builtin as a prefix doesn't serve much of a purpose.
> > Consider __is_constructible. It doesn't add value
> > to make that __builtin_is_constructible. It's a built-in. Of course
> > it's a built-in. It's a compiler-implemented trait, and
> > this is just the intrinsic that implements it.
>
> FWIW, I also like __is_constructible better than __builtin_is_constructible.

So, updating all existing built-in trait names does not seem
realistic.  I think there are two options:

1. As Jason said, we can use the same name as Clang does and otherwise
use __builtin.
2. Or we can simply use __ for all built-in traits to keep consistency
with other built-in traits.

Then, I feel option 2 would sound better since it's consistent across
all built-in type traits and it might confuse Clang when they
implement the same built-in.  Also, it would be easier for me to
implement built-in traits as I don't need to dig into the Clang code
every time I add a new one.

>
> > Most of the existing builtins for traits don't use a __builtin prefix.
> > Not in GCC, not in other compilers. They are indeed
> > just double-underscored versions of the traits. I think that's fine,
> > and consistent. There's precedent for this
> > across Embarcadero, Clang, MSVC, and GCC. See
> > https://clang.llvm.org/docs/LanguageExtensions.html
> >
> > Yes, I know it's inconsistent with other built-ins that aren't C++
> > library traits. But the water's been flowing under
> > the bridge on that question for a while now.
> >
> > I would also prefer at least considering mimicking a trait builtin's
> > name if some other compiler did it first. That's not a hill
> > to die on, we don't need to be 100% compatible including the naming,
> > but if we can, we should just use a name that was
> > chosen by someone else already. It's just nice to have the same name
> > if the traits do exactly the same thing. If they don't,
> > then it's good and in fact very good to give our trait a different name.
> >
>
> Marek
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 19:30                                     ` Ken Matsui
@ 2024-05-02 19:36                                       ` Iain Sandoe
  2024-05-02 19:48                                         ` Jason Merrill
  0 siblings, 1 reply; 244+ messages in thread
From: Iain Sandoe @ 2024-05-02 19:36 UTC (permalink / raw)
  To: Ken Matsui
  Cc: Marek Polacek, Ville Voutilainen, Jason Merrill, Patrick Palka,
	Ken Matsui, GCC Patches, libstdc++



> On 2 May 2024, at 20:30, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> 
> On Thu, May 2, 2024 at 10:54 AM Marek Polacek <polacek@redhat.com> wrote:
>> 
>> On Thu, May 02, 2024 at 08:37:53PM +0300, Ville Voutilainen wrote:
>>> On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
>>>>> There was some discussion of how to name the built-ins back in
>>>>> https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
>>>>> but __builtin wasn't discussed.
>>>>> 
>>>>> Apparently this naming convention follows the MSVC precedent:
>>>>> http://msdn2.microsoft.com/en-us/library/ms177194.aspx
>>>>> 
>>>>> I notice some discussion of this pattern around Clang adding various
>>>>> built-ins in https://github.com/llvm/llvm-project/issues/61852
>>>>> indicating that this is a policy based on precedent.
>>>>> 
>>>>> But I don't see any actual reason for this pattern other than that it's
>>>>> what Paolo happened to do in 2007.
>>>>> 
>>>>> I'm not sure what the right way forward is.  Perhaps we're stuck with
>>>>> the questionable choices of the past.
>>>>> 
>>>> 
>>>> Hmm, I personally prefer the __builtin prefix.  However, it seems that
>>>> we need to reach a consensus across MSVC, Clang, and GCC.  Would this
>>>> be realistically possible?
>>>> 
>>>> Until then, I think it would be better to use __ for all built-in
>>>> traits.  What do you think?
>>> 
>>> My 0.02: __builtin as a prefix doesn't serve much of a purpose.
>>> Consider __is_constructible. It doesn't add value
>>> to make that __builtin_is_constructible. It's a built-in. Of course
>>> it's a built-in. It's a compiler-implemented trait, and
>>> this is just the intrinsic that implements it.
>> 
>> FWIW, I also like __is_constructible better than __builtin_is_constructible.
> 
> So, updating all existing built-in trait names does not seem
> realistic.  I think there are two options:
> 
> 1. As Jason said, we can use the same name as Clang does and otherwise
> use __builtin.
> 2. Or we can simply use __ for all built-in traits to keep consistency
> with other built-in traits.
> 
> Then, I feel option 2 would sound better since it's consistent across
> all built-in type traits and it might confuse Clang when they
> implement the same built-in.  Also, it would be easier for me to
> implement built-in traits as I don't need to dig into the Clang code
> every time I add a new one.

I agree, being consistent with the status-quo is valuable, some decisions
might have not be the best ones - but I think it would be terribly confusing
to mix __ and __builtin (it immediately makes the reader wonder whar the
difference is).

0.02GBP only, as always
Iain

> 
>> 
>>> Most of the existing builtins for traits don't use a __builtin prefix.
>>> Not in GCC, not in other compilers. They are indeed
>>> just double-underscored versions of the traits. I think that's fine,
>>> and consistent. There's precedent for this
>>> across Embarcadero, Clang, MSVC, and GCC. See
>>> https://clang.llvm.org/docs/LanguageExtensions.html
>>> 
>>> Yes, I know it's inconsistent with other built-ins that aren't C++
>>> library traits. But the water's been flowing under
>>> the bridge on that question for a while now.
>>> 
>>> I would also prefer at least considering mimicking a trait builtin's
>>> name if some other compiler did it first. That's not a hill
>>> to die on, we don't need to be 100% compatible including the naming,
>>> but if we can, we should just use a name that was
>>> chosen by someone else already. It's just nice to have the same name
>>> if the traits do exactly the same thing. If they don't,
>>> then it's good and in fact very good to give our trait a different name.
>>> 
>> 
>> Marek


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 19:36                                       ` Iain Sandoe
@ 2024-05-02 19:48                                         ` Jason Merrill
  2024-05-02 19:52                                           ` Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 19:48 UTC (permalink / raw)
  To: Iain Sandoe, Ken Matsui
  Cc: Marek Polacek, Ville Voutilainen, Patrick Palka, Ken Matsui,
	GCC Patches, libstdc++

On 5/2/24 15:36, Iain Sandoe wrote:
> 
> 
>> On 2 May 2024, at 20:30, Ken Matsui <kmatsui@cs.washington.edu> wrote:
>>
>> On Thu, May 2, 2024 at 10:54 AM Marek Polacek <polacek@redhat.com> wrote:
>>>
>>> On Thu, May 02, 2024 at 08:37:53PM +0300, Ville Voutilainen wrote:
>>>> On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
>>>>>> There was some discussion of how to name the built-ins back in
>>>>>> https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
>>>>>> but __builtin wasn't discussed.
>>>>>>
>>>>>> Apparently this naming convention follows the MSVC precedent:
>>>>>> http://msdn2.microsoft.com/en-us/library/ms177194.aspx
>>>>>>
>>>>>> I notice some discussion of this pattern around Clang adding various
>>>>>> built-ins in https://github.com/llvm/llvm-project/issues/61852
>>>>>> indicating that this is a policy based on precedent.
>>>>>>
>>>>>> But I don't see any actual reason for this pattern other than that it's
>>>>>> what Paolo happened to do in 2007.
>>>>>>
>>>>>> I'm not sure what the right way forward is.  Perhaps we're stuck with
>>>>>> the questionable choices of the past.
>>>>>>
>>>>>
>>>>> Hmm, I personally prefer the __builtin prefix.  However, it seems that
>>>>> we need to reach a consensus across MSVC, Clang, and GCC.  Would this
>>>>> be realistically possible?
>>>>>
>>>>> Until then, I think it would be better to use __ for all built-in
>>>>> traits.  What do you think?
>>>>
>>>> My 0.02: __builtin as a prefix doesn't serve much of a purpose.
>>>> Consider __is_constructible. It doesn't add value
>>>> to make that __builtin_is_constructible. It's a built-in. Of course
>>>> it's a built-in. It's a compiler-implemented trait, and
>>>> this is just the intrinsic that implements it.
>>>
>>> FWIW, I also like __is_constructible better than __builtin_is_constructible.
>>
>> So, updating all existing built-in trait names does not seem
>> realistic.  I think there are two options:
>>
>> 1. As Jason said, we can use the same name as Clang does and otherwise
>> use __builtin.
>> 2. Or we can simply use __ for all built-in traits to keep consistency
>> with other built-in traits.
>>
>> Then, I feel option 2 would sound better since it's consistent across
>> all built-in type traits and it might confuse Clang when they
>> implement the same built-in.  Also, it would be easier for me to
>> implement built-in traits as I don't need to dig into the Clang code
>> every time I add a new one.
> 
> I agree, being consistent with the status-quo is valuable, some decisions
> might have not be the best ones - but I think it would be terribly confusing
> to mix __ and __builtin (it immediately makes the reader wonder whar the
> difference is).

This seems to be the prevailing sentiment, so let's continue that way. 
Thanks for the input.

Jason

>>>> Most of the existing builtins for traits don't use a __builtin prefix.
>>>> Not in GCC, not in other compilers. They are indeed
>>>> just double-underscored versions of the traits. I think that's fine,
>>>> and consistent. There's precedent for this
>>>> across Embarcadero, Clang, MSVC, and GCC. See
>>>> https://clang.llvm.org/docs/LanguageExtensions.html
>>>>
>>>> Yes, I know it's inconsistent with other built-ins that aren't C++
>>>> library traits. But the water's been flowing under
>>>> the bridge on that question for a while now.
>>>>
>>>> I would also prefer at least considering mimicking a trait builtin's
>>>> name if some other compiler did it first. That's not a hill
>>>> to die on, we don't need to be 100% compatible including the naming,
>>>> but if we can, we should just use a name that was
>>>> chosen by someone else already. It's just nice to have the same name
>>>> if the traits do exactly the same thing. If they don't,
>>>> then it's good and in fact very good to give our trait a different name.
>>>>
>>>
>>> Marek
> 


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v17 01/26] c++: Implement __is_const built-in trait
  2024-05-02 18:42                     ` [PATCH v17 01/26] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-05-02 19:49                       ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 19:49 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/2/24 14:42, Ken Matsui wrote:
> This patch implements built-in trait for std::is_const.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_const.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
> 	* g++.dg/ext/is_const.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 +++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  4 ++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
>   5 files changed, 31 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 8a3b5d80ba7..eaf17a50877 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3768,6 +3768,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_CLASS:
>         inform (loc, "  %qT is not a class", t1);
>         break;
> +    case CPTK_IS_CONST:
> +      inform (loc, "  %qT is not a const type", t1);
> +      break;
>       case CPTK_IS_CONSTRUCTIBLE:
>         if (!t2)
>       inform (loc, "  %qT is not default constructible", t1);
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 394f006f20f..36faed9c0b3 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
>   DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
>   DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
>   DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
> +DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
>   DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
>   DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
>   DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 2dde65a970b..fa7ba6a9edc 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12530,6 +12530,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_CLASS:
>         return NON_UNION_CLASS_TYPE_P (type1);
>   
> +    case CPTK_IS_CONST:
> +      return CP_TYPE_CONST_P (type1);
> +
>       case CPTK_IS_CONSTRUCTIBLE:
>         return is_xible (INIT_EXPR, type1, type2);
>   
> @@ -12812,6 +12815,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_ARRAY:
>       case CPTK_IS_BOUNDED_ARRAY:
>       case CPTK_IS_CLASS:
> +    case CPTK_IS_CONST:
>       case CPTK_IS_ENUM:
>       case CPTK_IS_FUNCTION:
>       case CPTK_IS_MEMBER_FUNCTION_POINTER:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index 02b4b4d745d..e3640faeb96 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -71,6 +71,9 @@
>   #if !__has_builtin (__is_class)
>   # error "__has_builtin (__is_class) failed"
>   #endif
> +#if !__has_builtin (__is_const)
> +# error "__has_builtin (__is_const) failed"
> +#endif
>   #if !__has_builtin (__is_constructible)
>   # error "__has_builtin (__is_constructible) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
> new file mode 100644
> index 00000000000..8a0e8df72a9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_const.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +using cClassType = const ClassType;
> +using vClassType = volatile ClassType;
> +using cvClassType = const volatile ClassType;
> +
> +// Positive tests.
> +SA(__is_const(const int));
> +SA(__is_const(const volatile int));
> +SA(__is_const(cClassType));
> +SA(__is_const(cvClassType));
> +
> +// Negative tests.
> +SA(!__is_const(int));
> +SA(!__is_const(volatile int));
> +SA(!__is_const(ClassType));
> +SA(!__is_const(vClassType));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v17 03/26] c++: Implement __is_volatile built-in trait
  2024-05-02 18:42                     ` [PATCH v17 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-05-02 19:49                       ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 19:49 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/2/24 14:42, Ken Matsui wrote:
> This patch implements built-in trait for std::is_volatile.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_volatile.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
> 	* g++.dg/ext/is_volatile.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 +++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  4 ++++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
>   gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
>   5 files changed, 31 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index eaf17a50877..d9caf546423 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_UNION:
>         inform (loc, "  %qT is not a union", t1);
>         break;
> +    case CPTK_IS_VOLATILE:
> +      inform (loc, "  %qT is not a volatile type", t1);
> +      break;
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>         inform (loc, "  %qT is not a reference that binds to a temporary "
>   	      "object of type %qT (direct-initialization)", t1, t2);
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index 36faed9c0b3..e9347453829 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
>   DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
>   DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
> +DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
>   DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
>   DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
>   DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index fa7ba6a9edc..351235a639a 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_UNION:
>         return type_code1 == UNION_TYPE;
>   
> +    case CPTK_IS_VOLATILE:
> +      return CP_TYPE_VOLATILE_P (type1);
> +
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>         return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
>   
> @@ -12826,6 +12829,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_SAME:
>       case CPTK_IS_SCOPED_ENUM:
>       case CPTK_IS_UNION:
> +    case CPTK_IS_VOLATILE:
>         break;
>   
>       case CPTK_IS_LAYOUT_COMPATIBLE:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index e3640faeb96..b2e2f2f694d 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -158,6 +158,9 @@
>   #if !__has_builtin (__is_union)
>   # error "__has_builtin (__is_union) failed"
>   #endif
> +#if !__has_builtin (__is_volatile)
> +# error "__has_builtin (__is_volatile) failed"
> +#endif
>   #if !__has_builtin (__reference_constructs_from_temporary)
>   # error "__has_builtin (__reference_constructs_from_temporary) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
> new file mode 100644
> index 00000000000..80a1cfc880d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +class ClassType { };
> +using cClassType = const ClassType;
> +using vClassType = volatile ClassType;
> +using cvClassType = const volatile ClassType;
> +
> +// Positive tests.
> +SA(__is_volatile(volatile int));
> +SA(__is_volatile(const volatile int));
> +SA(__is_volatile(vClassType));
> +SA(__is_volatile(cvClassType));
> +
> +// Negative tests.
> +SA(!__is_volatile(int));
> +SA(!__is_volatile(const int));
> +SA(!__is_volatile(ClassType));
> +SA(!__is_volatile(cClassType));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v17 05/26] c++: Implement __is_pointer built-in trait
  2024-05-02 18:42                     ` [PATCH v17 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-05-02 19:50                       ` Jason Merrill
  0 siblings, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 19:50 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/2/24 14:42, Ken Matsui wrote:
> This patch implements built-in trait for std::is_pointer.

OK.

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_pointer.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
> 	Arrange the order lexically around __is_pointer.
> 	* g++.dg/ext/is_pointer.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |  3 ++
>   gcc/cp/cp-trait.def                      |  1 +
>   gcc/cp/semantics.cc                      |  4 ++
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |  9 +++--
>   gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
>   5 files changed, 65 insertions(+), 3 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index d9caf546423..5a8aaa70fa6 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3829,6 +3829,9 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_POD:
>         inform (loc, "  %qT is not a POD type", t1);
>         break;
> +    case CPTK_IS_POINTER:
> +      inform (loc, "  %qT is not a pointer", t1);
> +      break;
>       case CPTK_IS_POLYMORPHIC:
>         inform (loc, "  %qT is not a polymorphic type", t1);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index e9347453829..18e2d0f3480 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
>   DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
>   DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
>   DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
> +DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
>   DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
>   DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
>   DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 351235a639a..86372ea0aba 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12586,6 +12586,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_POD:
>         return pod_type_p (type1);
>   
> +    case CPTK_IS_POINTER:
> +      return TYPE_PTR_P (type1);
> +
>       case CPTK_IS_POLYMORPHIC:
>         return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
>   
> @@ -12825,6 +12828,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_MEMBER_OBJECT_POINTER:
>       case CPTK_IS_MEMBER_POINTER:
>       case CPTK_IS_OBJECT:
> +    case CPTK_IS_POINTER:
>       case CPTK_IS_REFERENCE:
>       case CPTK_IS_SAME:
>       case CPTK_IS_SCOPED_ENUM:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index b2e2f2f694d..4cbe6fe8cea 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -119,12 +119,15 @@
>   #if !__has_builtin (__is_object)
>   # error "__has_builtin (__is_object) failed"
>   #endif
> -#if !__has_builtin (__is_pointer_interconvertible_base_of)
> -# error "__has_builtin (__is_pointer_interconvertible_base_of) failed"
> -#endif
>   #if !__has_builtin (__is_pod)
>   # error "__has_builtin (__is_pod) failed"
>   #endif
> +#if !__has_builtin (__is_pointer)
> +# error "__has_builtin (__is_pointer) failed"
> +#endif
> +#if !__has_builtin (__is_pointer_interconvertible_base_of)
> +# error "__has_builtin (__is_pointer_interconvertible_base_of) failed"
> +#endif
>   #if !__has_builtin (__is_polymorphic)
>   # error "__has_builtin (__is_polymorphic) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
> new file mode 100644
> index 00000000000..d6e39565950
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
> @@ -0,0 +1,51 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +SA(!__is_pointer(int));
> +SA(__is_pointer(int*));
> +SA(__is_pointer(int**));
> +
> +SA(__is_pointer(const int*));
> +SA(__is_pointer(const int**));
> +SA(__is_pointer(int* const));
> +SA(__is_pointer(int** const));
> +SA(__is_pointer(int* const* const));
> +
> +SA(__is_pointer(volatile int*));
> +SA(__is_pointer(volatile int**));
> +SA(__is_pointer(int* volatile));
> +SA(__is_pointer(int** volatile));
> +SA(__is_pointer(int* volatile* volatile));
> +
> +SA(__is_pointer(const volatile int*));
> +SA(__is_pointer(const volatile int**));
> +SA(__is_pointer(const int* volatile));
> +SA(__is_pointer(volatile int* const));
> +SA(__is_pointer(int* const volatile));
> +SA(__is_pointer(const int** volatile));
> +SA(__is_pointer(volatile int** const));
> +SA(__is_pointer(int** const volatile));
> +SA(__is_pointer(int* const* const volatile));
> +SA(__is_pointer(int* volatile* const volatile));
> +SA(__is_pointer(int* const volatile* const volatile));
> +
> +SA(!__is_pointer(int&));
> +SA(!__is_pointer(const int&));
> +SA(!__is_pointer(volatile int&));
> +SA(!__is_pointer(const volatile int&));
> +
> +SA(!__is_pointer(int&&));
> +SA(!__is_pointer(const int&&));
> +SA(!__is_pointer(volatile int&&));
> +SA(!__is_pointer(const volatile int&&));
> +
> +SA(!__is_pointer(int[3]));
> +SA(!__is_pointer(const int[3]));
> +SA(!__is_pointer(volatile int[3]));
> +SA(!__is_pointer(const volatile int[3]));
> +
> +SA(!__is_pointer(int(int)));
> +SA(__is_pointer(int(*const)(int)));
> +SA(__is_pointer(int(*volatile)(int)));
> +SA(__is_pointer(int(*const volatile)(int)));


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 19:48                                         ` Jason Merrill
@ 2024-05-02 19:52                                           ` Ken Matsui
  2024-05-02 20:09                                             ` Jakub Jelinek
  0 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 19:52 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Iain Sandoe, Marek Polacek, Ville Voutilainen, Patrick Palka,
	Ken Matsui, GCC Patches, libstdc++

On Thu, May 2, 2024 at 12:49 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 15:36, Iain Sandoe wrote:
> >
> >
> >> On 2 May 2024, at 20:30, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> >>
> >> On Thu, May 2, 2024 at 10:54 AM Marek Polacek <polacek@redhat.com> wrote:
> >>>
> >>> On Thu, May 02, 2024 at 08:37:53PM +0300, Ville Voutilainen wrote:
> >>>> On Thu, 2 May 2024 at 20:25, Ken Matsui <kmatsui@cs.washington.edu> wrote:
> >>>>>> There was some discussion of how to name the built-ins back in
> >>>>>> https://gcc.gnu.org/pipermail/gcc-patches/2007-March/thread.html#212171
> >>>>>> but __builtin wasn't discussed.
> >>>>>>
> >>>>>> Apparently this naming convention follows the MSVC precedent:
> >>>>>> http://msdn2.microsoft.com/en-us/library/ms177194.aspx
> >>>>>>
> >>>>>> I notice some discussion of this pattern around Clang adding various
> >>>>>> built-ins in https://github.com/llvm/llvm-project/issues/61852
> >>>>>> indicating that this is a policy based on precedent.
> >>>>>>
> >>>>>> But I don't see any actual reason for this pattern other than that it's
> >>>>>> what Paolo happened to do in 2007.
> >>>>>>
> >>>>>> I'm not sure what the right way forward is.  Perhaps we're stuck with
> >>>>>> the questionable choices of the past.
> >>>>>>
> >>>>>
> >>>>> Hmm, I personally prefer the __builtin prefix.  However, it seems that
> >>>>> we need to reach a consensus across MSVC, Clang, and GCC.  Would this
> >>>>> be realistically possible?
> >>>>>
> >>>>> Until then, I think it would be better to use __ for all built-in
> >>>>> traits.  What do you think?
> >>>>
> >>>> My 0.02: __builtin as a prefix doesn't serve much of a purpose.
> >>>> Consider __is_constructible. It doesn't add value
> >>>> to make that __builtin_is_constructible. It's a built-in. Of course
> >>>> it's a built-in. It's a compiler-implemented trait, and
> >>>> this is just the intrinsic that implements it.
> >>>
> >>> FWIW, I also like __is_constructible better than __builtin_is_constructible.
> >>
> >> So, updating all existing built-in trait names does not seem
> >> realistic.  I think there are two options:
> >>
> >> 1. As Jason said, we can use the same name as Clang does and otherwise
> >> use __builtin.
> >> 2. Or we can simply use __ for all built-in traits to keep consistency
> >> with other built-in traits.
> >>
> >> Then, I feel option 2 would sound better since it's consistent across
> >> all built-in type traits and it might confuse Clang when they
> >> implement the same built-in.  Also, it would be easier for me to
> >> implement built-in traits as I don't need to dig into the Clang code
> >> every time I add a new one.
> >
> > I agree, being consistent with the status-quo is valuable, some decisions
> > might have not be the best ones - but I think it would be terribly confusing
> > to mix __ and __builtin (it immediately makes the reader wonder whar the
> > difference is).
>
> This seems to be the prevailing sentiment, so let's continue that way.
> Thanks for the input.

I actually found that we have two built-in type traits prefixed with
__builtin: __builtin_is_corresponding_member and
__builtin_is_pointer_interconvertible_with_class.  Do we want to
update these to use __ instead for consistency?

>
> Jason
>
> >>>> Most of the existing builtins for traits don't use a __builtin prefix.
> >>>> Not in GCC, not in other compilers. They are indeed
> >>>> just double-underscored versions of the traits. I think that's fine,
> >>>> and consistent. There's precedent for this
> >>>> across Embarcadero, Clang, MSVC, and GCC. See
> >>>> https://clang.llvm.org/docs/LanguageExtensions.html
> >>>>
> >>>> Yes, I know it's inconsistent with other built-ins that aren't C++
> >>>> library traits. But the water's been flowing under
> >>>> the bridge on that question for a while now.
> >>>>
> >>>> I would also prefer at least considering mimicking a trait builtin's
> >>>> name if some other compiler did it first. That's not a hill
> >>>> to die on, we don't need to be 100% compatible including the naming,
> >>>> but if we can, we should just use a name that was
> >>>> chosen by someone else already. It's just nice to have the same name
> >>>> if the traits do exactly the same thing. If they don't,
> >>>> then it's good and in fact very good to give our trait a different name.
> >>>>
> >>>
> >>> Marek
> >
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 19:52                                           ` Ken Matsui
@ 2024-05-02 20:09                                             ` Jakub Jelinek
  2024-05-02 20:15                                               ` Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Jakub Jelinek @ 2024-05-02 20:09 UTC (permalink / raw)
  To: Ken Matsui
  Cc: Jason Merrill, Iain Sandoe, Marek Polacek, Ville Voutilainen,
	Patrick Palka, Ken Matsui, GCC Patches, libstdc++

On Thu, May 02, 2024 at 12:52:59PM -0700, Ken Matsui wrote:
> > This seems to be the prevailing sentiment, so let's continue that way.
> > Thanks for the input.
> 
> I actually found that we have two built-in type traits prefixed with
> __builtin: __builtin_is_corresponding_member and

That is a FE builtin, not a trait, and is very much different from the
__is_* traits, is varargs with extra processing, I don't think any of
the normal traits accepts pointer to members.

> __builtin_is_pointer_interconvertible_with_class.  Do we want to
> update these to use __ instead for consistency?

No, I think we want to keep them as is.

	Jakub


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 00/26] Optimize more type traits
  2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
                                       ` (25 preceding siblings ...)
  2024-05-02 18:42                     ` [PATCH v17 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
@ 2024-05-02 20:12                     ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 01/26] c++: Implement __is_const built-in trait Ken Matsui
                                         ` (25 more replies)
  26 siblings, 26 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

Changes from v17:

* Replaced __builtin with __ for is_unbounded_array, is_invocable, and
is_nothrow_invocable.

Changes from v16:

* Replaced __builtin with __ for built-in trais other than is_unbounded_array,
is_invocable, and is_nothrow_invocable.
* Renamed __rank to __array_rank.

Changes from v15:

* Rebased on top of trunk
* Fixed build_invoke
* Implemented object_type_p & referenceable_type_p
* Prefixed all built-in traits with __builtin instead of __

--

This patch series implements __is_const, __is_volatile, __is_pointer,
and __is_unbounded_array built-in traits, which were isolated from my
previous patch series "Optimize type traits compilation performance"
because they contained performance regression.  I confirmed that this
patch series does not cause any performance regression.  The main reason
of the performance regression were the exhaustiveness of the benchmarks
and the instability of the benchmark results.  Also, this patch series
includes built-ins for add_pointer, remove_extent, remove_all_extents,
add_lvalue_reference, add_rvalue_reference, decay, rank, is_invocable,
and is_nothrow_invocable.  Here are the benchmark results:

is_const: https://github.com/ken-matsui/gcc-bench/blob/main/is_const.md#sat-dec-23-090605-am-pst-2023
time: -4.36603%, peak memory: -0.300891%, total memory: -0.247934%

is_const_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_const_v.md#sat-jun-24-044815-am-pdt-2023
time: -2.86467%, peak memory: -1.0654%, total memory: -1.62369%

is_volatile: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile.md#sun-oct-22-091644-pm-pdt-2023
time: -5.25164%, peak memory: -0.337971%, total memory: -0.247934%

is_volatile_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_volatile_v.md#sat-dec-23-091518-am-pst-2023
time: -4.06816%, peak memory: -0.609298%, total memory: -0.659134%

is_pointer: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer.md#sat-dec-23-124903-pm-pst-2023
time: -2.47124%, peak memory: -2.98207%, total memory: -4.0811%

is_pointer_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_pointer_v.md#sun-oct-22-122257-am-pdt-2023
time: -4.71336%, peak memory: -2.25026%, total memory: -3.125%

is_unbounded_array: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array.md#sun-oct-22-091644-pm-pdt-2023
time: -6.33287%, peak memory: -0.602494%, total memory: -1.56035%

is_unbounded_array_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_unbounded_array_v.md#sat-dec-23-010046-pm-pst-2023
time: -1.50025%, peak memory: -1.07386%, total memory: -2.32394%

add_pointer_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_pointer_t.md#wed-feb-28-060044-am-pst-2024
time: -21.6673%, peak memory: -14.6666%, total memory: -17.4716%

remove_extent_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_extent_t.md#wed-feb-28-063021-am-pst-2024
time: -14.4089%, peak memory: -2.71836%, total memory: -9.87013%

remove_all_extents_t: https://github.com/ken-matsui/gcc-bench/blob/main/remove_all_extents_t.md#wed-feb-28-064716-am-pst-2024
time: -28.8941%, peak memory: -16.6981%, total memory: -23.6088%

add_lvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_lvalue_reference_t.md#wed-feb-28-070023-am-pst-2024
time: -33.8827%, peak memory: -24.9292%, total memory: -25.3043%

add_rvalue_reference_t: https://github.com/ken-matsui/gcc-bench/blob/main/add_rvalue_reference_t.md#wed-feb-28-070701-am-pst-2024
time: -23.9186%, peak memory: -17.1311%, total memory: -19.5891%

decay_t: https://github.com/ken-matsui/gcc-bench/blob/main/decay_t.md#wed-feb-28-072330-am-pst-2024
time: -42.4076%, peak memory: -29.2077%, total memory: -33.0914%

rank: https://github.com/ken-matsui/gcc-bench/blob/main/rank.md#wed-feb-28-074917-am-pst-2024
time: -33.7312%, peak memory: -27.5885%, total memory: -34.5736%

rank_v: https://github.com/ken-matsui/gcc-bench/blob/main/rank_v.md#wed-feb-28-073632-am-pst-2024
time: -40.7174%, peak memory: -16.4653%, total memory: -23.0131%

is_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_invocable.md#wed-feb-28-111001-am-pst-2024
time: -58.8307%, peak memory: -59.4966%, total memory: -59.8871%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

is_nothrow_invocable_v: https://github.com/ken-matsui/gcc-bench/blob/main/is_nothrow_invocable.md#wed-feb-28-112414-am-pst-2024
time: -70.4102%, peak memory: -62.5516%, total memory: -65.5853%
(This benchmark is not exhaustive as my laptop crashed with larger benchmarks)

Ken Matsui (26):
  c++: Implement __is_const built-in trait
  libstdc++: Optimize std::is_const compilation performance
  c++: Implement __is_volatile built-in trait
  libstdc++: Optimize std::is_volatile compilation performance
  c++: Implement __is_pointer built-in trait
  libstdc++: Optimize std::is_pointer compilation performance
  c++: Implement __is_unbounded_array built-in trait
  libstdc++: Optimize std::is_unbounded_array compilation performance
  c++: Implement __add_pointer built-in trait
  libstdc++: Optimize std::add_pointer compilation performance
  c++: Implement __remove_extent built-in trait
  libstdc++: Optimize std::remove_extent compilation performance
  c++: Implement __remove_all_extents built-in trait
  libstdc++: Optimize std::remove_all_extents compilation performance
  c++: Implement __add_lvalue_reference built-in trait
  libstdc++: Optimize std::add_lvalue_reference compilation performance
  c++: Implement __add_rvalue_reference built-in trait
  libstdc++: Optimize std::add_rvalue_reference compilation performance
  c++: Implement __decay built-in trait
  libstdc++: Optimize std::decay compilation performance
  c++: Implement __array_rank built-in trait
  libstdc++: Optimize std::rank compilation performance
  c++: Implement __is_invocable built-in trait
  libstdc++: Optimize std::is_invocable compilation performance
  c++: Implement __is_nothrow_invocable built-in trait
  libstdc++: Optimize std::is_nothrow_invocable compilation performance

 gcc/cp/constraint.cc                          |  27 ++
 gcc/cp/cp-trait.def                           |  13 +
 gcc/cp/cp-tree.h                              |   2 +
 gcc/cp/method.cc                              | 134 +++++++
 gcc/cp/semantics.cc                           | 118 +++++-
 .../g++.dg/ext/add_lvalue_reference.C         |  21 ++
 gcc/testsuite/g++.dg/ext/add_pointer.C        |  39 ++
 .../g++.dg/ext/add_rvalue_reference.C         |  20 +
 gcc/testsuite/g++.dg/ext/decay.C              |  22 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  45 ++-
 gcc/testsuite/g++.dg/ext/is_const.C           |  20 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C      | 349 ++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C      | 139 +++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C      |  51 +++
 gcc/testsuite/g++.dg/ext/is_invocable4.C      |  33 ++
 .../g++.dg/ext/is_nothrow_invocable.C         |  62 ++++
 gcc/testsuite/g++.dg/ext/is_pointer.C         |  51 +++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C |  37 ++
 gcc/testsuite/g++.dg/ext/is_volatile.C        |  20 +
 gcc/testsuite/g++.dg/ext/rank.C               |  24 ++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C |  16 +
 gcc/testsuite/g++.dg/ext/remove_extent.C      |  16 +
 libstdc++-v3/include/bits/cpp_type_traits.h   |  31 +-
 libstdc++-v3/include/std/type_traits          | 142 ++++++-
 .../is_invocable/incomplete_args_neg.cc       |   1 +
 .../20_util/is_invocable/incomplete_neg.cc    |   1 +
 .../incomplete_args_neg.cc                    |   1 +
 .../is_nothrow_invocable/incomplete_neg.cc    |   1 +
 28 files changed, 1417 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 01/26] c++: Implement __is_const built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
                                         ` (24 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_const.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_const.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_CONST.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_const.
	* g++.dg/ext/is_const.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_const.C      | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_const.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 8a3b5d80ba7..eaf17a50877 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3768,6 +3768,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_CLASS:
       inform (loc, "  %qT is not a class", t1);
       break;
+    case CPTK_IS_CONST:
+      inform (loc, "  %qT is not a const type", t1);
+      break;
     case CPTK_IS_CONSTRUCTIBLE:
       if (!t2)
     inform (loc, "  %qT is not default constructible", t1);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 394f006f20f..36faed9c0b3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -64,6 +64,7 @@ DEFTRAIT_EXPR (IS_ASSIGNABLE, "__is_assignable", 2)
 DEFTRAIT_EXPR (IS_BASE_OF, "__is_base_of", 2)
 DEFTRAIT_EXPR (IS_BOUNDED_ARRAY, "__is_bounded_array", 1)
 DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1)
+DEFTRAIT_EXPR (IS_CONST, "__is_const", 1)
 DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1)
 DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2)
 DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 2dde65a970b..fa7ba6a9edc 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12530,6 +12530,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CLASS:
       return NON_UNION_CLASS_TYPE_P (type1);
 
+    case CPTK_IS_CONST:
+      return CP_TYPE_CONST_P (type1);
+
     case CPTK_IS_CONSTRUCTIBLE:
       return is_xible (INIT_EXPR, type1, type2);
 
@@ -12812,6 +12815,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ARRAY:
     case CPTK_IS_BOUNDED_ARRAY:
     case CPTK_IS_CLASS:
+    case CPTK_IS_CONST:
     case CPTK_IS_ENUM:
     case CPTK_IS_FUNCTION:
     case CPTK_IS_MEMBER_FUNCTION_POINTER:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 02b4b4d745d..e3640faeb96 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -71,6 +71,9 @@
 #if !__has_builtin (__is_class)
 # error "__has_builtin (__is_class) failed"
 #endif
+#if !__has_builtin (__is_const)
+# error "__has_builtin (__is_const) failed"
+#endif
 #if !__has_builtin (__is_constructible)
 # error "__has_builtin (__is_constructible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_const.C b/gcc/testsuite/g++.dg/ext/is_const.C
new file mode 100644
index 00000000000..8a0e8df72a9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_const.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_const(const int));
+SA(__is_const(const volatile int));
+SA(__is_const(cClassType));
+SA(__is_const(cvClassType));
+
+// Negative tests.
+SA(!__is_const(int));
+SA(!__is_const(volatile int));
+SA(!__is_const(ClassType));
+SA(!__is_const(vClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 02/26] libstdc++: Optimize std::is_const compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 01/26] c++: Implement __is_const built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-07 13:55                         ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
                                         ` (23 subsequent siblings)
  25 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_const
by dispatching to the new __is_const built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_const): Use __is_const built-in
	trait.
	(is_const_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b441bf9908f..8df0cf3ac3b 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Type properties.
 
   /// is_const
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+  template<typename _Tp>
+    struct is_const
+    : public __bool_constant<__is_const(_Tp)>
+    { };
+#else
   template<typename>
     struct is_const
     : public false_type { };
@@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_const<_Tp const>
     : public true_type { };
+#endif
 
   /// is_volatile
   template<typename>
@@ -3331,10 +3338,15 @@ template <typename _Tp>
   inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
+template <typename _Tp>
+  inline constexpr bool is_const_v = __is_const(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_const_v = false;
 template <typename _Tp>
   inline constexpr bool is_const_v<const _Tp> = true;
+#endif
 
 #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 03/26] c++: Implement __is_volatile built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 01/26] c++: Implement __is_const built-in trait Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
                                         ` (22 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_volatile.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_volatile.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_VOLATILE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_volatile.
	* g++.dg/ext/is_volatile.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/is_volatile.C   | 20 ++++++++++++++++++++
 5 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_volatile.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index eaf17a50877..d9caf546423 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
+    case CPTK_IS_VOLATILE:
+      inform (loc, "  %qT is not a volatile type", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 36faed9c0b3..e9347453829 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
+DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fa7ba6a9edc..351235a639a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
+    case CPTK_IS_VOLATILE:
+      return CP_TYPE_VOLATILE_P (type1);
+
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       return ref_xes_from_temporary (type1, type2, /*direct_init=*/true);
 
@@ -12826,6 +12829,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
     case CPTK_IS_UNION:
+    case CPTK_IS_VOLATILE:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index e3640faeb96..b2e2f2f694d 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
+#if !__has_builtin (__is_volatile)
+# error "__has_builtin (__is_volatile) failed"
+#endif
 #if !__has_builtin (__reference_constructs_from_temporary)
 # error "__has_builtin (__reference_constructs_from_temporary) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_volatile.C b/gcc/testsuite/g++.dg/ext/is_volatile.C
new file mode 100644
index 00000000000..80a1cfc880d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_volatile.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+using cClassType = const ClassType;
+using vClassType = volatile ClassType;
+using cvClassType = const volatile ClassType;
+
+// Positive tests.
+SA(__is_volatile(volatile int));
+SA(__is_volatile(const volatile int));
+SA(__is_volatile(vClassType));
+SA(__is_volatile(cvClassType));
+
+// Negative tests.
+SA(!__is_volatile(int));
+SA(!__is_volatile(const int));
+SA(!__is_volatile(ClassType));
+SA(!__is_volatile(cClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 04/26] libstdc++: Optimize std::is_volatile compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (2 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
                                         ` (21 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_volatile
by dispatching to the new __is_volatile built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_volatile): Use __is_volatile
	built-in trait.
	(is_volatile_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 8df0cf3ac3b..748fa186881 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -851,6 +851,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// is_volatile
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+  template<typename _Tp>
+    struct is_volatile
+    : public __bool_constant<__is_volatile(_Tp)>
+    { };
+#else
   template<typename>
     struct is_volatile
     : public false_type { };
@@ -858,6 +864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct is_volatile<_Tp volatile>
     : public true_type { };
+#endif
 
   /// is_trivial
   template<typename _Tp>
@@ -3360,10 +3367,15 @@ template <typename _Tp>
   inline constexpr bool is_function_v<_Tp&&> = false;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_volatile)
+template <typename _Tp>
+  inline constexpr bool is_volatile_v = __is_volatile(_Tp);
+#else
 template <typename _Tp>
   inline constexpr bool is_volatile_v = false;
 template <typename _Tp>
   inline constexpr bool is_volatile_v<volatile _Tp> = true;
+#endif
 
 template <typename _Tp>
   inline constexpr bool is_trivial_v = __is_trivial(_Tp);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 05/26] c++: Implement __is_pointer built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (3 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
                                         ` (20 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_pointer.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_pointer.
	Arrange the order lexically around __is_pointer.
	* g++.dg/ext/is_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 ++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  9 +++--
 gcc/testsuite/g++.dg/ext/is_pointer.C    | 51 ++++++++++++++++++++++++
 5 files changed, 65 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_pointer.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d9caf546423..5a8aaa70fa6 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3829,6 +3829,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_POD:
       inform (loc, "  %qT is not a POD type", t1);
       break;
+    case CPTK_IS_POINTER:
+      inform (loc, "  %qT is not a pointer", t1);
+      break;
     case CPTK_IS_POLYMORPHIC:
       inform (loc, "  %qT is not a polymorphic type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index e9347453829..18e2d0f3480 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -82,6 +82,7 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
+DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1)
 DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1)
 DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1)
 DEFTRAIT_EXPR (IS_SAME, "__is_same", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 351235a639a..86372ea0aba 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12586,6 +12586,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POD:
       return pod_type_p (type1);
 
+    case CPTK_IS_POINTER:
+      return TYPE_PTR_P (type1);
+
     case CPTK_IS_POLYMORPHIC:
       return CLASS_TYPE_P (type1) && TYPE_POLYMORPHIC_P (type1);
 
@@ -12825,6 +12828,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_MEMBER_OBJECT_POINTER:
     case CPTK_IS_MEMBER_POINTER:
     case CPTK_IS_OBJECT:
+    case CPTK_IS_POINTER:
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index b2e2f2f694d..4cbe6fe8cea 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -119,12 +119,15 @@
 #if !__has_builtin (__is_object)
 # error "__has_builtin (__is_object) failed"
 #endif
-#if !__has_builtin (__is_pointer_interconvertible_base_of)
-# error "__has_builtin (__is_pointer_interconvertible_base_of) failed"
-#endif
 #if !__has_builtin (__is_pod)
 # error "__has_builtin (__is_pod) failed"
 #endif
+#if !__has_builtin (__is_pointer)
+# error "__has_builtin (__is_pointer) failed"
+#endif
+#if !__has_builtin (__is_pointer_interconvertible_base_of)
+# error "__has_builtin (__is_pointer_interconvertible_base_of) failed"
+#endif
 #if !__has_builtin (__is_polymorphic)
 # error "__has_builtin (__is_polymorphic) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_pointer.C b/gcc/testsuite/g++.dg/ext/is_pointer.C
new file mode 100644
index 00000000000..d6e39565950
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_pointer.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+SA(!__is_pointer(int));
+SA(__is_pointer(int*));
+SA(__is_pointer(int**));
+
+SA(__is_pointer(const int*));
+SA(__is_pointer(const int**));
+SA(__is_pointer(int* const));
+SA(__is_pointer(int** const));
+SA(__is_pointer(int* const* const));
+
+SA(__is_pointer(volatile int*));
+SA(__is_pointer(volatile int**));
+SA(__is_pointer(int* volatile));
+SA(__is_pointer(int** volatile));
+SA(__is_pointer(int* volatile* volatile));
+
+SA(__is_pointer(const volatile int*));
+SA(__is_pointer(const volatile int**));
+SA(__is_pointer(const int* volatile));
+SA(__is_pointer(volatile int* const));
+SA(__is_pointer(int* const volatile));
+SA(__is_pointer(const int** volatile));
+SA(__is_pointer(volatile int** const));
+SA(__is_pointer(int** const volatile));
+SA(__is_pointer(int* const* const volatile));
+SA(__is_pointer(int* volatile* const volatile));
+SA(__is_pointer(int* const volatile* const volatile));
+
+SA(!__is_pointer(int&));
+SA(!__is_pointer(const int&));
+SA(!__is_pointer(volatile int&));
+SA(!__is_pointer(const volatile int&));
+
+SA(!__is_pointer(int&&));
+SA(!__is_pointer(const int&&));
+SA(!__is_pointer(volatile int&&));
+SA(!__is_pointer(const volatile int&&));
+
+SA(!__is_pointer(int[3]));
+SA(!__is_pointer(const int[3]));
+SA(!__is_pointer(volatile int[3]));
+SA(!__is_pointer(const volatile int[3]));
+
+SA(!__is_pointer(int(int)));
+SA(__is_pointer(int(*const)(int)));
+SA(__is_pointer(int(*volatile)(int)));
+SA(__is_pointer(int(*const volatile)(int)));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 06/26] libstdc++: Optimize std::is_pointer compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (4 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
                                         ` (19 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui, Jonathan Wakely

This patch optimizes the compilation performance of std::is_pointer
by dispatching to the new __is_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/bits/cpp_type_traits.h (__is_pointer): Use
	__is_pointer built-in trait.  Optimize its implementation.
	* include/std/type_traits (is_pointer): Likewise.
	(is_pointer_v): Likewise.

Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/bits/cpp_type_traits.h | 31 ++++++++++++++-
 libstdc++-v3/include/std/type_traits        | 44 +++++++++++++++++----
 2 files changed, 66 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h b/libstdc++-v3/include/bits/cpp_type_traits.h
index 59f1a1875eb..210a9ea00da 100644
--- a/libstdc++-v3/include/bits/cpp_type_traits.h
+++ b/libstdc++-v3/include/bits/cpp_type_traits.h
@@ -363,6 +363,13 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
   //
   // Pointer types
   //
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
+    struct __is_pointer : __truth_type<_IsPtr>
+    {
+      enum { __value = _IsPtr };
+    };
+#else
   template<typename _Tp>
     struct __is_pointer
     {
@@ -377,6 +384,28 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
       typedef __true_type __type;
     };
 
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+
+  template<typename _Tp>
+    struct __is_pointer<_Tp* const volatile>
+    {
+      enum { __value = 1 };
+      typedef __true_type __type;
+    };
+#endif
+
   //
   // An arithmetic type is an integer type or a floating point type
   //
@@ -387,7 +416,7 @@ __INT_N(__GLIBCXX_TYPE_INT_N_3)
 
   //
   // A scalar type is an arithmetic type or a pointer type
-  // 
+  //
   template<typename _Tp>
     struct __is_scalar
     : public __traitor<__is_arithmetic<_Tp>, __is_pointer<_Tp> >
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 748fa186881..ea013b4b7bc 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -542,19 +542,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public true_type { };
 #endif
 
-  template<typename>
-    struct __is_pointer_helper
+  /// is_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+  template<typename _Tp>
+    struct is_pointer
+    : public __bool_constant<__is_pointer(_Tp)>
+    { };
+#else
+  template<typename _Tp>
+    struct is_pointer
     : public false_type { };
 
   template<typename _Tp>
-    struct __is_pointer_helper<_Tp*>
+    struct is_pointer<_Tp*>
     : public true_type { };
 
-  /// is_pointer
   template<typename _Tp>
-    struct is_pointer
-    : public __is_pointer_helper<__remove_cv_t<_Tp>>::type
-    { };
+    struct is_pointer<_Tp* const>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* volatile>
+    : public true_type { };
+
+  template<typename _Tp>
+    struct is_pointer<_Tp* const volatile>
+    : public true_type { };
+#endif
 
   /// is_lvalue_reference
   template<typename>
@@ -3268,8 +3282,22 @@ template <typename _Tp, size_t _Num>
   inline constexpr bool is_array_v<_Tp[_Num]> = true;
 #endif
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_pointer)
+template <typename _Tp>
+  inline constexpr bool is_pointer_v = __is_pointer(_Tp);
+#else
 template <typename _Tp>
-  inline constexpr bool is_pointer_v = is_pointer<_Tp>::value;
+  inline constexpr bool is_pointer_v = false;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp*> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* volatile> = true;
+template <typename _Tp>
+  inline constexpr bool is_pointer_v<_Tp* const volatile> = true;
+#endif
+
 template <typename _Tp>
   inline constexpr bool is_lvalue_reference_v = false;
 template <typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 07/26] c++: Implement __is_unbounded_array built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (5 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
                                         ` (18 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_unbounded_array.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_unbounded_array.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_UNBOUNDED_ARRAY.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_unbounded_array.
	* g++.dg/ext/is_unbounded_array.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  3 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  4 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 ++
 gcc/testsuite/g++.dg/ext/is_unbounded_array.C | 37 +++++++++++++++++++
 5 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_unbounded_array.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 5a8aaa70fa6..d4cc8850486 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3862,6 +3862,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       inform (loc, "  %qT is not trivially copyable", t1);
       break;
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      inform (loc, "  %qT is not an unbounded array", t1);
+      break;
     case CPTK_IS_UNION:
       inform (loc, "  %qT is not a union", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 18e2d0f3480..05514a51c21 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -92,6 +92,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2)
 DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1)
 DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
+DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 86372ea0aba..138b180d9fb 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12616,6 +12616,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
       return trivially_copyable_p (type1);
 
+    case CPTK_IS_UNBOUNDED_ARRAY:
+      return array_of_unknown_bound_p (type1);
+
     case CPTK_IS_UNION:
       return type_code1 == UNION_TYPE;
 
@@ -12832,6 +12835,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_REFERENCE:
     case CPTK_IS_SAME:
     case CPTK_IS_SCOPED_ENUM:
+    case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
       break;
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 4cbe6fe8cea..1e10c87754a 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -158,6 +158,9 @@
 #if !__has_builtin (__is_trivially_copyable)
 # error "__has_builtin (__is_trivially_copyable) failed"
 #endif
+#if !__has_builtin (__is_unbounded_array)
+# error "__has_builtin (__is_unbounded_array) failed"
+#endif
 #if !__has_builtin (__is_union)
 # error "__has_builtin (__is_union) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_unbounded_array.C b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
new file mode 100644
index 00000000000..283a74e1a0a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_unbounded_array.C
@@ -0,0 +1,37 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+#define SA_TEST_CATEGORY(TRAIT, TYPE, EXPECT)	\
+  SA(TRAIT(TYPE) == EXPECT);					\
+  SA(TRAIT(const TYPE) == EXPECT);				\
+  SA(TRAIT(volatile TYPE) == EXPECT);			\
+  SA(TRAIT(const volatile TYPE) == EXPECT)
+
+class ClassType { };
+class IncompleteClass;
+union IncompleteUnion;
+
+SA_TEST_CATEGORY(__is_unbounded_array, int[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, float*[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[], true);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[2][3], false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass[][3], true);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(*)[], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[2], false);
+SA_TEST_CATEGORY(__is_unbounded_array, int(&)[], false);
+
+// Sanity check.
+SA_TEST_CATEGORY(__is_unbounded_array, ClassType, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteClass, false);
+SA_TEST_CATEGORY(__is_unbounded_array, IncompleteUnion, false);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (6 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
                                         ` (17 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::is_unbounded_array by dispatching to the new
__is_unbounded_array built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_unbounded_array_v): Use
	__is_unbounded_array built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index ea013b4b7bc..1562757f886 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3710,11 +3710,16 @@ template<typename _Ret, typename _Fn, typename... _Args>
   /// True for a type that is an array of unknown bound.
   /// @ingroup variable_templates
   /// @since C++20
+# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_unbounded_array)
+  template<typename _Tp>
+    inline constexpr bool is_unbounded_array_v = __is_unbounded_array(_Tp);
+# else
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v = false;
 
   template<typename _Tp>
     inline constexpr bool is_unbounded_array_v<_Tp[]> = true;
+# endif
 
   /// True for a type that is an array of known bound.
   /// @since C++20
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 09/26] c++: Implement __add_pointer built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (7 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
                                         ` (16 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_pointer.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_pointer.
	* semantics.cc (finish_trait_type): Handle CPTK_ADD_POINTER.
	(object_type_p): New function.
	(referenceable_type_p): Likewise.
	(trait_expr_value): Use object_type_p.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __add_pointer.
	* g++.dg/ext/add_pointer.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 36 ++++++++++++++++++++--
 gcc/testsuite/g++.dg/ext/add_pointer.C   | 39 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 ++
 4 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_pointer.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 05514a51c21..63f879287ce 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 138b180d9fb..29c1dbe2a85 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12434,6 +12434,16 @@ fold_builtin_is_corresponding_member (location_t loc, int nargs,
 				   fold_convert (TREE_TYPE (arg1), arg2)));
 }
 
+/* [basic.types] 8.  True iff TYPE is an object type.  */
+
+static bool
+object_type_p (const_tree type)
+{
+  return (TREE_CODE (type) != FUNCTION_TYPE
+          && !TYPE_REF_P (type)
+          && !VOID_TYPE_P (type));
+}
+
 /* Actually evaluates the trait.  */
 
 static bool
@@ -12576,9 +12586,7 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
       return is_nothrow_convertible (type1, type2);
 
     case CPTK_IS_OBJECT:
-      return (type_code1 != FUNCTION_TYPE
-	      && type_code1 != REFERENCE_TYPE
-	      && type_code1 != VOID_TYPE);
+      return object_type_p (type1);
 
     case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
       return pointer_interconvertible_base_of_p (type1, type2);
@@ -12733,6 +12741,18 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
 	      (non_reference (to), non_reference (from))));
 }
 
+/* [defns.referenceable] True iff TYPE is a referenceable type.  */
+
+static bool
+referenceable_type_p (const_tree type)
+{
+  return (TYPE_REF_P (type)
+	  || object_type_p (type)
+	  || (FUNC_OR_METHOD_TYPE_P (type)
+	      && (type_memfn_quals (type) == TYPE_UNQUALIFIED
+		  && type_memfn_rqual (type) == REF_QUAL_NONE)));
+}
+
 /* Process a trait expression.  */
 
 tree
@@ -12900,6 +12920,16 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_POINTER:
+      /* [meta.trans.ptr].  */
+      if (VOID_TYPE_P (type1) || referenceable_type_p (type1))
+	{
+	  if (TYPE_REF_P (type1))
+	    type1 = TREE_TYPE (type1);
+	  return build_pointer_type (type1);
+	}
+      return type1;
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_pointer.C b/gcc/testsuite/g++.dg/ext/add_pointer.C
new file mode 100644
index 00000000000..c405cdd0feb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_pointer.C
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_pointer(int), int*));
+SA(__is_same(__add_pointer(int*), int**));
+SA(__is_same(__add_pointer(const int), const int*));
+SA(__is_same(__add_pointer(int&), int*));
+SA(__is_same(__add_pointer(ClassType*), ClassType**));
+SA(__is_same(__add_pointer(ClassType), ClassType*));
+SA(__is_same(__add_pointer(void), void*));
+SA(__is_same(__add_pointer(const void), const void*));
+SA(__is_same(__add_pointer(volatile void), volatile void*));
+SA(__is_same(__add_pointer(const volatile void), const volatile void*));
+
+void f1();
+using f1_type = decltype(f1);
+using pf1_type = decltype(&f1);
+SA(__is_same(__add_pointer(f1_type), pf1_type));
+
+void f2() noexcept; // PR libstdc++/78361
+using f2_type = decltype(f2);
+using pf2_type = decltype(&f2);
+SA(__is_same(__add_pointer(f2_type), pf2_type));
+
+using fn_type = void();
+using pfn_type = void(*)();
+SA(__is_same(__add_pointer(fn_type), pfn_type));
+
+SA(__is_same(__add_pointer(void() &), void() &));
+SA(__is_same(__add_pointer(void() & noexcept), void() & noexcept));
+SA(__is_same(__add_pointer(void() const), void() const));
+SA(__is_same(__add_pointer(void(...) &), void(...) &));
+SA(__is_same(__add_pointer(void(...) & noexcept), void(...) & noexcept));
+SA(__is_same(__add_pointer(void(...) const), void(...) const));
+
+SA(__is_same(__add_pointer(void() __restrict), void() __restrict));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 1e10c87754a..bd9e064ce3a 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_pointer)
+# error "__has_builtin (__add_pointer) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 10/26] libstdc++: Optimize std::add_pointer compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (8 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
                                         ` (15 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::add_pointer
by dispatching to the new __add_pointer built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_pointer): Use __add_pointer
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1562757f886..6313fde94c2 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2149,6 +2149,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 #endif
 
+  /// add_pointer
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_pointer)
+  template<typename _Tp>
+    struct add_pointer
+    { using type = __add_pointer(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_pointer_helper
     { using type = _Tp; };
@@ -2157,7 +2163,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __add_pointer_helper<_Tp, __void_t<_Tp*>>
     { using type = _Tp*; };
 
-  /// add_pointer
   template<typename _Tp>
     struct add_pointer
     : public __add_pointer_helper<_Tp>
@@ -2170,6 +2175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct add_pointer<_Tp&&>
     { using type = _Tp*; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_pointer
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 11/26] c++: Implement __remove_extent built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (9 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
                                         ` (14 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::remove_extent.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_extent.
	* semantics.cc (finish_trait_type): Handle CPTK_REMOVE_EXTENT.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __remove_extent.
	* g++.dg/ext/remove_extent.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      |  5 +++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/remove_extent.C | 16 ++++++++++++++++
 4 files changed, 25 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_extent.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 63f879287ce..577c96d579b 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -100,6 +100,7 @@ DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_tempo
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
+DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
 DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1)
 DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 29c1dbe2a85..383259fca56 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12938,6 +12938,11 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	type1 = TREE_TYPE (type1);
       return cv_unqualified (type1);
 
+    case CPTK_REMOVE_EXTENT:
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	type1 = TREE_TYPE (type1);
+      return type1;
+
     case CPTK_REMOVE_POINTER:
       if (TYPE_PTR_P (type1))
 	type1 = TREE_TYPE (type1);
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index bd9e064ce3a..d58e4186704 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -182,6 +182,9 @@
 #if !__has_builtin (__remove_cvref)
 # error "__has_builtin (__remove_cvref) failed"
 #endif
+#if !__has_builtin (__remove_extent)
+# error "__has_builtin (__remove_extent) failed"
+#endif
 #if !__has_builtin (__remove_pointer)
 # error "__has_builtin (__remove_pointer) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_extent.C b/gcc/testsuite/g++.dg/ext/remove_extent.C
new file mode 100644
index 00000000000..6183aca5a48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_extent.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_extent(int), int));
+SA(__is_same(__remove_extent(int[2]), int));
+SA(__is_same(__remove_extent(int[2][3]), int[3]));
+SA(__is_same(__remove_extent(int[][3]), int[3]));
+SA(__is_same(__remove_extent(const int[2]), const int));
+SA(__is_same(__remove_extent(ClassType), ClassType));
+SA(__is_same(__remove_extent(ClassType[2]), ClassType));
+SA(__is_same(__remove_extent(ClassType[2][3]), ClassType[3]));
+SA(__is_same(__remove_extent(ClassType[][3]), ClassType[3]));
+SA(__is_same(__remove_extent(const ClassType[2]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 12/26] libstdc++: Optimize std::remove_extent compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (10 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
                                         ` (13 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::remove_extent
by dispatching to the new __remove_extent built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_extent): Use __remove_extent
	built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6313fde94c2..58281c2e38c 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2092,6 +2092,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Array modifications.
 
   /// remove_extent
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_extent)
+  template<typename _Tp>
+    struct remove_extent
+    { using type = __remove_extent(_Tp); };
+#else
   template<typename _Tp>
     struct remove_extent
     { using type = _Tp; };
@@ -2103,6 +2108,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_extent<_Tp[]>
     { using type = _Tp; };
+#endif
 
   /// remove_all_extents
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 13/26] c++: Implement __remove_all_extents built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (11 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
                                         ` (12 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::remove_all_extents.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __remove_all_extents.
	* semantics.cc (finish_trait_type): Handle
	CPTK_REMOVE_ALL_EXTENTS.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__remove_all_extents.
	* g++.dg/ext/remove_all_extents.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  3 +++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 gcc/testsuite/g++.dg/ext/remove_all_extents.C | 16 ++++++++++++++++
 4 files changed, 23 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/remove_all_extents.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 577c96d579b..933c8bcbe68 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -98,6 +98,7 @@ DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
+DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
 DEFTRAIT_TYPE (REMOVE_CV, "__remove_cv", 1)
 DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1)
 DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 383259fca56..07779a79d24 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12930,6 +12930,9 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	}
       return type1;
 
+    case CPTK_REMOVE_ALL_EXTENTS:
+      return strip_array_types (type1);
+
     case CPTK_REMOVE_CV:
       return cv_unqualified (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index d58e4186704..f0b165e2891 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -176,6 +176,9 @@
 #if !__has_builtin (__reference_converts_from_temporary)
 # error "__has_builtin (__reference_converts_from_temporary) failed"
 #endif
+#if !__has_builtin (__remove_all_extents)
+# error "__has_builtin (__remove_all_extents) failed"
+#endif
 #if !__has_builtin (__remove_cv)
 # error "__has_builtin (__remove_cv) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/remove_all_extents.C b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
new file mode 100644
index 00000000000..60ade2ade7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/remove_all_extents.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__remove_all_extents(int), int));
+SA(__is_same(__remove_all_extents(int[2]), int));
+SA(__is_same(__remove_all_extents(int[2][3]), int));
+SA(__is_same(__remove_all_extents(int[][3]), int));
+SA(__is_same(__remove_all_extents(const int[2][3]), const int));
+SA(__is_same(__remove_all_extents(ClassType), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[2][3]), ClassType));
+SA(__is_same(__remove_all_extents(ClassType[][3]), ClassType));
+SA(__is_same(__remove_all_extents(const ClassType[2][3]), const ClassType));
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 14/26] libstdc++: Optimize std::remove_all_extents compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (12 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
                                         ` (11 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::remove_all_extents by dispatching to the new
__remove_all_extents built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (remove_all_extents): Use
	__remove_all_extents built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 58281c2e38c..5b74e44d0a6 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2111,6 +2111,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// remove_all_extents
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__remove_all_extents)
+  template<typename _Tp>
+    struct remove_all_extents
+    { using type = __remove_all_extents(_Tp); };
+#else
   template<typename _Tp>
     struct remove_all_extents
     { using type = _Tp; };
@@ -2122,6 +2127,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct remove_all_extents<_Tp[]>
     { using type = typename remove_all_extents<_Tp>::type; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_extent
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 15/26] c++: Implement __add_lvalue_reference built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (13 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
                                         ` (10 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_lvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_lvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_LVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_lvalue_reference.
	* g++.dg/ext/add_lvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  6 ++++++
 .../g++.dg/ext/add_lvalue_reference.C         | 21 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 31 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_lvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 933c8bcbe68..9a27dca4ea3 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -48,6 +48,7 @@
 #define DEFTRAIT_TYPE_DEFAULTED
 #endif
 
+DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 07779a79d24..9f2220a1a91 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12920,6 +12920,12 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 
   switch (kind)
     {
+    case CPTK_ADD_LVALUE_REFERENCE:
+      /* [meta.trans.ref].  */
+      if (referenceable_type_p (type1))
+	return cp_build_reference_type (type1, /*rval=*/false);
+      return type1;
+
     case CPTK_ADD_POINTER:
       /* [meta.trans.ptr].  */
       if (VOID_TYPE_P (type1) || referenceable_type_p (type1))
diff --git a/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
new file mode 100644
index 00000000000..8fe1e0300e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_lvalue_reference.C
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_lvalue_reference(int), int&));
+SA(__is_same(__add_lvalue_reference(int&), int&));
+SA(__is_same(__add_lvalue_reference(const int), const int&));
+SA(__is_same(__add_lvalue_reference(int*), int*&));
+SA(__is_same(__add_lvalue_reference(ClassType&), ClassType&));
+SA(__is_same(__add_lvalue_reference(ClassType), ClassType&));
+SA(__is_same(__add_lvalue_reference(int(int)), int(&)(int)));
+SA(__is_same(__add_lvalue_reference(int&&), int&));
+SA(__is_same(__add_lvalue_reference(ClassType&&), ClassType&));
+SA(__is_same(__add_lvalue_reference(void), void));
+SA(__is_same(__add_lvalue_reference(const void), const void));
+SA(__is_same(__add_lvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_lvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_lvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_lvalue_reference(bool(int)), bool(&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index f0b165e2891..a8b2f0fcfd2 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -2,6 +2,9 @@
 // { dg-do compile }
 // Verify that __has_builtin gives the correct answer for C++ built-ins.
 
+#if !__has_builtin (__add_lvalue_reference)
+# error "__has_builtin (__add_lvalue_reference) failed"
+#endif
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (14 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
                                         ` (9 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::add_lvalue_reference by dispatching to the new
__add_lvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_lvalue_reference): Use
	__add_lvalue_reference built-in trait.
	(__add_lvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 5b74e44d0a6..effa3fbcb75 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1157,6 +1157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct __add_lvalue_reference_helper
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_lvalue_reference_helper
     { using type = _Tp; };
@@ -1164,6 +1169,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_lvalue_reference_helper<_Tp, __void_t<_Tp&>>
     { using type = _Tp&; };
+#endif
 
   template<typename _Tp>
     using __add_lval_ref_t = typename __add_lvalue_reference_helper<_Tp>::type;
@@ -1731,9 +1737,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_lvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_lvalue_reference)
+  template<typename _Tp>
+    struct add_lvalue_reference
+    { using type = __add_lvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_lvalue_reference
     { using type = __add_lval_ref_t<_Tp>; };
+#endif
 
   /// add_rvalue_reference
   template<typename _Tp>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 17/26] c++: Implement __add_rvalue_reference built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (15 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
                                         ` (8 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::add_rvalue_reference.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __add_rvalue_reference.
	* semantics.cc (finish_trait_type): Handle
	CPTK_ADD_RVALUE_REFERENCE.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__add_rvalue_reference.
	* g++.dg/ext/add_rvalue_reference.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  6 ++++++
 .../g++.dg/ext/add_rvalue_reference.C         | 20 +++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +++
 4 files changed, 30 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/add_rvalue_reference.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 9a27dca4ea3..173818adf79 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -50,6 +50,7 @@
 
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
+DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9f2220a1a91..fd6d9fc1dc5 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12936,6 +12936,12 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	}
       return type1;
 
+    case CPTK_ADD_RVALUE_REFERENCE:
+      /* [meta.trans.ref].  */
+      if (referenceable_type_p (type1))
+	return cp_build_reference_type (type1, /*rval=*/true);
+      return type1;
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
new file mode 100644
index 00000000000..c92fe6bfa17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/add_rvalue_reference.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__is_same(__add_rvalue_reference(int), int&&));
+SA(__is_same(__add_rvalue_reference(int&&), int&&));
+SA(__is_same(__add_rvalue_reference(int&), int&));
+SA(__is_same(__add_rvalue_reference(const int), const int&&));
+SA(__is_same(__add_rvalue_reference(int*), int*&&));
+SA(__is_same(__add_rvalue_reference(ClassType&&), ClassType&&));
+SA(__is_same(__add_rvalue_reference(ClassType), ClassType&&));
+SA(__is_same(__add_rvalue_reference(int(int)), int(&&)(int)));
+SA(__is_same(__add_rvalue_reference(void), void));
+SA(__is_same(__add_rvalue_reference(const void), const void));
+SA(__is_same(__add_rvalue_reference(bool(int) const), bool(int) const));
+SA(__is_same(__add_rvalue_reference(bool(int) &), bool(int) &));
+SA(__is_same(__add_rvalue_reference(bool(int) const &&), bool(int) const &&));
+SA(__is_same(__add_rvalue_reference(bool(int)), bool(&&)(int)));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index a8b2f0fcfd2..266cb35a368 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -8,6 +8,9 @@
 #if !__has_builtin (__add_pointer)
 # error "__has_builtin (__add_pointer) failed"
 #endif
+#if !__has_builtin (__add_rvalue_reference)
+# error "__has_builtin (__add_rvalue_reference) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (16 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 19/26] c++: Implement __decay built-in trait Ken Matsui
                                         ` (7 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::add_rvalue_reference by dispatching to the new
__add_rvalue_reference built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (add_rvalue_reference): Use
	__add_rvalue_reference built-in trait.
	(__add_rvalue_reference_helper): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index effa3fbcb75..824cad90a25 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1185,6 +1185,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// @cond undocumented
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct __add_rvalue_reference_helper
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp, typename = void>
     struct __add_rvalue_reference_helper
     { using type = _Tp; };
@@ -1192,6 +1197,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct __add_rvalue_reference_helper<_Tp, __void_t<_Tp&&>>
     { using type = _Tp&&; };
+#endif
 
   template<typename _Tp>
     using __add_rval_ref_t = typename __add_rvalue_reference_helper<_Tp>::type;
@@ -1748,9 +1754,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
   /// add_rvalue_reference
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__add_rvalue_reference)
+  template<typename _Tp>
+    struct add_rvalue_reference
+    { using type = __add_rvalue_reference(_Tp); };
+#else
   template<typename _Tp>
     struct add_rvalue_reference
     { using type = __add_rval_ref_t<_Tp>; };
+#endif
 
 #if __cplusplus > 201103L
   /// Alias template for remove_reference
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 19/26] c++: Implement __decay built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (17 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
                                         ` (6 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::decay.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __decay.
	* semantics.cc (finish_trait_type): Handle CPTK_DECAY.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __decay.
	* g++.dg/ext/decay.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 12 ++++++++++++
 gcc/testsuite/g++.dg/ext/decay.C         | 22 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 4 files changed, 38 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/decay.C

diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 173818adf79..2d1cb7c227c 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -51,6 +51,7 @@
 DEFTRAIT_TYPE (ADD_LVALUE_REFERENCE, "__add_lvalue_reference", 1)
 DEFTRAIT_TYPE (ADD_POINTER, "__add_pointer", 1)
 DEFTRAIT_TYPE (ADD_RVALUE_REFERENCE, "__add_rvalue_reference", 1)
+DEFTRAIT_TYPE (DECAY, "__decay", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_ASSIGN, "__has_nothrow_assign", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_CONSTRUCTOR, "__has_nothrow_constructor", 1)
 DEFTRAIT_EXPR (HAS_NOTHROW_COPY, "__has_nothrow_copy", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fd6d9fc1dc5..c498191a3c8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12942,6 +12942,18 @@ finish_trait_type (cp_trait_kind kind, tree type1, tree type2,
 	return cp_build_reference_type (type1, /*rval=*/true);
       return type1;
 
+    case CPTK_DECAY:
+      if (TYPE_REF_P (type1))
+	type1 = TREE_TYPE (type1);
+
+      if (TREE_CODE (type1) == ARRAY_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, TREE_TYPE (type1), type2,
+				  complain);
+      else if (TREE_CODE (type1) == FUNCTION_TYPE)
+	return finish_trait_type (CPTK_ADD_POINTER, type1, type2, complain);
+      else
+	return cv_unqualified (type1);
+
     case CPTK_REMOVE_ALL_EXTENTS:
       return strip_array_types (type1);
 
diff --git a/gcc/testsuite/g++.dg/ext/decay.C b/gcc/testsuite/g++.dg/ext/decay.C
new file mode 100644
index 00000000000..8adedfeefe6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/decay.C
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+// Positive tests.
+using test1_type = __decay(bool);
+SA(__is_same(test1_type, bool));
+
+// NB: DR 705.
+using test2_type = __decay(const int);
+SA(__is_same(test2_type, int));
+
+using test3_type = __decay(int[4]);
+SA(__is_same(test3_type, __remove_extent(int[4])*));
+
+using fn_type = void ();
+using test4_type = __decay(fn_type);
+SA(__is_same(test4_type, __add_pointer(fn_type)));
+
+using cfn_type = void () const;
+using test5_type = __decay(cfn_type);
+SA(__is_same(test5_type, cfn_type));
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index 266cb35a368..d2e6a5b4dcb 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -32,6 +32,9 @@
 #if !__has_builtin (__builtin_source_location)
 # error "__has_builtin (__builtin_source_location) failed"
 #endif
+#if !__has_builtin (__decay)
+# error "__has_builtin (__decay) failed"
+#endif
 #if !__has_builtin (__has_nothrow_assign)
 # error "__has_builtin (__has_nothrow_assign) failed"
 #endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 20/26] libstdc++: Optimize std::decay compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (18 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 19/26] c++: Implement __decay built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 21/26] c++: Implement __array_rank built-in trait Ken Matsui
                                         ` (5 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::decay
by dispatching to the new __decay built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (decay): Use __decay built-in trait.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 824cad90a25..4cc587d4e08 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2316,6 +2316,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @cond undocumented
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__decay)
+  template<typename _Tp>
+    struct decay
+    { using type = __decay(_Tp); };
+#else
   // Decay trait for arrays and functions, used for perfect forwarding
   // in make_pair, make_tuple, etc.
   template<typename _Up>
@@ -2347,6 +2352,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct decay<_Tp&&>
     { using type = typename __decay_selector<_Tp>::type; };
+#endif
 
   /// @cond undocumented
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 21/26] c++: Implement __array_rank built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (19 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
                                         ` (4 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::rank.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __array_rank.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_RANK.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __array_rank.
	* g++.dg/ext/rank.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |  3 +++
 gcc/cp/cp-trait.def                      |  1 +
 gcc/cp/semantics.cc                      | 24 +++++++++++++++++++++---
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |  3 +++
 gcc/testsuite/g++.dg/ext/rank.C          | 24 ++++++++++++++++++++++++
 5 files changed, 52 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/rank.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d4cc8850486..c28d7bf428e 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3871,6 +3871,9 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_VOLATILE:
       inform (loc, "  %qT is not a volatile type", t1);
       break;
+    case CPTK_RANK:
+      inform (loc, "  %qT cannot yield a rank", t1);
+      break;
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
       inform (loc, "  %qT is not a reference that binds to a temporary "
 	      "object of type %qT (direct-initialization)", t1, t2);
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 2d1cb7c227c..b1c875a6e7d 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -99,6 +99,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1)
 DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1)
 DEFTRAIT_EXPR (IS_UNION, "__is_union", 1)
 DEFTRAIT_EXPR (IS_VOLATILE, "__is_volatile", 1)
+DEFTRAIT_EXPR (RANK, "__array_rank", 1)
 DEFTRAIT_EXPR (REF_CONSTRUCTS_FROM_TEMPORARY, "__reference_constructs_from_temporary", 2)
 DEFTRAIT_EXPR (REF_CONVERTS_FROM_TEMPORARY, "__reference_converts_from_temporary", 2)
 DEFTRAIT_TYPE (REMOVE_ALL_EXTENTS, "__remove_all_extents", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c498191a3c8..a293e0fc23e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12642,6 +12642,10 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
+    /* __array_rank is handled in finish_trait_expr. */
+    case CPTK_RANK:
+      gcc_unreachable ();
+
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
     case CPTK_##CODE:
 #include "cp-trait.def"
@@ -12765,7 +12769,10 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
   if (processing_template_decl)
     {
       tree trait_expr = make_node (TRAIT_EXPR);
-      TREE_TYPE (trait_expr) = boolean_type_node;
+      if (kind == CPTK_RANK)
+	TREE_TYPE (trait_expr) = size_type_node;
+      else
+	TREE_TYPE (trait_expr) = boolean_type_node;
       TRAIT_EXPR_TYPE1 (trait_expr) = type1;
       TRAIT_EXPR_TYPE2 (trait_expr) = type2;
       TRAIT_EXPR_KIND (trait_expr) = kind;
@@ -12858,6 +12865,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_UNBOUNDED_ARRAY:
     case CPTK_IS_UNION:
     case CPTK_IS_VOLATILE:
+    case CPTK_RANK:
       break;
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
@@ -12889,8 +12897,18 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  tree val = (trait_expr_value (kind, type1, type2)
-	      ? boolean_true_node : boolean_false_node);
+  tree val;
+  if (kind == CPTK_RANK)
+    {
+      size_t __array_rank = 0;
+      for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+	++__array_rank;
+      val = build_int_cst (size_type_node, __array_rank);
+    }
+  else
+    val = (trait_expr_value (kind, type1, type2)
+	   ? boolean_true_node : boolean_false_node);
+
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index d2e6a5b4dcb..bcaa56a7bc5 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -11,6 +11,9 @@
 #if !__has_builtin (__add_rvalue_reference)
 # error "__has_builtin (__add_rvalue_reference) failed"
 #endif
+#if !__has_builtin (__array_rank)
+# error "__has_builtin (__array_rank) failed"
+#endif
 #if !__has_builtin (__builtin_addressof)
 # error "__has_builtin (__builtin_addressof) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/rank.C b/gcc/testsuite/g++.dg/ext/rank.C
new file mode 100644
index 00000000000..0ab2f235036
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/rank.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <cstddef>
+
+#define SA(X) static_assert((X),#X)
+
+class ClassType { };
+
+SA(__array_rank(int) == 0);
+SA(__array_rank(int[2]) == 1);
+SA(__array_rank(int[][4]) == 2);
+SA(__array_rank(int[2][2][4][4][6][6]) == 6);
+SA(__array_rank(ClassType) == 0);
+SA(__array_rank(ClassType[2]) == 1);
+SA(__array_rank(ClassType[][4]) == 2);
+SA(__array_rank(ClassType[2][2][4][4][6][6]) == 6);
+
+template<class T> void f(T) = delete;
+void f(size_t);
+
+template<class T>
+void g() { f(__array_rank(T)); }
+
+template void g<int>();
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 22/26] libstdc++: Optimize std::rank compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (20 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 21/26] c++: Implement __array_rank built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
                                         ` (3 subsequent siblings)
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::rank
by dispatching to the new __array_rank built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (rank): Use __array_rank built-in
	trait.
	(rank_v): Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 4cc587d4e08..e9313205550 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1473,6 +1473,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 
   /// rank
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__array_rank)
+  template<typename _Tp>
+    struct rank
+    : public integral_constant<std::size_t, __array_rank(_Tp)> { };
+#else
   template<typename>
     struct rank
     : public integral_constant<std::size_t, 0> { };
@@ -1484,6 +1489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct rank<_Tp[]>
     : public integral_constant<std::size_t, 1 + rank<_Tp>::value> { };
+#endif
 
   /// extent
   template<typename, unsigned _Uint = 0>
@@ -3583,12 +3589,17 @@ template <typename _Tp>
 template <typename _Tp>
   inline constexpr size_t alignment_of_v = alignment_of<_Tp>::value;
 
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__array_rank)
+template <typename _Tp>
+  inline constexpr size_t rank_v = __array_rank(_Tp);
+#else
 template <typename _Tp>
   inline constexpr size_t rank_v = 0;
 template <typename _Tp, size_t _Size>
   inline constexpr size_t rank_v<_Tp[_Size]> = 1 + rank_v<_Tp>;
 template <typename _Tp>
   inline constexpr size_t rank_v<_Tp[]> = 1 + rank_v<_Tp>;
+#endif
 
 template <typename _Tp, unsigned _Idx = 0>
   inline constexpr size_t extent_v = 0;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (21 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:38                         ` Jason Merrill
  2024-05-03  2:09                         ` [PATCH v19 " Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
                                         ` (2 subsequent siblings)
  25 siblings, 2 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 134 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 723 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index b1c875a6e7d..4e420d5390a 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1938ada0268..83dc20e1130 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..faf932258e6 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,140 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be"
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_base_of_datum =
+	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	 && NON_UNION_CLASS_TYPE_P (datum_type)
+	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							datum_type)
+	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+      tree datum_expr = build_trait_object (datum_type);
+      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index a293e0fc23e..55c2ec4b5b1 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index bcaa56a7bc5..d1c66267855 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -107,6 +107,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 24/26] libstdc++: Optimize std::is_invocable compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (22 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of std::is_invocable
by dispatching to the new __is_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_invocable): Use __is_invocable
	built-in trait.
	* testsuite/20_util/is_invocable/incomplete_args_neg.cc: Handle
	the new error from __is_invocable.
	* testsuite/20_util/is_invocable/incomplete_neg.cc: Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../testsuite/20_util/is_invocable/incomplete_args_neg.cc     | 1 +
 libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index e9313205550..502032787bd 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3239,7 +3239,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_invocable)
+    : public __bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+#else
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
index a575750f9e9..9619129b817 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
index 05848603555..b478ebce815 100644
--- a/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 25/26] c++: Implement __is_nothrow_invocable built-in trait
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (23 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  2024-05-02 20:12                       ` [PATCH v18 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_nothrow_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_nothrow_invocable.
	* constraint.cc (diagnose_trait_expr): Handle
	CPTK_IS_NOTHROW_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of
	__is_nothrow_invocable.
	* g++.dg/ext/is_nothrow_invocable.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                          |  6 ++
 gcc/cp/cp-trait.def                           |  1 +
 gcc/cp/semantics.cc                           |  5 ++
 gcc/testsuite/g++.dg/ext/has-builtin-1.C      |  3 +
 .../g++.dg/ext/is_nothrow_invocable.C         | 62 +++++++++++++++++++
 5 files changed, 77 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 6d14ef7dcc7..8679d3ce658 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3825,6 +3825,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
 	  inform (loc, "  %qT is not nothrow convertible from %qE", t2, t1);
       break;
+    case CPTK_IS_NOTHROW_INVOCABLE:
+	if (!t2)
+	  inform (loc, "  %qT is not nothrow invocable", t1);
+	else
+	  inform (loc, "  %qT is not nothrow invocable by %qE", t1, t2);
+	break;
     case CPTK_IS_OBJECT:
       inform (loc, "  %qT is not an object type", t1);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index 4e420d5390a..0721ecdf3f0 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -84,6 +84,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1)
 DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2)
 DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1)
 DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2)
+DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1)
 DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1)
 DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2)
 DEFTRAIT_EXPR (IS_POD, "__is_pod", 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 55c2ec4b5b1..5d1acba7bc8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12588,6 +12588,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONVERTIBLE:
       return is_nothrow_convertible (type1, type2);
 
+    case CPTK_IS_NOTHROW_INVOCABLE:
+      return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none);
+
     case CPTK_IS_OBJECT:
       return object_type_p (type1);
 
@@ -12723,6 +12726,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_IS_INVOCABLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12828,6 +12832,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
+    case CPTK_IS_NOTHROW_INVOCABLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index d1c66267855..65740a11800 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -134,6 +134,9 @@
 #if !__has_builtin (__is_nothrow_convertible)
 # error "__has_builtin (__is_nothrow_convertible) failed"
 #endif
+#if !__has_builtin (__is_nothrow_invocable)
+# error "__has_builtin (__is_nothrow_invocable) failed"
+#endif
 #if !__has_builtin (__is_object)
 # error "__has_builtin (__is_object) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
new file mode 100644
index 00000000000..2f9b40e5538
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_nothrow_invocable.C
@@ -0,0 +1,62 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type = void(*)();
+SA( ! __is_nothrow_invocable(func_type) );
+
+#if __cpp_noexcept_function_type
+using func_type_nt = void(*)() noexcept;
+SA(   __is_nothrow_invocable(func_type_nt) );
+#endif
+
+struct X { };
+using mem_type = int X::*;
+
+SA( ! __is_nothrow_invocable(mem_type) );
+SA( ! __is_nothrow_invocable(mem_type, int) );
+SA( ! __is_nothrow_invocable(mem_type, int&) );
+SA(   __is_nothrow_invocable(mem_type, X&) );
+
+using memfun_type = int (X::*)();
+
+SA( ! __is_nothrow_invocable(memfun_type) );
+SA( ! __is_nothrow_invocable(memfun_type, int) );
+SA( ! __is_nothrow_invocable(memfun_type, int&) );
+SA( ! __is_nothrow_invocable(memfun_type, X&) );
+SA( ! __is_nothrow_invocable(memfun_type, X*) );
+
+#if __cpp_noexcept_function_type
+using memfun_type_nt = int (X::*)() noexcept;
+
+SA( ! __is_nothrow_invocable(memfun_type_nt) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int) );
+SA( ! __is_nothrow_invocable(memfun_type_nt, int&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X&) );
+SA(   __is_nothrow_invocable(memfun_type_nt, X*) );
+#endif
+
+struct F {
+  int& operator()();
+  long& operator()() const noexcept;
+  short& operator()(int) &&;
+  char& operator()(int) const& noexcept;
+private:
+  void operator()(int, int) noexcept;
+};
+using CF = const F;
+
+SA( ! __is_nothrow_invocable(F ) );
+SA(   __is_nothrow_invocable(CF) );
+
+SA( ! __is_nothrow_invocable(F,   int) );
+SA(   __is_nothrow_invocable(F&,  int) );
+
+SA(   __is_nothrow_invocable(CF,   int) );
+SA(   __is_nothrow_invocable(CF&,  int) );
+SA( ! __is_nothrow_invocable(F, int, int) );
+
+struct FX {
+  X operator()() const noexcept { return {}; }
+};
+SA(   __is_nothrow_invocable(FX) );
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v18 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance
  2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
                                         ` (24 preceding siblings ...)
  2024-05-02 20:12                       ` [PATCH v18 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
@ 2024-05-02 20:12                       ` Ken Matsui
  25 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch optimizes the compilation performance of
std::is_nothrow_invocable by dispatching to the new
__is_nothrow_invocable built-in trait.

libstdc++-v3/ChangeLog:

	* include/std/type_traits (is_nothrow_invocable): Use
	__is_nothrow_invocable built-in trait.
	* testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc:
	Handle the new error from __is_nothrow_invocable.
	* testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc:
	Likewise.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 libstdc++-v3/include/std/type_traits                          | 4 ++++
 .../20_util/is_nothrow_invocable/incomplete_args_neg.cc       | 1 +
 .../testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 502032787bd..a046560c178 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -3269,8 +3269,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// std::is_nothrow_invocable
   template<typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable
+#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_nothrow_invocable)
+    : public __bool_constant<__is_nothrow_invocable(_Fn, _ArgTypes...)>
+#else
     : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>,
 	     __call_is_nothrow_<_Fn, _ArgTypes...>>::type
+#endif
     {
       static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),
 	"_Fn must be a complete class or an unbounded array");
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
index 3c225883eaf..3f8542dd366 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_args_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
index 5a728bfa03b..d3bdf08448b 100644
--- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/incomplete_neg.cc
@@ -18,6 +18,7 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-error "must be a complete class" "" { target *-*-* } 0 }
+// { dg-prune-output "invalid use of incomplete type" }
 
 #include <type_traits>
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: Trait built-in naming convention
  2024-05-02 20:09                                             ` Jakub Jelinek
@ 2024-05-02 20:15                                               ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:15 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Jason Merrill, Iain Sandoe, Marek Polacek, Ville Voutilainen,
	Patrick Palka, Ken Matsui, GCC Patches, libstdc++

On Thu, May 2, 2024 at 1:09 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Thu, May 02, 2024 at 12:52:59PM -0700, Ken Matsui wrote:
> > > This seems to be the prevailing sentiment, so let's continue that way.
> > > Thanks for the input.
> >
> > I actually found that we have two built-in type traits prefixed with
> > __builtin: __builtin_is_corresponding_member and
>
> That is a FE builtin, not a trait, and is very much different from the
> __is_* traits, is varargs with extra processing, I don't think any of
> the normal traits accepts pointer to members.
>
> > __builtin_is_pointer_interconvertible_with_class.  Do we want to
> > update these to use __ instead for consistency?
>
> No, I think we want to keep them as is.

I see.  Thank you.

>
>         Jakub
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:12                       ` [PATCH v18 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
@ 2024-05-02 20:38                         ` Jason Merrill
  2024-05-02 20:45                           ` Marek Polacek
                                             ` (2 more replies)
  2024-05-03  2:09                         ` [PATCH v19 " Ken Matsui
  1 sibling, 3 replies; 244+ messages in thread
From: Jason Merrill @ 2024-05-02 20:38 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/2/24 16:12, Ken Matsui wrote:
> This patch implements built-in trait for std::is_invocable.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_invocable.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 	* cp-tree.h (build_invoke): New function.
> 	* method.cc (build_invoke): New function.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> 	* g++.dg/ext/is_invocable1.C: New test.
> 	* g++.dg/ext/is_invocable2.C: New test.
> 	* g++.dg/ext/is_invocable3.C: New test.
> 	* g++.dg/ext/is_invocable4.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |   6 +
>   gcc/cp/cp-trait.def                      |   1 +
>   gcc/cp/cp-tree.h                         |   2 +
>   gcc/cp/method.cc                         | 134 +++++++++
>   gcc/cp/semantics.cc                      |   5 +
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
>   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
>   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
>   10 files changed, 723 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index c28d7bf428e..6d14ef7dcc7 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_FUNCTION:
>         inform (loc, "  %qT is not a function", t1);
>         break;
> +    case CPTK_IS_INVOCABLE:
> +      if (!t2)
> +    inform (loc, "  %qT is not invocable", t1);
> +      else
> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> +      break;
>       case CPTK_IS_LAYOUT_COMPATIBLE:
>         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index b1c875a6e7d..4e420d5390a 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
>   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
>   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
>   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
>   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
>   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
>   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 1938ada0268..83dc20e1130 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
>   extern tree get_default_ctor			(tree);
>   extern tree get_dtor				(tree, tsubst_flags_t);
>   extern tree build_stub_object			(tree);
> +extern tree build_invoke			(tree, const_tree,
> +						 tsubst_flags_t);
>   extern tree strip_inheriting_ctors		(tree);
>   extern tree inherited_ctor_binfo		(tree);
>   extern bool base_ctor_omit_inherited_parms	(tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 08a3d34fb01..faf932258e6 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
>     return build_stub_object (type);
>   }
>   
> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> +   given is not invocable, returns error_mark_node.  */
> +
> +tree
> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> +{
> +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> +    return error_mark_node;
> +
> +  gcc_assert (TYPE_P (fn_type));
> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> +
> +  /* Access check is required to determine if the given is invocable.  */
> +  deferring_access_check_sentinel acs (dk_no_deferred);
> +
> +  /* INVOKE is an unevaluated context.  */
> +  cp_unevaluated cp_uneval_guard;
> +
> +  bool is_ptrdatamem;
> +  bool is_ptrmemfunc;
> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> +    {
> +      tree deref_fn_type = TREE_TYPE (fn_type);
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> +
> +      /* Dereference fn_type if it is a pointer to member.  */
> +      if (is_ptrdatamem || is_ptrmemfunc)
> +	fn_type = deref_fn_type;
> +    }
> +  else
> +    {
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> +    }
> +
> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to data member type %qT can only be invoked with "
> +	       "one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to member function type %qT must be invoked with "
> +	       "at least one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  /* Construct an expression of a pointer to member.  */
> +  tree ptrmem_expr;
> +  if (is_ptrdatamem || is_ptrmemfunc)
> +    {
> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> +
> +      /* datum must be a class type or a reference/pointer to a class type.  */
> +      if (!(CLASS_TYPE_P (datum_type)
> +	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> +		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> +	{
> +	  if (complain & tf_error)
> +	    error ("first argument type %qT of a pointer to member must be"
> +		   "a class type or a reference/pointer to a class type",
> +		   datum_type);
> +	  return error_mark_node;
> +	}
> +
> +      bool is_refwrap = false;
> +      if (CLASS_TYPE_P (datum_type))
> +	{
> +	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> +	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> +	  if (decl_in_std_namespace_p (datum_decl))
> +	    {
> +	      const_tree name = DECL_NAME (datum_decl);
> +	      if (name && (id_equal (name, "reference_wrapper")))
> +		{
> +		  /* Retrieve T from std::reference_wrapper<T>,
> +		     i.e., decltype(datum.get()).  */
> +		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> +		  is_refwrap = true;
> +		}
> +	    }
> +	}
> +
> +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> +      const bool ptrmem_is_base_of_datum =
> +	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> +	 && NON_UNION_CLASS_TYPE_P (datum_type)

Why NON_UNION?  That doesn't seem to be based on anything in the standard.

This check and the one for reference_wrapper need to ignore 
REFERENCE_TYPE; INVOKE is defined in terms of expressions, and the type 
of an expression is never a reference.

> +	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> +							datum_type)
> +	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> +
> +      tree datum_expr = build_trait_object (datum_type);
> +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))

...so we shouldn't need to check TYPE_REF_P here.

Jason

> +	/* 1.3 & 1.6: Try to dereference datum_expr.  */
> +	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> +					   RO_UNARY_STAR, NULL_TREE, complain);
> +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> +
> +      tree fn_expr = build_trait_object (fn_type);
> +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> +
> +      if (error_operand_p (ptrmem_expr))
> +	return error_mark_node;
> +
> +      if (is_ptrdatamem)
> +	return ptrmem_expr;
> +    }
> +
> +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> +     function, the first argument, which is the object, is not arguments to
> +     the function.  */
> +  releasing_vec args;
> +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> +    {
> +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> +      tree arg = build_trait_object (arg_type);
> +      vec_safe_push (args, arg);
> +    }
> +
> +  tree invoke_expr;
> +  if (is_ptrmemfunc)
> +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> +						   complain);
> +  else  /* 1.7.  */
> +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> +				    false, complain);
> +  return invoke_expr;
> +}
> +
>   /* Determine which function will be called when looking up NAME in TYPE,
>      called with a single ARGTYPE argument, or no argument if ARGTYPE is
>      null.  FLAGS and COMPLAIN are as for build_new_method_call.


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:38                         ` Jason Merrill
@ 2024-05-02 20:45                           ` Marek Polacek
  2024-05-02 20:50                             ` Ken Matsui
  2024-05-02 20:47                           ` Ken Matsui
  2024-05-02 20:54                           ` Ken Matsui
  2 siblings, 1 reply; 244+ messages in thread
From: Marek Polacek @ 2024-05-02 20:45 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Thu, May 02, 2024 at 04:38:16PM -0400, Jason Merrill wrote:
> On 5/2/24 16:12, Ken Matsui wrote:
> > This patch implements built-in trait for std::is_invocable.
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* cp-trait.def: Define __is_invocable.
> > 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> > 	* semantics.cc (trait_expr_value): Likewise.
> > 	(finish_trait_expr): Likewise.
> > 	* cp-tree.h (build_invoke): New function.
> > 	* method.cc (build_invoke): New function.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> > 	* g++.dg/ext/is_invocable1.C: New test.
> > 	* g++.dg/ext/is_invocable2.C: New test.
> > 	* g++.dg/ext/is_invocable3.C: New test.
> > 	* g++.dg/ext/is_invocable4.C: New test.
> > 
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |   6 +
> >   gcc/cp/cp-trait.def                      |   1 +
> >   gcc/cp/cp-tree.h                         |   2 +
> >   gcc/cp/method.cc                         | 134 +++++++++
> >   gcc/cp/semantics.cc                      |   5 +
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >   10 files changed, 723 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> > 
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index c28d7bf428e..6d14ef7dcc7 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_FUNCTION:
> >         inform (loc, "  %qT is not a function", t1);
> >         break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> >         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >         break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index b1c875a6e7d..4e420d5390a 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 1938ada0268..83dc20e1130 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
> >   extern tree get_default_ctor			(tree);
> >   extern tree get_dtor				(tree, tsubst_flags_t);
> >   extern tree build_stub_object			(tree);
> > +extern tree build_invoke			(tree, const_tree,
> > +						 tsubst_flags_t);
> >   extern tree strip_inheriting_ctors		(tree);
> >   extern tree inherited_ctor_binfo		(tree);
> >   extern bool base_ctor_omit_inherited_parms	(tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 08a3d34fb01..faf932258e6 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
> >     return build_stub_object (type);
> >   }
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +	fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    {
> > +      if (complain & tf_error)
> > +	error ("pointer to data member type %qT can only be invoked with "
> > +	       "one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    {
> > +      if (complain & tf_error)
> > +	error ("pointer to member function type %qT must be invoked with "
> > +	       "at least one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (!(CLASS_TYPE_P (datum_type)
> > +	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> > +	{
> > +	  if (complain & tf_error)
> > +	    error ("first argument type %qT of a pointer to member must be"
> > +		   "a class type or a reference/pointer to a class type",

There's a space missing before "a class".  But we'll never see these errors
anyway, because build_invoke will only ever be called with complain=tf_none,
yes?

Functions like constructible_expr / assignable_expr don't have a complain
parameter, so do we need it here?

> > +		   datum_type);
> > +	  return error_mark_node;
> > +	}
> > +
> > +      bool is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +	{
> > +	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +	  if (decl_in_std_namespace_p (datum_decl))
> > +	    {
> > +	      const_tree name = DECL_NAME (datum_decl);
> > +	      if (name && (id_equal (name, "reference_wrapper")))
> > +		{
> > +		  /* Retrieve T from std::reference_wrapper<T>,
> > +		     i.e., decltype(datum.get()).  */
> > +		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +		  is_refwrap = true;
> > +		}
> > +	    }
> > +	}
> > +
> > +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > +      const bool ptrmem_is_base_of_datum =
> > +	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > +	 && NON_UNION_CLASS_TYPE_P (datum_type)
> 
> Why NON_UNION?  That doesn't seem to be based on anything in the standard.
> 
> This check and the one for reference_wrapper need to ignore REFERENCE_TYPE;
> INVOKE is defined in terms of expressions, and the type of an expression is
> never a reference.
> 
> > +	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > +							datum_type)
> > +	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
> > +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
> 
> ...so we shouldn't need to check TYPE_REF_P here.
> 
> Jason
> 
> > +	/* 1.3 & 1.6: Try to dereference datum_expr.  */
> > +	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > +					   RO_UNARY_STAR, NULL_TREE, complain);
> > +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> > +
> > +      tree fn_expr = build_trait_object (fn_type);
> > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > +
> > +      if (error_operand_p (ptrmem_expr))
> > +	return error_mark_node;
> > +
> > +      if (is_ptrdatamem)
> > +	return ptrmem_expr;
> > +    }
> > +
> > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > +     function, the first argument, which is the object, is not arguments to
> > +     the function.  */
> > +  releasing_vec args;
> > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > +    {
> > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > +      tree arg = build_trait_object (arg_type);
> > +      vec_safe_push (args, arg);
> > +    }
> > +
> > +  tree invoke_expr;
> > +  if (is_ptrmemfunc)
> > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > +						   complain);
> > +  else  /* 1.7.  */
> > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > +				    false, complain);
> > +  return invoke_expr;
> > +}
> > +
> >   /* Determine which function will be called when looking up NAME in TYPE,
> >      called with a single ARGTYPE argument, or no argument if ARGTYPE is
> >      null.  FLAGS and COMPLAIN are as for build_new_method_call.
> 

Marek


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:38                         ` Jason Merrill
  2024-05-02 20:45                           ` Marek Polacek
@ 2024-05-02 20:47                           ` Ken Matsui
  2024-05-03  2:43                             ` Jason Merrill
  2024-05-02 20:54                           ` Ken Matsui
  2 siblings, 1 reply; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:47 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 1:38 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 16:12, Ken Matsui wrote:
> > This patch implements built-in trait for std::is_invocable.
> >
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __is_invocable.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >       * cp-tree.h (build_invoke): New function.
> >       * method.cc (build_invoke): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >       * g++.dg/ext/is_invocable1.C: New test.
> >       * g++.dg/ext/is_invocable2.C: New test.
> >       * g++.dg/ext/is_invocable3.C: New test.
> >       * g++.dg/ext/is_invocable4.C: New test.
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |   6 +
> >   gcc/cp/cp-trait.def                      |   1 +
> >   gcc/cp/cp-tree.h                         |   2 +
> >   gcc/cp/method.cc                         | 134 +++++++++
> >   gcc/cp/semantics.cc                      |   5 +
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >   10 files changed, 723 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index c28d7bf428e..6d14ef7dcc7 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_FUNCTION:
> >         inform (loc, "  %qT is not a function", t1);
> >         break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> >         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >         break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index b1c875a6e7d..4e420d5390a 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 1938ada0268..83dc20e1130 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7338,6 +7338,8 @@ extern tree get_copy_assign                     (tree);
> >   extern tree get_default_ctor                        (tree);
> >   extern tree get_dtor                                (tree, tsubst_flags_t);
> >   extern tree build_stub_object                       (tree);
> > +extern tree build_invoke                     (tree, const_tree,
> > +                                              tsubst_flags_t);
> >   extern tree strip_inheriting_ctors          (tree);
> >   extern tree inherited_ctor_binfo            (tree);
> >   extern bool base_ctor_omit_inherited_parms  (tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 08a3d34fb01..faf932258e6 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
> >     return build_stub_object (type);
> >   }
> >
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +     fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to data member type %qT can only be invoked with "
> > +            "one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to member function type %qT must be invoked with "
> > +            "at least one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (!(CLASS_TYPE_P (datum_type)
> > +         || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +             && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> > +     {
> > +       if (complain & tf_error)
> > +         error ("first argument type %qT of a pointer to member must be"
> > +                "a class type or a reference/pointer to a class type",
> > +                datum_type);
> > +       return error_mark_node;
> > +     }
> > +
> > +      bool is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +     {
> > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +       if (decl_in_std_namespace_p (datum_decl))
> > +         {
> > +           const_tree name = DECL_NAME (datum_decl);
> > +           if (name && (id_equal (name, "reference_wrapper")))
> > +             {
> > +               /* Retrieve T from std::reference_wrapper<T>,
> > +                  i.e., decltype(datum.get()).  */
> > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +               is_refwrap = true;
> > +             }
> > +         }
> > +     }
> > +
> > +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > +      const bool ptrmem_is_base_of_datum =
> > +     (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > +      && NON_UNION_CLASS_TYPE_P (datum_type)
>
> Why NON_UNION?  That doesn't seem to be based on anything in the standard.

This comes from the __is_base_of implementation in semantics.cc:

    case CPTK_IS_BASE_OF:
      return (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
      && (same_type_ignoring_top_level_qualifiers_p (type1, type2)
  || DERIVED_FROM_P (type1, type2)));

>
> This check and the one for reference_wrapper need to ignore
> REFERENCE_TYPE; INVOKE is defined in terms of expressions, and the type
> of an expression is never a reference.
>
> > +      && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > +                                                     datum_type)
> > +          || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
> > +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
>
> ...so we shouldn't need to check TYPE_REF_P here.
>
> Jason
>
> > +     /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > +     datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > +                                        RO_UNARY_STAR, NULL_TREE, complain);
> > +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> > +
> > +      tree fn_expr = build_trait_object (fn_type);
> > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > +
> > +      if (error_operand_p (ptrmem_expr))
> > +     return error_mark_node;
> > +
> > +      if (is_ptrdatamem)
> > +     return ptrmem_expr;
> > +    }
> > +
> > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > +     function, the first argument, which is the object, is not arguments to
> > +     the function.  */
> > +  releasing_vec args;
> > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > +    {
> > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > +      tree arg = build_trait_object (arg_type);
> > +      vec_safe_push (args, arg);
> > +    }
> > +
> > +  tree invoke_expr;
> > +  if (is_ptrmemfunc)
> > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > +                                                complain);
> > +  else  /* 1.7.  */
> > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > +                                 false, complain);
> > +  return invoke_expr;
> > +}
> > +
> >   /* Determine which function will be called when looking up NAME in TYPE,
> >      called with a single ARGTYPE argument, or no argument if ARGTYPE is
> >      null.  FLAGS and COMPLAIN are as for build_new_method_call.
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:45                           ` Marek Polacek
@ 2024-05-02 20:50                             ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:50 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Jason Merrill, Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 1:45 PM Marek Polacek <polacek@redhat.com> wrote:
>
> On Thu, May 02, 2024 at 04:38:16PM -0400, Jason Merrill wrote:
> > On 5/2/24 16:12, Ken Matsui wrote:
> > > This patch implements built-in trait for std::is_invocable.
> > >
> > > gcc/cp/ChangeLog:
> > >
> > >     * cp-trait.def: Define __is_invocable.
> > >     * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> > >     * semantics.cc (trait_expr_value): Likewise.
> > >     (finish_trait_expr): Likewise.
> > >     * cp-tree.h (build_invoke): New function.
> > >     * method.cc (build_invoke): New function.
> > >
> > > gcc/testsuite/ChangeLog:
> > >
> > >     * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> > >     * g++.dg/ext/is_invocable1.C: New test.
> > >     * g++.dg/ext/is_invocable2.C: New test.
> > >     * g++.dg/ext/is_invocable3.C: New test.
> > >     * g++.dg/ext/is_invocable4.C: New test.
> > >
> > > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > > ---
> > >   gcc/cp/constraint.cc                     |   6 +
> > >   gcc/cp/cp-trait.def                      |   1 +
> > >   gcc/cp/cp-tree.h                         |   2 +
> > >   gcc/cp/method.cc                         | 134 +++++++++
> > >   gcc/cp/semantics.cc                      |   5 +
> > >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> > >   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> > >   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> > >   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> > >   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> > >   10 files changed, 723 insertions(+)
> > >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> > >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> > >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> > >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> > >
> > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > > index c28d7bf428e..6d14ef7dcc7 100644
> > > --- a/gcc/cp/constraint.cc
> > > +++ b/gcc/cp/constraint.cc
> > > @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> > >       case CPTK_IS_FUNCTION:
> > >         inform (loc, "  %qT is not a function", t1);
> > >         break;
> > > +    case CPTK_IS_INVOCABLE:
> > > +      if (!t2)
> > > +    inform (loc, "  %qT is not invocable", t1);
> > > +      else
> > > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > > +      break;
> > >       case CPTK_IS_LAYOUT_COMPATIBLE:
> > >         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> > >         break;
> > > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > > index b1c875a6e7d..4e420d5390a 100644
> > > --- a/gcc/cp/cp-trait.def
> > > +++ b/gcc/cp/cp-trait.def
> > > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> > >   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> > >   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> > >   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> > >   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> > >   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> > >   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > > index 1938ada0268..83dc20e1130 100644
> > > --- a/gcc/cp/cp-tree.h
> > > +++ b/gcc/cp/cp-tree.h
> > > @@ -7338,6 +7338,8 @@ extern tree get_copy_assign                   (tree);
> > >   extern tree get_default_ctor                      (tree);
> > >   extern tree get_dtor                              (tree, tsubst_flags_t);
> > >   extern tree build_stub_object                     (tree);
> > > +extern tree build_invoke                   (tree, const_tree,
> > > +                                            tsubst_flags_t);
> > >   extern tree strip_inheriting_ctors                (tree);
> > >   extern tree inherited_ctor_binfo          (tree);
> > >   extern bool base_ctor_omit_inherited_parms        (tree);
> > > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > > index 08a3d34fb01..faf932258e6 100644
> > > --- a/gcc/cp/method.cc
> > > +++ b/gcc/cp/method.cc
> > > @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
> > >     return build_stub_object (type);
> > >   }
> > > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > > +   given is not invocable, returns error_mark_node.  */
> > > +
> > > +tree
> > > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > > +{
> > > +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> > > +    return error_mark_node;
> > > +
> > > +  gcc_assert (TYPE_P (fn_type));
> > > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > > +
> > > +  /* Access check is required to determine if the given is invocable.  */
> > > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > > +
> > > +  /* INVOKE is an unevaluated context.  */
> > > +  cp_unevaluated cp_uneval_guard;
> > > +
> > > +  bool is_ptrdatamem;
> > > +  bool is_ptrmemfunc;
> > > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > > +    {
> > > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > > +
> > > +      /* Dereference fn_type if it is a pointer to member.  */
> > > +      if (is_ptrdatamem || is_ptrmemfunc)
> > > +   fn_type = deref_fn_type;
> > > +    }
> > > +  else
> > > +    {
> > > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > > +    }
> > > +
> > > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > > +    {
> > > +      if (complain & tf_error)
> > > +   error ("pointer to data member type %qT can only be invoked with "
> > > +          "one argument", fn_type);
> > > +      return error_mark_node;
> > > +    }
> > > +
> > > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > > +    {
> > > +      if (complain & tf_error)
> > > +   error ("pointer to member function type %qT must be invoked with "
> > > +          "at least one argument", fn_type);
> > > +      return error_mark_node;
> > > +    }
> > > +
> > > +  /* Construct an expression of a pointer to member.  */
> > > +  tree ptrmem_expr;
> > > +  if (is_ptrdatamem || is_ptrmemfunc)
> > > +    {
> > > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > > +
> > > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > > +      if (!(CLASS_TYPE_P (datum_type)
> > > +       || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > > +           && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> > > +   {
> > > +     if (complain & tf_error)
> > > +       error ("first argument type %qT of a pointer to member must be"
> > > +              "a class type or a reference/pointer to a class type",
>
> There's a space missing before "a class".  But we'll never see these errors
> anyway, because build_invoke will only ever be called with complain=tf_none,
> yes?

Thank you for pointing this out.  And yes for now, we might want to
use it with another complain someday, so I think leaving errors makes
sense.

>
> Functions like constructible_expr / assignable_expr don't have a complain
> parameter, so do we need it here?
>
> > > +              datum_type);
> > > +     return error_mark_node;
> > > +   }
> > > +
> > > +      bool is_refwrap = false;
> > > +      if (CLASS_TYPE_P (datum_type))
> > > +   {
> > > +     /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > > +     tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > > +     if (decl_in_std_namespace_p (datum_decl))
> > > +       {
> > > +         const_tree name = DECL_NAME (datum_decl);
> > > +         if (name && (id_equal (name, "reference_wrapper")))
> > > +           {
> > > +             /* Retrieve T from std::reference_wrapper<T>,
> > > +                i.e., decltype(datum.get()).  */
> > > +             datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > > +             is_refwrap = true;
> > > +           }
> > > +       }
> > > +   }
> > > +
> > > +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > > +      const bool ptrmem_is_base_of_datum =
> > > +   (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > > +    && NON_UNION_CLASS_TYPE_P (datum_type)
> >
> > Why NON_UNION?  That doesn't seem to be based on anything in the standard.
> >
> > This check and the one for reference_wrapper need to ignore REFERENCE_TYPE;
> > INVOKE is defined in terms of expressions, and the type of an expression is
> > never a reference.
> >
> > > +    && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > > +                                                   datum_type)
> > > +        || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > > +
> > > +      tree datum_expr = build_trait_object (datum_type);
> > > +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
> >
> > ...so we shouldn't need to check TYPE_REF_P here.
> >
> > Jason
> >
> > > +   /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > > +   datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > > +                                      RO_UNARY_STAR, NULL_TREE, complain);
> > > +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> > > +
> > > +      tree fn_expr = build_trait_object (fn_type);
> > > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > > +
> > > +      if (error_operand_p (ptrmem_expr))
> > > +   return error_mark_node;
> > > +
> > > +      if (is_ptrdatamem)
> > > +   return ptrmem_expr;
> > > +    }
> > > +
> > > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > > +     function, the first argument, which is the object, is not arguments to
> > > +     the function.  */
> > > +  releasing_vec args;
> > > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > > +    {
> > > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > > +      tree arg = build_trait_object (arg_type);
> > > +      vec_safe_push (args, arg);
> > > +    }
> > > +
> > > +  tree invoke_expr;
> > > +  if (is_ptrmemfunc)
> > > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > > +                                              complain);
> > > +  else  /* 1.7.  */
> > > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > > +                               false, complain);
> > > +  return invoke_expr;
> > > +}
> > > +
> > >   /* Determine which function will be called when looking up NAME in TYPE,
> > >      called with a single ARGTYPE argument, or no argument if ARGTYPE is
> > >      null.  FLAGS and COMPLAIN are as for build_new_method_call.
> >
>
> Marek
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:38                         ` Jason Merrill
  2024-05-02 20:45                           ` Marek Polacek
  2024-05-02 20:47                           ` Ken Matsui
@ 2024-05-02 20:54                           ` Ken Matsui
  2 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-02 20:54 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 1:38 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 16:12, Ken Matsui wrote:
> > This patch implements built-in trait for std::is_invocable.
> >
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __is_invocable.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >       * cp-tree.h (build_invoke): New function.
> >       * method.cc (build_invoke): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >       * g++.dg/ext/is_invocable1.C: New test.
> >       * g++.dg/ext/is_invocable2.C: New test.
> >       * g++.dg/ext/is_invocable3.C: New test.
> >       * g++.dg/ext/is_invocable4.C: New test.
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |   6 +
> >   gcc/cp/cp-trait.def                      |   1 +
> >   gcc/cp/cp-tree.h                         |   2 +
> >   gcc/cp/method.cc                         | 134 +++++++++
> >   gcc/cp/semantics.cc                      |   5 +
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >   10 files changed, 723 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index c28d7bf428e..6d14ef7dcc7 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_FUNCTION:
> >         inform (loc, "  %qT is not a function", t1);
> >         break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> >         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >         break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index b1c875a6e7d..4e420d5390a 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 1938ada0268..83dc20e1130 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7338,6 +7338,8 @@ extern tree get_copy_assign                     (tree);
> >   extern tree get_default_ctor                        (tree);
> >   extern tree get_dtor                                (tree, tsubst_flags_t);
> >   extern tree build_stub_object                       (tree);
> > +extern tree build_invoke                     (tree, const_tree,
> > +                                              tsubst_flags_t);
> >   extern tree strip_inheriting_ctors          (tree);
> >   extern tree inherited_ctor_binfo            (tree);
> >   extern bool base_ctor_omit_inherited_parms  (tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 08a3d34fb01..faf932258e6 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
> >     return build_stub_object (type);
> >   }
> >
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +     fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to data member type %qT can only be invoked with "
> > +            "one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to member function type %qT must be invoked with "
> > +            "at least one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (!(CLASS_TYPE_P (datum_type)
> > +         || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +             && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> > +     {
> > +       if (complain & tf_error)
> > +         error ("first argument type %qT of a pointer to member must be"
> > +                "a class type or a reference/pointer to a class type",
> > +                datum_type);
> > +       return error_mark_node;
> > +     }
> > +
> > +      bool is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +     {
> > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +       if (decl_in_std_namespace_p (datum_decl))
> > +         {
> > +           const_tree name = DECL_NAME (datum_decl);
> > +           if (name && (id_equal (name, "reference_wrapper")))
> > +             {
> > +               /* Retrieve T from std::reference_wrapper<T>,
> > +                  i.e., decltype(datum.get()).  */
> > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +               is_refwrap = true;
> > +             }
> > +         }
> > +     }
> > +
> > +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > +      const bool ptrmem_is_base_of_datum =
> > +     (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > +      && NON_UNION_CLASS_TYPE_P (datum_type)
>
> Why NON_UNION?  That doesn't seem to be based on anything in the standard.
>
> This check and the one for reference_wrapper need to ignore
> REFERENCE_TYPE; INVOKE is defined in terms of expressions, and the type
> of an expression is never a reference.

I think ptrmem_is_base_of_datum and is_refwrap checks are ignoring
REFERENCE_TYPE, leading to false, ...

>
> > +      && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > +                                                     datum_type)
> > +          || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
> > +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))

... which is why I think we need to check TYPE_REF_P here.

>
> ...so we shouldn't need to check TYPE_REF_P here.
>
> Jason
>
> > +     /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > +     datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > +                                        RO_UNARY_STAR, NULL_TREE, complain);
> > +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> > +
> > +      tree fn_expr = build_trait_object (fn_type);
> > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > +
> > +      if (error_operand_p (ptrmem_expr))
> > +     return error_mark_node;
> > +
> > +      if (is_ptrdatamem)
> > +     return ptrmem_expr;
> > +    }
> > +
> > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > +     function, the first argument, which is the object, is not arguments to
> > +     the function.  */
> > +  releasing_vec args;
> > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > +    {
> > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > +      tree arg = build_trait_object (arg_type);
> > +      vec_safe_push (args, arg);
> > +    }
> > +
> > +  tree invoke_expr;
> > +  if (is_ptrmemfunc)
> > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > +                                                complain);
> > +  else  /* 1.7.  */
> > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > +                                 false, complain);
> > +  return invoke_expr;
> > +}
> > +
> >   /* Determine which function will be called when looking up NAME in TYPE,
> >      called with a single ARGTYPE argument, or no argument if ARGTYPE is
> >      null.  FLAGS and COMPLAIN are as for build_new_method_call.
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v19 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:12                       ` [PATCH v18 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
  2024-05-02 20:38                         ` Jason Merrill
@ 2024-05-03  2:09                         ` Ken Matsui
  2024-05-03  2:33                           ` Jason Merrill
  2024-05-03  3:25                           ` [PATCH v20 " Ken Matsui
  1 sibling, 2 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-03  2:09 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 134 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 723 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index b1c875a6e7d..4e420d5390a 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1938ada0268..83dc20e1130 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..f6bca87282f 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,140 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be "
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      bool is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  is_refwrap = true;
+		}
+	    }
+	}
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_base_of_datum =
+	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	 && NON_UNION_CLASS_TYPE_P (datum_type)
+	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							datum_type)
+	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
+
+      tree datum_expr = build_trait_object (datum_type);
+      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index a293e0fc23e..55c2ec4b5b1 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index bcaa56a7bc5..d1c66267855 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -107,6 +107,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v19 23/26] c++: Implement __is_invocable built-in trait
  2024-05-03  2:09                         ` [PATCH v19 " Ken Matsui
@ 2024-05-03  2:33                           ` Jason Merrill
  2024-05-03  3:25                           ` [PATCH v20 " Ken Matsui
  1 sibling, 0 replies; 244+ messages in thread
From: Jason Merrill @ 2024-05-03  2:33 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/2/24 22:09, Ken Matsui wrote:
> This patch implements built-in trait for std::is_invocable.

Looks like this doesn't address my review comments on v18, only Marek's?

> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_invocable.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 	* cp-tree.h (build_invoke): New function.
> 	* method.cc (build_invoke): New function.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> 	* g++.dg/ext/is_invocable1.C: New test.
> 	* g++.dg/ext/is_invocable2.C: New test.
> 	* g++.dg/ext/is_invocable3.C: New test.
> 	* g++.dg/ext/is_invocable4.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |   6 +
>   gcc/cp/cp-trait.def                      |   1 +
>   gcc/cp/cp-tree.h                         |   2 +
>   gcc/cp/method.cc                         | 134 +++++++++
>   gcc/cp/semantics.cc                      |   5 +
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
>   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
>   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
>   10 files changed, 723 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index c28d7bf428e..6d14ef7dcc7 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_FUNCTION:
>         inform (loc, "  %qT is not a function", t1);
>         break;
> +    case CPTK_IS_INVOCABLE:
> +      if (!t2)
> +    inform (loc, "  %qT is not invocable", t1);
> +      else
> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> +      break;
>       case CPTK_IS_LAYOUT_COMPATIBLE:
>         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index b1c875a6e7d..4e420d5390a 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
>   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
>   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
>   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
>   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
>   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
>   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 1938ada0268..83dc20e1130 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
>   extern tree get_default_ctor			(tree);
>   extern tree get_dtor				(tree, tsubst_flags_t);
>   extern tree build_stub_object			(tree);
> +extern tree build_invoke			(tree, const_tree,
> +						 tsubst_flags_t);
>   extern tree strip_inheriting_ctors		(tree);
>   extern tree inherited_ctor_binfo		(tree);
>   extern bool base_ctor_omit_inherited_parms	(tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 08a3d34fb01..f6bca87282f 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
>     return build_stub_object (type);
>   }
>   
> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> +   given is not invocable, returns error_mark_node.  */
> +
> +tree
> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> +{
> +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> +    return error_mark_node;
> +
> +  gcc_assert (TYPE_P (fn_type));
> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> +
> +  /* Access check is required to determine if the given is invocable.  */
> +  deferring_access_check_sentinel acs (dk_no_deferred);
> +
> +  /* INVOKE is an unevaluated context.  */
> +  cp_unevaluated cp_uneval_guard;
> +
> +  bool is_ptrdatamem;
> +  bool is_ptrmemfunc;
> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> +    {
> +      tree deref_fn_type = TREE_TYPE (fn_type);
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> +
> +      /* Dereference fn_type if it is a pointer to member.  */
> +      if (is_ptrdatamem || is_ptrmemfunc)
> +	fn_type = deref_fn_type;
> +    }
> +  else
> +    {
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> +    }
> +
> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to data member type %qT can only be invoked with "
> +	       "one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to member function type %qT must be invoked with "
> +	       "at least one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  /* Construct an expression of a pointer to member.  */
> +  tree ptrmem_expr;
> +  if (is_ptrdatamem || is_ptrmemfunc)
> +    {
> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> +
> +      /* datum must be a class type or a reference/pointer to a class type.  */
> +      if (!(CLASS_TYPE_P (datum_type)
> +	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> +		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> +	{
> +	  if (complain & tf_error)
> +	    error ("first argument type %qT of a pointer to member must be "
> +		   "a class type or a reference/pointer to a class type",
> +		   datum_type);
> +	  return error_mark_node;
> +	}
> +
> +      bool is_refwrap = false;
> +      if (CLASS_TYPE_P (datum_type))
> +	{
> +	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> +	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> +	  if (decl_in_std_namespace_p (datum_decl))
> +	    {
> +	      const_tree name = DECL_NAME (datum_decl);
> +	      if (name && (id_equal (name, "reference_wrapper")))
> +		{
> +		  /* Retrieve T from std::reference_wrapper<T>,
> +		     i.e., decltype(datum.get()).  */
> +		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> +		  is_refwrap = true;
> +		}
> +	    }
> +	}
> +
> +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> +      const bool ptrmem_is_base_of_datum =
> +	(NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> +	 && NON_UNION_CLASS_TYPE_P (datum_type)
> +	 && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> +							datum_type)
> +	     || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> +
> +      tree datum_expr = build_trait_object (datum_type);
> +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
> +	/* 1.3 & 1.6: Try to dereference datum_expr.  */
> +	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> +					   RO_UNARY_STAR, NULL_TREE, complain);
> +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> +
> +      tree fn_expr = build_trait_object (fn_type);
> +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> +
> +      if (error_operand_p (ptrmem_expr))
> +	return error_mark_node;
> +
> +      if (is_ptrdatamem)
> +	return ptrmem_expr;
> +    }
> +
> +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> +     function, the first argument, which is the object, is not arguments to
> +     the function.  */
> +  releasing_vec args;
> +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> +    {
> +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> +      tree arg = build_trait_object (arg_type);
> +      vec_safe_push (args, arg);
> +    }
> +
> +  tree invoke_expr;
> +  if (is_ptrmemfunc)
> +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> +						   complain);
> +  else  /* 1.7.  */
> +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> +				    false, complain);
> +  return invoke_expr;
> +}
> +
>   /* Determine which function will be called when looking up NAME in TYPE,
>      called with a single ARGTYPE argument, or no argument if ARGTYPE is
>      null.  FLAGS and COMPLAIN are as for build_new_method_call.
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index a293e0fc23e..55c2ec4b5b1 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_FUNCTION:
>         return type_code1 == FUNCTION_TYPE;
>   
> +    case CPTK_IS_INVOCABLE:
> +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> +
>       case CPTK_IS_LAYOUT_COMPATIBLE:
>         return layout_compatible_type_p (type1, type2);
>   
> @@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_CONSTRUCTIBLE:
>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
> +    case CPTK_IS_INVOCABLE:
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>       case CPTK_REF_CONVERTS_FROM_TEMPORARY:
>         to = type1;
> @@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>   
>       case CPTK_IS_CONSTRUCTIBLE:
>       case CPTK_IS_CONVERTIBLE:
> +    case CPTK_IS_INVOCABLE:
>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>       case CPTK_IS_NOTHROW_CONVERTIBLE:
>       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index bcaa56a7bc5..d1c66267855 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -107,6 +107,9 @@
>   #if !__has_builtin (__is_function)
>   # error "__has_builtin (__is_function) failed"
>   #endif
> +#if !__has_builtin (__is_invocable)
> +# error "__has_builtin (__is_invocable) failed"
> +#endif
>   #if !__has_builtin (__is_layout_compatible)
>   # error "__has_builtin (__is_layout_compatible) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> new file mode 100644
> index 00000000000..d21ae1d1958
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> @@ -0,0 +1,349 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using func_type_v0 = void(*)();
> +
> +SA(   __is_invocable( func_type_v0 ) );
> +SA( ! __is_invocable( func_type_v0, int ) );
> +
> +using func_type_i0 = int(*)();
> +
> +SA(   __is_invocable( func_type_i0 ) );
> +SA( ! __is_invocable( func_type_i0, int ) );
> +
> +using func_type_l0 = int&(*)();
> +
> +SA(   __is_invocable( func_type_l0 ) );
> +SA( ! __is_invocable( func_type_l0(int) ) );
> +
> +using func_type_ii = int(*)(int);
> +
> +SA( ! __is_invocable( func_type_ii ) );
> +SA(   __is_invocable( func_type_ii, int ) );
> +
> +using func_type_il = int(*)(int&);
> +
> +SA( ! __is_invocable( func_type_il ) );
> +SA( ! __is_invocable( func_type_il, int ) );
> +SA(   __is_invocable( func_type_il, int& ) );
> +
> +using func_type_ir = int(*)(int&&);
> +
> +SA( ! __is_invocable( func_type_ir ) );
> +SA( ! __is_invocable( func_type_ir, int& ) );
> +SA(   __is_invocable( func_type_ir, int ) );
> +SA(   __is_invocable( func_type_ir, int&& ) );
> +
> +struct A { };
> +
> +using mem_type_i = int A::*;
> +
> +SA( ! __is_invocable( mem_type_i ) );
> +SA( ! __is_invocable( mem_type_i, int ) );
> +SA( ! __is_invocable( mem_type_i, int* ) );
> +SA( ! __is_invocable( mem_type_i, int& ) );
> +SA( ! __is_invocable( mem_type_i, int&& ) );
> +SA(   __is_invocable( mem_type_i, A ) );
> +SA(   __is_invocable( mem_type_i, A* ) );
> +SA(   __is_invocable( mem_type_i, A& ) );
> +SA(   __is_invocable( mem_type_i, A&& ) );
> +SA(   __is_invocable( mem_type_i, const A& ) );
> +SA( ! __is_invocable( mem_type_i, A&, int ) );
> +
> +using memfun_type_i = int (A::*)();
> +
> +SA( ! __is_invocable( memfun_type_i ) );
> +SA( ! __is_invocable( memfun_type_i, int ) );
> +SA( ! __is_invocable( memfun_type_i, int* ) );
> +SA( ! __is_invocable( memfun_type_i, int& ) );
> +SA( ! __is_invocable( memfun_type_i, int&& ) );
> +SA(   __is_invocable( memfun_type_i, A ) );
> +SA(   __is_invocable( memfun_type_i, A* ) );
> +SA(   __is_invocable( memfun_type_i, A& ) );
> +SA(   __is_invocable( memfun_type_i, A&& ) );
> +SA( ! __is_invocable( memfun_type_i, const A& ) );
> +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> +
> +using memfun_type_ic = int (A::*)() const;
> +
> +SA( ! __is_invocable( memfun_type_ic ) );
> +SA( ! __is_invocable( memfun_type_ic, int ) );
> +SA( ! __is_invocable( memfun_type_ic, int& ) );
> +SA(   __is_invocable( memfun_type_ic, A& ) );
> +SA(   __is_invocable( memfun_type_ic, A* ) );
> +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> +SA(   __is_invocable( memfun_type_ic, const A& ) );
> +SA(   __is_invocable( memfun_type_ic, const A* ) );
> +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> +
> +using memfun_type_iic = int& (A::*)(int&) const;
> +
> +SA( ! __is_invocable( memfun_type_iic ) );
> +SA( ! __is_invocable( memfun_type_iic, int ) );
> +SA( ! __is_invocable( memfun_type_iic, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> +
> +struct B {
> +  int& operator()();
> +  long& operator()() const;
> +  bool& operator()(int);
> +private:
> +  void operator()(int, int);
> +};
> +using CB = const B;
> +
> +SA(   __is_invocable( B ) );
> +SA(   __is_invocable( B& ) );
> +SA(   __is_invocable( B&& ) );
> +SA( ! __is_invocable( B* ) );
> +SA(   __is_invocable( CB ) );
> +SA(   __is_invocable( CB& ) );
> +SA( ! __is_invocable( CB* ) );
> +
> +SA(   __is_invocable( B, int ) );
> +SA(   __is_invocable( B&, int ) );
> +SA(   __is_invocable( B&&, int ) );
> +SA( ! __is_invocable( B*, int ) );
> +SA( ! __is_invocable( CB, int ) );
> +SA( ! __is_invocable( CB&, int ) );
> +SA( ! __is_invocable( CB*, int ) );
> +
> +SA( ! __is_invocable( B, int, int ) );
> +SA( ! __is_invocable( B&, int, int ) );
> +SA( ! __is_invocable( B&&, int, int ) );
> +SA( ! __is_invocable( B*, int, int ) );
> +SA( ! __is_invocable( CB, int, int ) );
> +SA( ! __is_invocable( CB&, int, int ) );
> +SA( ! __is_invocable( CB*, int, int ) );
> +
> +struct C : B { int& operator()() = delete; };
> +using CC = const C;
> +
> +SA( ! __is_invocable( C ) );
> +SA( ! __is_invocable( C& ) );
> +SA( ! __is_invocable( C&& ) );
> +SA( ! __is_invocable( C* ) );
> +SA( ! __is_invocable( CC ) );
> +SA( ! __is_invocable( CC& ) );
> +SA( ! __is_invocable( CC* ) );
> +
> +struct D { B operator*(); };
> +using CD = const D;
> +
> +SA( ! __is_invocable( D ) );
> +
> +struct E { void v(); };
> +using CE = const E;
> +
> +SA( ! __is_invocable( E ) );
> +SA( ! __is_invocable( void (E::*)() ) );
> +SA(   __is_invocable( void (E::*)(), E ) );
> +SA(   __is_invocable( void (E::*)(), E* ) );
> +SA( ! __is_invocable( void (E::*)(), CE ) );
> +
> +struct F : E {};
> +using CF = const F;
> +
> +SA( ! __is_invocable( F ) );
> +SA(   __is_invocable( void (E::*)(), F ) );
> +SA(   __is_invocable( void (E::*)(), F* ) );
> +SA( ! __is_invocable( void (E::*)(), CF ) );
> +
> +struct G { E operator*(); };
> +using CG = const G;
> +
> +SA( ! __is_invocable( G ) );
> +SA(   __is_invocable( void (E::*)(), G ) );
> +SA( ! __is_invocable( void (E::*)(), G* ) );
> +SA( ! __is_invocable( void (E::*)(), CG ) );
> +
> +struct H { E& operator*(); };
> +using CH = const H;
> +
> +SA( ! __is_invocable( H ) );
> +SA(   __is_invocable( void (E::*)(), H ) );
> +SA( ! __is_invocable( void (E::*)(), H* ) );
> +SA( ! __is_invocable( void (E::*)(), CH ) );
> +
> +struct I { E&& operator*(); };
> +using CI = const I;
> +
> +SA( ! __is_invocable( I ) );
> +SA(   __is_invocable( void (E::*)(), I ) );
> +SA( ! __is_invocable( void (E::*)(), I* ) );
> +SA( ! __is_invocable( void (E::*)(), CI ) );
> +
> +struct K { E* operator*(); };
> +using CK = const K;
> +
> +SA( ! __is_invocable( K ) );
> +SA( ! __is_invocable( void (E::*)(), K ) );
> +SA( ! __is_invocable( void (E::*)(), K* ) );
> +SA( ! __is_invocable( void (E::*)(), CK ) );
> +
> +struct L { CE operator*(); };
> +using CL = const L;
> +
> +SA( ! __is_invocable( L ) );
> +SA( ! __is_invocable( void (E::*)(), L ) );
> +SA( ! __is_invocable( void (E::*)(), L* ) );
> +SA( ! __is_invocable( void (E::*)(), CL ) );
> +
> +struct M {
> +  int i;
> +private:
> +  long l;
> +};
> +using CM = const M;
> +
> +SA( ! __is_invocable( M ) );
> +SA( ! __is_invocable( M& ) );
> +SA( ! __is_invocable( M&& ) );
> +SA( ! __is_invocable( M* ) );
> +SA( ! __is_invocable( CM ) );
> +SA( ! __is_invocable( CM& ) );
> +SA( ! __is_invocable( CM* ) );
> +
> +SA( ! __is_invocable( int M::* ) );
> +SA(   __is_invocable( int M::*, M ) );
> +SA(   __is_invocable( int M::*, M& ) );
> +SA(   __is_invocable( int M::*, M&& ) );
> +SA(   __is_invocable( int M::*, M* ) );
> +SA(   __is_invocable( int M::*, CM ) );
> +SA(   __is_invocable( int M::*, CM& ) );
> +SA(   __is_invocable( int M::*, CM* ) );
> +SA( ! __is_invocable( int M::*, int ) );
> +
> +SA( ! __is_invocable( int CM::* ) );
> +SA(   __is_invocable( int CM::*, M ) );
> +SA(   __is_invocable( int CM::*, M& ) );
> +SA(   __is_invocable( int CM::*, M&& ) );
> +SA(   __is_invocable( int CM::*, M* ) );
> +SA(   __is_invocable( int CM::*, CM ) );
> +SA(   __is_invocable( int CM::*, CM& ) );
> +SA(   __is_invocable( int CM::*, CM* ) );
> +SA( ! __is_invocable( int CM::*, int ) );
> +
> +SA( ! __is_invocable( long M::* ) );
> +SA(   __is_invocable( long M::*, M ) );
> +SA(   __is_invocable( long M::*, M& ) );
> +SA(   __is_invocable( long M::*, M&& ) );
> +SA(   __is_invocable( long M::*, M* ) );
> +SA(   __is_invocable( long M::*, CM ) );
> +SA(   __is_invocable( long M::*, CM& ) );
> +SA(   __is_invocable( long M::*, CM* ) );
> +SA( ! __is_invocable( long M::*, long ) );
> +
> +SA( ! __is_invocable( long CM::* ) );
> +SA(   __is_invocable( long CM::*, M ) );
> +SA(   __is_invocable( long CM::*, M& ) );
> +SA(   __is_invocable( long CM::*, M&& ) );
> +SA(   __is_invocable( long CM::*, M* ) );
> +SA(   __is_invocable( long CM::*, CM ) );
> +SA(   __is_invocable( long CM::*, CM& ) );
> +SA(   __is_invocable( long CM::*, CM* ) );
> +SA( ! __is_invocable( long CM::*, long ) );
> +
> +SA( ! __is_invocable( short M::* ) );
> +SA(   __is_invocable( short M::*, M ) );
> +SA(   __is_invocable( short M::*, M& ) );
> +SA(   __is_invocable( short M::*, M&& ) );
> +SA(   __is_invocable( short M::*, M* ) );
> +SA(   __is_invocable( short M::*, CM ) );
> +SA(   __is_invocable( short M::*, CM& ) );
> +SA(   __is_invocable( short M::*, CM* ) );
> +SA( ! __is_invocable( short M::*, short ) );
> +
> +SA( ! __is_invocable( short CM::* ) );
> +SA(   __is_invocable( short CM::*, M ) );
> +SA(   __is_invocable( short CM::*, M& ) );
> +SA(   __is_invocable( short CM::*, M&& ) );
> +SA(   __is_invocable( short CM::*, M* ) );
> +SA(   __is_invocable( short CM::*, CM ) );
> +SA(   __is_invocable( short CM::*, CM& ) );
> +SA(   __is_invocable( short CM::*, CM* ) );
> +SA( ! __is_invocable( short CM::*, short ) );
> +
> +struct N { M operator*(); };
> +SA(   __is_invocable( int M::*, N ) );
> +SA( ! __is_invocable( int M::*, N* ) );
> +
> +struct O { M& operator*(); };
> +SA(   __is_invocable( int M::*, O ) );
> +SA( ! __is_invocable( int M::*, O* ) );
> +
> +struct P { M&& operator*(); };
> +SA(   __is_invocable( int M::*, P ) );
> +SA( ! __is_invocable( int M::*, P* ) );
> +
> +struct Q { M* operator*(); };
> +SA( ! __is_invocable( int M::*, Q ) );
> +SA( ! __is_invocable( int M::*, Q* ) );
> +
> +struct R { void operator()(int = 0); };
> +
> +SA(   __is_invocable( R ) );
> +SA(   __is_invocable( R, int ) );
> +SA( ! __is_invocable( R, int, int ) );
> +
> +struct S { void operator()(int, ...); };
> +
> +SA( ! __is_invocable( S ) );
> +SA(   __is_invocable( S, int ) );
> +SA(   __is_invocable( S, int, int ) );
> +SA(   __is_invocable( S, int, int, int ) );
> +
> +void fn1() {}
> +
> +SA(   __is_invocable( decltype(fn1) ) );
> +
> +void fn2(int arr[10]);
> +
> +SA(   __is_invocable( decltype(fn2), int[10] ) );
> +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> +SA(   __is_invocable( decltype(fn2), int[] ) );
> +
> +auto lambda = []() {};
> +
> +SA(   __is_invocable( decltype(lambda) ) );
> +
> +template <typename Func, typename... Args>
> +struct can_invoke {
> +    static constexpr bool value = __is_invocable( Func, Args... );
> +};
> +
> +SA(   can_invoke<decltype(lambda)>::value );
> +
> +struct T {
> +  void func() const {}
> +  int data;
> +};
> +
> +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> +
> +struct U { };
> +struct V : U { U& operator*() = delete; };
> +SA(   __is_invocable( int U::*, V ) );
> +
> +struct W : private U { U& operator*(); };
> +SA( ! __is_invocable( int U::*, W ) );
> +
> +struct X { int m; };
> +struct Y { X& operator*(); };
> +struct Z : Y { };
> +SA(   __is_invocable(int X::*, Z) );
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> new file mode 100644
> index 00000000000..a68aefd3e13
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> @@ -0,0 +1,139 @@
> +// { dg-do compile { target c++11 } }
> +// __is_invocable should handle std::reference_wrapper correctly.
> +
> +#include <functional>
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using std::reference_wrapper;
> +
> +using func_type_v0 = void(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> +
> +using func_type_i0 = int(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> +
> +using func_type_l0 = int&(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> +
> +using func_type_ii = int(*)(int);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> +
> +using func_type_il = int(*)(int&);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> +
> +using func_type_ir = int(*)(int&&);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> +
> +struct A { };
> +
> +using mem_type_i = int A::*;
> +
> +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> +
> +using memfun_type_i = int (A::*)();
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> +
> +using memfun_type_ic = int (A::*)() const;
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> +
> +using memfun_type_iic = int& (A::*)(int&) const;
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> +
> +struct B {
> +  int& operator()();
> +  long& operator()() const;
> +  bool& operator()(int);
> +private:
> +  void operator()(int, int);
> +};
> +using CB = const B;
> +
> +SA(   __is_invocable( reference_wrapper<B> ) );
> +SA(   __is_invocable( reference_wrapper<B>& ) );
> +SA(   __is_invocable( reference_wrapper<B>&& ) );
> +SA(   __is_invocable( reference_wrapper<CB> ) );
> +SA(   __is_invocable( reference_wrapper<CB>& ) );
> +SA(   __is_invocable( reference_wrapper<B>, int ) );
> +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> +
> +struct C : B { int& operator()() = delete; };
> +using CC = const C;
> +
> +SA( ! __is_invocable( reference_wrapper<C> ) );
> +SA( ! __is_invocable( reference_wrapper<C>& ) );
> +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> +SA( ! __is_invocable( reference_wrapper<CC> ) );
> +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> +
> +struct D { B operator*(); };
> +using CD = const D;
> +
> +SA( ! __is_invocable( reference_wrapper<D> ) );
> +SA( ! __is_invocable( reference_wrapper<D>& ) );
> +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> +SA( ! __is_invocable( reference_wrapper<D>* ) );
> +SA( ! __is_invocable( reference_wrapper<D*> ) );
> +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> +
> +std::function<void()> fn = []() {};
> +auto refwrap = std::ref(fn);
> +
> +SA(   __is_invocable( decltype(fn) ) );
> +SA(   __is_invocable( decltype(refwrap) ) );
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> new file mode 100644
> index 00000000000..8699b0a53ca
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> @@ -0,0 +1,51 @@
> +// { dg-do compile { target c++11 } }
> +// __is_invocable should handle incomplete class correctly.
> +
> +#define SA(X) static_assert((X),#X)
> +
> +struct Incomplete;
> +
> +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> +
> +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> +
> +template <typename T>
> +struct Holder { T t; };
> +
> +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> +
> +// Define Incomplete, which is now not incomplete.
> +struct Incomplete { void operator()(); };
> +
> +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> new file mode 100644
> index 00000000000..d1efccf08f8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> @@ -0,0 +1,33 @@
> +// { dg-do compile { target c++11 } }
> +// Failed access check should be a substitution failure, not an error.
> +
> +#define SA(X) static_assert((X),#X)
> +
> +template<bool B>
> +struct bool_constant { static constexpr bool value = B; };
> +
> +template<typename _Fn, typename... _ArgTypes>
> +struct is_invocable
> +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> +{ };
> +
> +#if __cpp_variable_templates
> +template<typename _Fn, typename... _ArgTypes>
> +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> +#endif
> +
> +class Private
> +{
> +  void operator()() const
> +  {
> +    SA( ! is_invocable<Private>::value );
> +#if __cpp_variable_templates
> +    SA( ! is_invocable_v<Private> );
> +#endif
> +  }
> +};
> +
> +SA( ! is_invocable<Private>::value );
> +#if __cpp_variable_templates
> +SA( ! is_invocable_v<Private> );
> +#endif


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-02 20:47                           ` Ken Matsui
@ 2024-05-03  2:43                             ` Jason Merrill
  2024-05-03  2:46                               ` Ken Matsui
  0 siblings, 1 reply; 244+ messages in thread
From: Jason Merrill @ 2024-05-03  2:43 UTC (permalink / raw)
  To: Ken Matsui; +Cc: Ken Matsui, gcc-patches, libstdc++

On 5/2/24 16:47, Ken Matsui wrote:
> On Thu, May 2, 2024 at 1:38 PM Jason Merrill <jason@redhat.com> wrote:
>>
>> On 5/2/24 16:12, Ken Matsui wrote:
>>> This patch implements built-in trait for std::is_invocable.
>>>
>>> gcc/cp/ChangeLog:
>>>
>>>        * cp-trait.def: Define __is_invocable.
>>>        * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
>>>        * semantics.cc (trait_expr_value): Likewise.
>>>        (finish_trait_expr): Likewise.
>>>        * cp-tree.h (build_invoke): New function.
>>>        * method.cc (build_invoke): New function.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>>        * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
>>>        * g++.dg/ext/is_invocable1.C: New test.
>>>        * g++.dg/ext/is_invocable2.C: New test.
>>>        * g++.dg/ext/is_invocable3.C: New test.
>>>        * g++.dg/ext/is_invocable4.C: New test.
>>>
>>> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
>>> ---
>>>    gcc/cp/constraint.cc                     |   6 +
>>>    gcc/cp/cp-trait.def                      |   1 +
>>>    gcc/cp/cp-tree.h                         |   2 +
>>>    gcc/cp/method.cc                         | 134 +++++++++
>>>    gcc/cp/semantics.cc                      |   5 +
>>>    gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
>>>    gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
>>>    gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
>>>    gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
>>>    gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
>>>    10 files changed, 723 insertions(+)
>>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
>>>
>>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
>>> index c28d7bf428e..6d14ef7dcc7 100644
>>> --- a/gcc/cp/constraint.cc
>>> +++ b/gcc/cp/constraint.cc
>>> @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
>>>        case CPTK_IS_FUNCTION:
>>>          inform (loc, "  %qT is not a function", t1);
>>>          break;
>>> +    case CPTK_IS_INVOCABLE:
>>> +      if (!t2)
>>> +    inform (loc, "  %qT is not invocable", t1);
>>> +      else
>>> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
>>> +      break;
>>>        case CPTK_IS_LAYOUT_COMPATIBLE:
>>>          inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
>>>          break;
>>> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
>>> index b1c875a6e7d..4e420d5390a 100644
>>> --- a/gcc/cp/cp-trait.def
>>> +++ b/gcc/cp/cp-trait.def
>>> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
>>>    DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
>>>    DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
>>>    DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
>>> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
>>>    DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
>>>    DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
>>>    DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>> index 1938ada0268..83dc20e1130 100644
>>> --- a/gcc/cp/cp-tree.h
>>> +++ b/gcc/cp/cp-tree.h
>>> @@ -7338,6 +7338,8 @@ extern tree get_copy_assign                     (tree);
>>>    extern tree get_default_ctor                        (tree);
>>>    extern tree get_dtor                                (tree, tsubst_flags_t);
>>>    extern tree build_stub_object                       (tree);
>>> +extern tree build_invoke                     (tree, const_tree,
>>> +                                              tsubst_flags_t);
>>>    extern tree strip_inheriting_ctors          (tree);
>>>    extern tree inherited_ctor_binfo            (tree);
>>>    extern bool base_ctor_omit_inherited_parms  (tree);
>>> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
>>> index 08a3d34fb01..faf932258e6 100644
>>> --- a/gcc/cp/method.cc
>>> +++ b/gcc/cp/method.cc
>>> @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
>>>      return build_stub_object (type);
>>>    }
>>>
>>> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
>>> +   given is not invocable, returns error_mark_node.  */
>>> +
>>> +tree
>>> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
>>> +{
>>> +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
>>> +    return error_mark_node;
>>> +
>>> +  gcc_assert (TYPE_P (fn_type));
>>> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
>>> +
>>> +  /* Access check is required to determine if the given is invocable.  */
>>> +  deferring_access_check_sentinel acs (dk_no_deferred);
>>> +
>>> +  /* INVOKE is an unevaluated context.  */
>>> +  cp_unevaluated cp_uneval_guard;
>>> +
>>> +  bool is_ptrdatamem;
>>> +  bool is_ptrmemfunc;
>>> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
>>> +    {
>>> +      tree deref_fn_type = TREE_TYPE (fn_type);
>>> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
>>> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
>>> +
>>> +      /* Dereference fn_type if it is a pointer to member.  */
>>> +      if (is_ptrdatamem || is_ptrmemfunc)
>>> +     fn_type = deref_fn_type;
>>> +    }
>>> +  else
>>> +    {
>>> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
>>> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
>>> +    }
>>> +
>>> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
>>> +    {
>>> +      if (complain & tf_error)
>>> +     error ("pointer to data member type %qT can only be invoked with "
>>> +            "one argument", fn_type);
>>> +      return error_mark_node;
>>> +    }
>>> +
>>> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
>>> +    {
>>> +      if (complain & tf_error)
>>> +     error ("pointer to member function type %qT must be invoked with "
>>> +            "at least one argument", fn_type);
>>> +      return error_mark_node;
>>> +    }
>>> +
>>> +  /* Construct an expression of a pointer to member.  */
>>> +  tree ptrmem_expr;
>>> +  if (is_ptrdatamem || is_ptrmemfunc)
>>> +    {
>>> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
>>> +
>>> +      /* datum must be a class type or a reference/pointer to a class type.  */
>>> +      if (!(CLASS_TYPE_P (datum_type)
>>> +         || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
>>> +             && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
>>> +     {
>>> +       if (complain & tf_error)
>>> +         error ("first argument type %qT of a pointer to member must be"
>>> +                "a class type or a reference/pointer to a class type",
>>> +                datum_type);
>>> +       return error_mark_node;
>>> +     }
>>> +
>>> +      bool is_refwrap = false;
>>> +      if (CLASS_TYPE_P (datum_type))
>>> +     {
>>> +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
>>> +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
>>> +       if (decl_in_std_namespace_p (datum_decl))
>>> +         {
>>> +           const_tree name = DECL_NAME (datum_decl);
>>> +           if (name && (id_equal (name, "reference_wrapper")))
>>> +             {
>>> +               /* Retrieve T from std::reference_wrapper<T>,
>>> +                  i.e., decltype(datum.get()).  */
>>> +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
>>> +               is_refwrap = true;
>>> +             }
>>> +         }
>>> +     }
>>> +
>>> +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
>>> +      const bool ptrmem_is_base_of_datum =
>>> +     (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
>>> +      && NON_UNION_CLASS_TYPE_P (datum_type)
>>
>> Why NON_UNION?  That doesn't seem to be based on anything in the standard.
> 
> This comes from the __is_base_of implementation in semantics.cc:
> 
>      case CPTK_IS_BASE_OF:
>        return (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
>        && (same_type_ignoring_top_level_qualifiers_p (type1, type2)
>    || DERIVED_FROM_P (type1, type2)));

OK, but non-union isn't a requirement for is_same, but you're applying 
that requirement for the same-type case as well as for the base case.

>> This check and the one for reference_wrapper need to ignore
>> REFERENCE_TYPE; INVOKE is defined in terms of expressions, and the type
>> of an expression is never a reference.

> I think ptrmem_is_base_of_datum and is_refwrap checks are ignoring
> REFERENCE_TYPE, leading to false, ...

I mean they need to look through REFERENCE_TYPE; if the first argument 
is reference to reference_wrapper, we need to use the reference_wrapper 
handling.  Likewise if it's a reference to T or a base of T.

>>> +      && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
>>> +                                                     datum_type)
>>> +          || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
>>> +
>>> +      tree datum_expr = build_trait_object (datum_type);
>>> +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
>>
>> ...so we shouldn't need to check TYPE_REF_P here.

> ... which is why I think we need to check TYPE_REF_P here.

The standard says that if t1 isn't either same, base, or 
reference_wrapper, we dereference it.  There's no exception for 
references because they shouldn't get this far.

Jason


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 23/26] c++: Implement __is_invocable built-in trait
  2024-05-03  2:43                             ` Jason Merrill
@ 2024-05-03  2:46                               ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-03  2:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Thu, May 2, 2024 at 7:43 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 16:47, Ken Matsui wrote:
> > On Thu, May 2, 2024 at 1:38 PM Jason Merrill <jason@redhat.com> wrote:
> >>
> >> On 5/2/24 16:12, Ken Matsui wrote:
> >>> This patch implements built-in trait for std::is_invocable.
> >>>
> >>> gcc/cp/ChangeLog:
> >>>
> >>>        * cp-trait.def: Define __is_invocable.
> >>>        * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >>>        * semantics.cc (trait_expr_value): Likewise.
> >>>        (finish_trait_expr): Likewise.
> >>>        * cp-tree.h (build_invoke): New function.
> >>>        * method.cc (build_invoke): New function.
> >>>
> >>> gcc/testsuite/ChangeLog:
> >>>
> >>>        * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >>>        * g++.dg/ext/is_invocable1.C: New test.
> >>>        * g++.dg/ext/is_invocable2.C: New test.
> >>>        * g++.dg/ext/is_invocable3.C: New test.
> >>>        * g++.dg/ext/is_invocable4.C: New test.
> >>>
> >>> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> >>> ---
> >>>    gcc/cp/constraint.cc                     |   6 +
> >>>    gcc/cp/cp-trait.def                      |   1 +
> >>>    gcc/cp/cp-tree.h                         |   2 +
> >>>    gcc/cp/method.cc                         | 134 +++++++++
> >>>    gcc/cp/semantics.cc                      |   5 +
> >>>    gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >>>    gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >>>    gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >>>    gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >>>    gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >>>    10 files changed, 723 insertions(+)
> >>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >>>    create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >>>
> >>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> >>> index c28d7bf428e..6d14ef7dcc7 100644
> >>> --- a/gcc/cp/constraint.cc
> >>> +++ b/gcc/cp/constraint.cc
> >>> @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> >>>        case CPTK_IS_FUNCTION:
> >>>          inform (loc, "  %qT is not a function", t1);
> >>>          break;
> >>> +    case CPTK_IS_INVOCABLE:
> >>> +      if (!t2)
> >>> +    inform (loc, "  %qT is not invocable", t1);
> >>> +      else
> >>> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> >>> +      break;
> >>>        case CPTK_IS_LAYOUT_COMPATIBLE:
> >>>          inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >>>          break;
> >>> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> >>> index b1c875a6e7d..4e420d5390a 100644
> >>> --- a/gcc/cp/cp-trait.def
> >>> +++ b/gcc/cp/cp-trait.def
> >>> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >>>    DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >>>    DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >>>    DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> >>> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >>>    DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >>>    DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >>>    DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> >>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> >>> index 1938ada0268..83dc20e1130 100644
> >>> --- a/gcc/cp/cp-tree.h
> >>> +++ b/gcc/cp/cp-tree.h
> >>> @@ -7338,6 +7338,8 @@ extern tree get_copy_assign                     (tree);
> >>>    extern tree get_default_ctor                        (tree);
> >>>    extern tree get_dtor                                (tree, tsubst_flags_t);
> >>>    extern tree build_stub_object                       (tree);
> >>> +extern tree build_invoke                     (tree, const_tree,
> >>> +                                              tsubst_flags_t);
> >>>    extern tree strip_inheriting_ctors          (tree);
> >>>    extern tree inherited_ctor_binfo            (tree);
> >>>    extern bool base_ctor_omit_inherited_parms  (tree);
> >>> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> >>> index 08a3d34fb01..faf932258e6 100644
> >>> --- a/gcc/cp/method.cc
> >>> +++ b/gcc/cp/method.cc
> >>> @@ -1928,6 +1928,140 @@ build_trait_object (tree type)
> >>>      return build_stub_object (type);
> >>>    }
> >>>
> >>> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> >>> +   given is not invocable, returns error_mark_node.  */
> >>> +
> >>> +tree
> >>> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> >>> +{
> >>> +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> >>> +    return error_mark_node;
> >>> +
> >>> +  gcc_assert (TYPE_P (fn_type));
> >>> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> >>> +
> >>> +  /* Access check is required to determine if the given is invocable.  */
> >>> +  deferring_access_check_sentinel acs (dk_no_deferred);
> >>> +
> >>> +  /* INVOKE is an unevaluated context.  */
> >>> +  cp_unevaluated cp_uneval_guard;
> >>> +
> >>> +  bool is_ptrdatamem;
> >>> +  bool is_ptrmemfunc;
> >>> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> >>> +    {
> >>> +      tree deref_fn_type = TREE_TYPE (fn_type);
> >>> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> >>> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> >>> +
> >>> +      /* Dereference fn_type if it is a pointer to member.  */
> >>> +      if (is_ptrdatamem || is_ptrmemfunc)
> >>> +     fn_type = deref_fn_type;
> >>> +    }
> >>> +  else
> >>> +    {
> >>> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> >>> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> >>> +    }
> >>> +
> >>> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> >>> +    {
> >>> +      if (complain & tf_error)
> >>> +     error ("pointer to data member type %qT can only be invoked with "
> >>> +            "one argument", fn_type);
> >>> +      return error_mark_node;
> >>> +    }
> >>> +
> >>> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> >>> +    {
> >>> +      if (complain & tf_error)
> >>> +     error ("pointer to member function type %qT must be invoked with "
> >>> +            "at least one argument", fn_type);
> >>> +      return error_mark_node;
> >>> +    }
> >>> +
> >>> +  /* Construct an expression of a pointer to member.  */
> >>> +  tree ptrmem_expr;
> >>> +  if (is_ptrdatamem || is_ptrmemfunc)
> >>> +    {
> >>> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> >>> +
> >>> +      /* datum must be a class type or a reference/pointer to a class type.  */
> >>> +      if (!(CLASS_TYPE_P (datum_type)
> >>> +         || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> >>> +             && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> >>> +     {
> >>> +       if (complain & tf_error)
> >>> +         error ("first argument type %qT of a pointer to member must be"
> >>> +                "a class type or a reference/pointer to a class type",
> >>> +                datum_type);
> >>> +       return error_mark_node;
> >>> +     }
> >>> +
> >>> +      bool is_refwrap = false;
> >>> +      if (CLASS_TYPE_P (datum_type))
> >>> +     {
> >>> +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> >>> +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> >>> +       if (decl_in_std_namespace_p (datum_decl))
> >>> +         {
> >>> +           const_tree name = DECL_NAME (datum_decl);
> >>> +           if (name && (id_equal (name, "reference_wrapper")))
> >>> +             {
> >>> +               /* Retrieve T from std::reference_wrapper<T>,
> >>> +                  i.e., decltype(datum.get()).  */
> >>> +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> >>> +               is_refwrap = true;
> >>> +             }
> >>> +         }
> >>> +     }
> >>> +
> >>> +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> >>> +      const bool ptrmem_is_base_of_datum =
> >>> +     (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> >>> +      && NON_UNION_CLASS_TYPE_P (datum_type)
> >>
> >> Why NON_UNION?  That doesn't seem to be based on anything in the standard.
> >
> > This comes from the __is_base_of implementation in semantics.cc:
> >
> >      case CPTK_IS_BASE_OF:
> >        return (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
> >        && (same_type_ignoring_top_level_qualifiers_p (type1, type2)
> >    || DERIVED_FROM_P (type1, type2)));
>
> OK, but non-union isn't a requirement for is_same, but you're applying
> that requirement for the same-type case as well as for the base case.
>
> >> This check and the one for reference_wrapper need to ignore
> >> REFERENCE_TYPE; INVOKE is defined in terms of expressions, and the type
> >> of an expression is never a reference.
>
> > I think ptrmem_is_base_of_datum and is_refwrap checks are ignoring
> > REFERENCE_TYPE, leading to false, ...
>
> I mean they need to look through REFERENCE_TYPE; if the first argument
> is reference to reference_wrapper, we need to use the reference_wrapper
> handling.  Likewise if it's a reference to T or a base of T.
>
> >>> +      && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> >>> +                                                     datum_type)
> >>> +          || DERIVED_FROM_P (ptrmem_class_type, datum_type)));
> >>> +
> >>> +      tree datum_expr = build_trait_object (datum_type);
> >>> +      if (!ptrmem_is_base_of_datum && !is_refwrap && !TYPE_REF_P (datum_type))
> >>
> >> ...so we shouldn't need to check TYPE_REF_P here.
>
> > ... which is why I think we need to check TYPE_REF_P here.
>
> The standard says that if t1 isn't either same, base, or
> reference_wrapper, we dereference it.  There's no exception for
> references because they shouldn't get this far.

Oh, that makes sense!  Thank you so much for your review!

>
> Jason
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v20 23/26] c++: Implement __is_invocable built-in trait
  2024-05-03  2:09                         ` [PATCH v19 " Ken Matsui
  2024-05-03  2:33                           ` Jason Merrill
@ 2024-05-03  3:25                           ` Ken Matsui
  2024-05-03 19:38                             ` Jason Merrill
  2024-05-03 20:52                             ` [PATCH v21 20/23] " Ken Matsui
  1 sibling, 2 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-03  3:25 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

Addressed Jason's review comments.  Ok for trunk?

-- >8 --

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 138 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 727 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index b1c875a6e7d..4e420d5390a 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1938ada0268..83dc20e1130 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..6bd590707b0 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,144 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be "
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      if (TYPE_REF_P (datum_type))
+	datum_type = TREE_TYPE (datum_type);
+
+      bool datum_is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  datum_is_refwrap = true;
+		}
+	    }
+	}
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_same_or_base_of_datum =
+	(same_type_p (ptrmem_class_type, datum_type)
+	 || (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, datum_type))));
+
+      tree datum_expr = build_trait_object (datum_type);
+      if (!ptrmem_is_same_or_base_of_datum && !datum_is_refwrap)
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index a293e0fc23e..55c2ec4b5b1 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index bcaa56a7bc5..d1c66267855 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -107,6 +107,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v20 23/26] c++: Implement __is_invocable built-in trait
  2024-05-03  3:25                           ` [PATCH v20 " Ken Matsui
@ 2024-05-03 19:38                             ` Jason Merrill
  2024-05-03 20:28                               ` Ken Matsui
  2024-05-03 20:52                             ` [PATCH v21 20/23] " Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Jason Merrill @ 2024-05-03 19:38 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/2/24 23:25, Ken Matsui wrote:
> Addressed Jason's review comments.  Ok for trunk?
> 
> -- >8 --
> 
> This patch implements built-in trait for std::is_invocable.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_invocable.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 	* cp-tree.h (build_invoke): New function.
> 	* method.cc (build_invoke): New function.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> 	* g++.dg/ext/is_invocable1.C: New test.
> 	* g++.dg/ext/is_invocable2.C: New test.
> 	* g++.dg/ext/is_invocable3.C: New test.
> 	* g++.dg/ext/is_invocable4.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |   6 +
>   gcc/cp/cp-trait.def                      |   1 +
>   gcc/cp/cp-tree.h                         |   2 +
>   gcc/cp/method.cc                         | 138 +++++++++
>   gcc/cp/semantics.cc                      |   5 +
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
>   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
>   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
>   10 files changed, 727 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index c28d7bf428e..6d14ef7dcc7 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_FUNCTION:
>         inform (loc, "  %qT is not a function", t1);
>         break;
> +    case CPTK_IS_INVOCABLE:
> +      if (!t2)
> +    inform (loc, "  %qT is not invocable", t1);
> +      else
> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> +      break;
>       case CPTK_IS_LAYOUT_COMPATIBLE:
>         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index b1c875a6e7d..4e420d5390a 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
>   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
>   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
>   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
>   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
>   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
>   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 1938ada0268..83dc20e1130 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7338,6 +7338,8 @@ extern tree get_copy_assign			(tree);
>   extern tree get_default_ctor			(tree);
>   extern tree get_dtor				(tree, tsubst_flags_t);
>   extern tree build_stub_object			(tree);
> +extern tree build_invoke			(tree, const_tree,
> +						 tsubst_flags_t);
>   extern tree strip_inheriting_ctors		(tree);
>   extern tree inherited_ctor_binfo		(tree);
>   extern bool base_ctor_omit_inherited_parms	(tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 08a3d34fb01..6bd590707b0 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1928,6 +1928,144 @@ build_trait_object (tree type)
>     return build_stub_object (type);
>   }
>   
> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> +   given is not invocable, returns error_mark_node.  */
> +
> +tree
> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> +{
> +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> +    return error_mark_node;
> +
> +  gcc_assert (TYPE_P (fn_type));
> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> +
> +  /* Access check is required to determine if the given is invocable.  */
> +  deferring_access_check_sentinel acs (dk_no_deferred);
> +
> +  /* INVOKE is an unevaluated context.  */
> +  cp_unevaluated cp_uneval_guard;
> +
> +  bool is_ptrdatamem;
> +  bool is_ptrmemfunc;
> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> +    {
> +      tree deref_fn_type = TREE_TYPE (fn_type);
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> +
> +      /* Dereference fn_type if it is a pointer to member.  */
> +      if (is_ptrdatamem || is_ptrmemfunc)
> +	fn_type = deref_fn_type;
> +    }
> +  else
> +    {
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> +    }
> +
> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to data member type %qT can only be invoked with "
> +	       "one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to member function type %qT must be invoked with "
> +	       "at least one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  /* Construct an expression of a pointer to member.  */
> +  tree ptrmem_expr;
> +  if (is_ptrdatamem || is_ptrmemfunc)
> +    {
> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> +
> +      /* datum must be a class type or a reference/pointer to a class type.  */
> +      if (!(CLASS_TYPE_P (datum_type)
> +	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> +		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> +	{
> +	  if (complain & tf_error)
> +	    error ("first argument type %qT of a pointer to member must be "
> +		   "a class type or a reference/pointer to a class type",
> +		   datum_type);
> +	  return error_mark_node;
> +	}
> +
> +      if (TYPE_REF_P (datum_type))
> +	datum_type = TREE_TYPE (datum_type);

Unfortunately, stripping the reference here...

> +      bool datum_is_refwrap = false;
> +      if (CLASS_TYPE_P (datum_type))
> +	{
> +	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> +	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> +	  if (decl_in_std_namespace_p (datum_decl))
> +	    {
> +	      const_tree name = DECL_NAME (datum_decl);
> +	      if (name && (id_equal (name, "reference_wrapper")))
> +		{
> +		  /* Retrieve T from std::reference_wrapper<T>,
> +		     i.e., decltype(datum.get()).  */
> +		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> +		  datum_is_refwrap = true;
> +		}
> +	    }
> +	}
> +
> +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> +      const bool ptrmem_is_same_or_base_of_datum =
> +	(same_type_p (ptrmem_class_type, datum_type)
> +	 || (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> +	     && NON_UNION_CLASS_TYPE_P (datum_type)
> +	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> +							    datum_type)
> +		 || DERIVED_FROM_P (ptrmem_class_type, datum_type))));
> +
> +      tree datum_expr = build_trait_object (datum_type);

...and then using the result for build_trait_object, loses the 
difference between lvalue and rvalue references.

It should be enough to move this line up before stripping the reference.

And then you could just

datum_type = TREE_TYPE (datum_expr);

since build_trait_object will have done a convert_from_reference.

> +      if (!ptrmem_is_same_or_base_of_datum && !datum_is_refwrap)
> +	/* 1.3 & 1.6: Try to dereference datum_expr.  */
> +	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> +					   RO_UNARY_STAR, NULL_TREE, complain);
> +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> +
> +      tree fn_expr = build_trait_object (fn_type);
> +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> +
> +      if (error_operand_p (ptrmem_expr))
> +	return error_mark_node;
> +
> +      if (is_ptrdatamem)
> +	return ptrmem_expr;
> +    }
> +
> +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> +     function, the first argument, which is the object, is not arguments to
> +     the function.  */
> +  releasing_vec args;
> +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> +    {
> +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> +      tree arg = build_trait_object (arg_type);
> +      vec_safe_push (args, arg);
> +    }
> +
> +  tree invoke_expr;
> +  if (is_ptrmemfunc)
> +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> +						   complain);
> +  else  /* 1.7.  */
> +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> +				    false, complain);
> +  return invoke_expr;
> +}
> +
>   /* Determine which function will be called when looking up NAME in TYPE,
>      called with a single ARGTYPE argument, or no argument if ARGTYPE is
>      null.  FLAGS and COMPLAIN are as for build_new_method_call.
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index a293e0fc23e..55c2ec4b5b1 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_FUNCTION:
>         return type_code1 == FUNCTION_TYPE;
>   
> +    case CPTK_IS_INVOCABLE:
> +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> +
>       case CPTK_IS_LAYOUT_COMPATIBLE:
>         return layout_compatible_type_p (type1, type2);
>   
> @@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
>       case CPTK_IS_CONSTRUCTIBLE:
>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
> +    case CPTK_IS_INVOCABLE:
>       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>       case CPTK_REF_CONVERTS_FROM_TEMPORARY:
>         to = type1;
> @@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>   
>       case CPTK_IS_CONSTRUCTIBLE:
>       case CPTK_IS_CONVERTIBLE:
> +    case CPTK_IS_INVOCABLE:
>       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>       case CPTK_IS_NOTHROW_CONVERTIBLE:
>       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
> diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> index bcaa56a7bc5..d1c66267855 100644
> --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> @@ -107,6 +107,9 @@
>   #if !__has_builtin (__is_function)
>   # error "__has_builtin (__is_function) failed"
>   #endif
> +#if !__has_builtin (__is_invocable)
> +# error "__has_builtin (__is_invocable) failed"
> +#endif
>   #if !__has_builtin (__is_layout_compatible)
>   # error "__has_builtin (__is_layout_compatible) failed"
>   #endif
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> new file mode 100644
> index 00000000000..d21ae1d1958
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> @@ -0,0 +1,349 @@
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using func_type_v0 = void(*)();
> +
> +SA(   __is_invocable( func_type_v0 ) );
> +SA( ! __is_invocable( func_type_v0, int ) );
> +
> +using func_type_i0 = int(*)();
> +
> +SA(   __is_invocable( func_type_i0 ) );
> +SA( ! __is_invocable( func_type_i0, int ) );
> +
> +using func_type_l0 = int&(*)();
> +
> +SA(   __is_invocable( func_type_l0 ) );
> +SA( ! __is_invocable( func_type_l0(int) ) );
> +
> +using func_type_ii = int(*)(int);
> +
> +SA( ! __is_invocable( func_type_ii ) );
> +SA(   __is_invocable( func_type_ii, int ) );
> +
> +using func_type_il = int(*)(int&);
> +
> +SA( ! __is_invocable( func_type_il ) );
> +SA( ! __is_invocable( func_type_il, int ) );
> +SA(   __is_invocable( func_type_il, int& ) );
> +
> +using func_type_ir = int(*)(int&&);
> +
> +SA( ! __is_invocable( func_type_ir ) );
> +SA( ! __is_invocable( func_type_ir, int& ) );
> +SA(   __is_invocable( func_type_ir, int ) );
> +SA(   __is_invocable( func_type_ir, int&& ) );
> +
> +struct A { };
> +
> +using mem_type_i = int A::*;
> +
> +SA( ! __is_invocable( mem_type_i ) );
> +SA( ! __is_invocable( mem_type_i, int ) );
> +SA( ! __is_invocable( mem_type_i, int* ) );
> +SA( ! __is_invocable( mem_type_i, int& ) );
> +SA( ! __is_invocable( mem_type_i, int&& ) );
> +SA(   __is_invocable( mem_type_i, A ) );
> +SA(   __is_invocable( mem_type_i, A* ) );
> +SA(   __is_invocable( mem_type_i, A& ) );
> +SA(   __is_invocable( mem_type_i, A&& ) );
> +SA(   __is_invocable( mem_type_i, const A& ) );
> +SA( ! __is_invocable( mem_type_i, A&, int ) );
> +
> +using memfun_type_i = int (A::*)();
> +
> +SA( ! __is_invocable( memfun_type_i ) );
> +SA( ! __is_invocable( memfun_type_i, int ) );
> +SA( ! __is_invocable( memfun_type_i, int* ) );
> +SA( ! __is_invocable( memfun_type_i, int& ) );
> +SA( ! __is_invocable( memfun_type_i, int&& ) );
> +SA(   __is_invocable( memfun_type_i, A ) );
> +SA(   __is_invocable( memfun_type_i, A* ) );
> +SA(   __is_invocable( memfun_type_i, A& ) );
> +SA(   __is_invocable( memfun_type_i, A&& ) );
> +SA( ! __is_invocable( memfun_type_i, const A& ) );
> +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> +
> +using memfun_type_ic = int (A::*)() const;
> +
> +SA( ! __is_invocable( memfun_type_ic ) );
> +SA( ! __is_invocable( memfun_type_ic, int ) );
> +SA( ! __is_invocable( memfun_type_ic, int& ) );
> +SA(   __is_invocable( memfun_type_ic, A& ) );
> +SA(   __is_invocable( memfun_type_ic, A* ) );
> +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> +SA(   __is_invocable( memfun_type_ic, const A& ) );
> +SA(   __is_invocable( memfun_type_ic, const A* ) );
> +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> +
> +using memfun_type_iic = int& (A::*)(int&) const;
> +
> +SA( ! __is_invocable( memfun_type_iic ) );
> +SA( ! __is_invocable( memfun_type_iic, int ) );
> +SA( ! __is_invocable( memfun_type_iic, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> +
> +struct B {
> +  int& operator()();
> +  long& operator()() const;
> +  bool& operator()(int);
> +private:
> +  void operator()(int, int);
> +};
> +using CB = const B;
> +
> +SA(   __is_invocable( B ) );
> +SA(   __is_invocable( B& ) );
> +SA(   __is_invocable( B&& ) );
> +SA( ! __is_invocable( B* ) );
> +SA(   __is_invocable( CB ) );
> +SA(   __is_invocable( CB& ) );
> +SA( ! __is_invocable( CB* ) );
> +
> +SA(   __is_invocable( B, int ) );
> +SA(   __is_invocable( B&, int ) );
> +SA(   __is_invocable( B&&, int ) );
> +SA( ! __is_invocable( B*, int ) );
> +SA( ! __is_invocable( CB, int ) );
> +SA( ! __is_invocable( CB&, int ) );
> +SA( ! __is_invocable( CB*, int ) );
> +
> +SA( ! __is_invocable( B, int, int ) );
> +SA( ! __is_invocable( B&, int, int ) );
> +SA( ! __is_invocable( B&&, int, int ) );
> +SA( ! __is_invocable( B*, int, int ) );
> +SA( ! __is_invocable( CB, int, int ) );
> +SA( ! __is_invocable( CB&, int, int ) );
> +SA( ! __is_invocable( CB*, int, int ) );
> +
> +struct C : B { int& operator()() = delete; };
> +using CC = const C;
> +
> +SA( ! __is_invocable( C ) );
> +SA( ! __is_invocable( C& ) );
> +SA( ! __is_invocable( C&& ) );
> +SA( ! __is_invocable( C* ) );
> +SA( ! __is_invocable( CC ) );
> +SA( ! __is_invocable( CC& ) );
> +SA( ! __is_invocable( CC* ) );
> +
> +struct D { B operator*(); };
> +using CD = const D;
> +
> +SA( ! __is_invocable( D ) );
> +
> +struct E { void v(); };
> +using CE = const E;
> +
> +SA( ! __is_invocable( E ) );
> +SA( ! __is_invocable( void (E::*)() ) );
> +SA(   __is_invocable( void (E::*)(), E ) );
> +SA(   __is_invocable( void (E::*)(), E* ) );
> +SA( ! __is_invocable( void (E::*)(), CE ) );
> +
> +struct F : E {};
> +using CF = const F;
> +
> +SA( ! __is_invocable( F ) );
> +SA(   __is_invocable( void (E::*)(), F ) );
> +SA(   __is_invocable( void (E::*)(), F* ) );
> +SA( ! __is_invocable( void (E::*)(), CF ) );
> +
> +struct G { E operator*(); };
> +using CG = const G;
> +
> +SA( ! __is_invocable( G ) );
> +SA(   __is_invocable( void (E::*)(), G ) );
> +SA( ! __is_invocable( void (E::*)(), G* ) );
> +SA( ! __is_invocable( void (E::*)(), CG ) );
> +
> +struct H { E& operator*(); };
> +using CH = const H;
> +
> +SA( ! __is_invocable( H ) );
> +SA(   __is_invocable( void (E::*)(), H ) );
> +SA( ! __is_invocable( void (E::*)(), H* ) );
> +SA( ! __is_invocable( void (E::*)(), CH ) );
> +
> +struct I { E&& operator*(); };
> +using CI = const I;
> +
> +SA( ! __is_invocable( I ) );
> +SA(   __is_invocable( void (E::*)(), I ) );
> +SA( ! __is_invocable( void (E::*)(), I* ) );
> +SA( ! __is_invocable( void (E::*)(), CI ) );
> +
> +struct K { E* operator*(); };
> +using CK = const K;
> +
> +SA( ! __is_invocable( K ) );
> +SA( ! __is_invocable( void (E::*)(), K ) );
> +SA( ! __is_invocable( void (E::*)(), K* ) );
> +SA( ! __is_invocable( void (E::*)(), CK ) );
> +
> +struct L { CE operator*(); };
> +using CL = const L;
> +
> +SA( ! __is_invocable( L ) );
> +SA( ! __is_invocable( void (E::*)(), L ) );
> +SA( ! __is_invocable( void (E::*)(), L* ) );
> +SA( ! __is_invocable( void (E::*)(), CL ) );
> +
> +struct M {
> +  int i;
> +private:
> +  long l;
> +};
> +using CM = const M;
> +
> +SA( ! __is_invocable( M ) );
> +SA( ! __is_invocable( M& ) );
> +SA( ! __is_invocable( M&& ) );
> +SA( ! __is_invocable( M* ) );
> +SA( ! __is_invocable( CM ) );
> +SA( ! __is_invocable( CM& ) );
> +SA( ! __is_invocable( CM* ) );
> +
> +SA( ! __is_invocable( int M::* ) );
> +SA(   __is_invocable( int M::*, M ) );
> +SA(   __is_invocable( int M::*, M& ) );
> +SA(   __is_invocable( int M::*, M&& ) );
> +SA(   __is_invocable( int M::*, M* ) );
> +SA(   __is_invocable( int M::*, CM ) );
> +SA(   __is_invocable( int M::*, CM& ) );
> +SA(   __is_invocable( int M::*, CM* ) );
> +SA( ! __is_invocable( int M::*, int ) );
> +
> +SA( ! __is_invocable( int CM::* ) );
> +SA(   __is_invocable( int CM::*, M ) );
> +SA(   __is_invocable( int CM::*, M& ) );
> +SA(   __is_invocable( int CM::*, M&& ) );
> +SA(   __is_invocable( int CM::*, M* ) );
> +SA(   __is_invocable( int CM::*, CM ) );
> +SA(   __is_invocable( int CM::*, CM& ) );
> +SA(   __is_invocable( int CM::*, CM* ) );
> +SA( ! __is_invocable( int CM::*, int ) );
> +
> +SA( ! __is_invocable( long M::* ) );
> +SA(   __is_invocable( long M::*, M ) );
> +SA(   __is_invocable( long M::*, M& ) );
> +SA(   __is_invocable( long M::*, M&& ) );
> +SA(   __is_invocable( long M::*, M* ) );
> +SA(   __is_invocable( long M::*, CM ) );
> +SA(   __is_invocable( long M::*, CM& ) );
> +SA(   __is_invocable( long M::*, CM* ) );
> +SA( ! __is_invocable( long M::*, long ) );
> +
> +SA( ! __is_invocable( long CM::* ) );
> +SA(   __is_invocable( long CM::*, M ) );
> +SA(   __is_invocable( long CM::*, M& ) );
> +SA(   __is_invocable( long CM::*, M&& ) );
> +SA(   __is_invocable( long CM::*, M* ) );
> +SA(   __is_invocable( long CM::*, CM ) );
> +SA(   __is_invocable( long CM::*, CM& ) );
> +SA(   __is_invocable( long CM::*, CM* ) );
> +SA( ! __is_invocable( long CM::*, long ) );
> +
> +SA( ! __is_invocable( short M::* ) );
> +SA(   __is_invocable( short M::*, M ) );
> +SA(   __is_invocable( short M::*, M& ) );
> +SA(   __is_invocable( short M::*, M&& ) );
> +SA(   __is_invocable( short M::*, M* ) );
> +SA(   __is_invocable( short M::*, CM ) );
> +SA(   __is_invocable( short M::*, CM& ) );
> +SA(   __is_invocable( short M::*, CM* ) );
> +SA( ! __is_invocable( short M::*, short ) );
> +
> +SA( ! __is_invocable( short CM::* ) );
> +SA(   __is_invocable( short CM::*, M ) );
> +SA(   __is_invocable( short CM::*, M& ) );
> +SA(   __is_invocable( short CM::*, M&& ) );
> +SA(   __is_invocable( short CM::*, M* ) );
> +SA(   __is_invocable( short CM::*, CM ) );
> +SA(   __is_invocable( short CM::*, CM& ) );
> +SA(   __is_invocable( short CM::*, CM* ) );
> +SA( ! __is_invocable( short CM::*, short ) );
> +
> +struct N { M operator*(); };
> +SA(   __is_invocable( int M::*, N ) );
> +SA( ! __is_invocable( int M::*, N* ) );
> +
> +struct O { M& operator*(); };
> +SA(   __is_invocable( int M::*, O ) );
> +SA( ! __is_invocable( int M::*, O* ) );
> +
> +struct P { M&& operator*(); };
> +SA(   __is_invocable( int M::*, P ) );
> +SA( ! __is_invocable( int M::*, P* ) );
> +
> +struct Q { M* operator*(); };
> +SA( ! __is_invocable( int M::*, Q ) );
> +SA( ! __is_invocable( int M::*, Q* ) );
> +
> +struct R { void operator()(int = 0); };
> +
> +SA(   __is_invocable( R ) );
> +SA(   __is_invocable( R, int ) );
> +SA( ! __is_invocable( R, int, int ) );
> +
> +struct S { void operator()(int, ...); };
> +
> +SA( ! __is_invocable( S ) );
> +SA(   __is_invocable( S, int ) );
> +SA(   __is_invocable( S, int, int ) );
> +SA(   __is_invocable( S, int, int, int ) );
> +
> +void fn1() {}
> +
> +SA(   __is_invocable( decltype(fn1) ) );
> +
> +void fn2(int arr[10]);
> +
> +SA(   __is_invocable( decltype(fn2), int[10] ) );
> +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> +SA(   __is_invocable( decltype(fn2), int[] ) );
> +
> +auto lambda = []() {};
> +
> +SA(   __is_invocable( decltype(lambda) ) );
> +
> +template <typename Func, typename... Args>
> +struct can_invoke {
> +    static constexpr bool value = __is_invocable( Func, Args... );
> +};
> +
> +SA(   can_invoke<decltype(lambda)>::value );
> +
> +struct T {
> +  void func() const {}
> +  int data;
> +};
> +
> +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> +
> +struct U { };
> +struct V : U { U& operator*() = delete; };
> +SA(   __is_invocable( int U::*, V ) );
> +
> +struct W : private U { U& operator*(); };
> +SA( ! __is_invocable( int U::*, W ) );
> +
> +struct X { int m; };
> +struct Y { X& operator*(); };
> +struct Z : Y { };
> +SA(   __is_invocable(int X::*, Z) );
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> new file mode 100644
> index 00000000000..a68aefd3e13
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> @@ -0,0 +1,139 @@
> +// { dg-do compile { target c++11 } }
> +// __is_invocable should handle std::reference_wrapper correctly.
> +
> +#include <functional>
> +
> +#define SA(X) static_assert((X),#X)
> +
> +using std::reference_wrapper;
> +
> +using func_type_v0 = void(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> +
> +using func_type_i0 = int(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> +
> +using func_type_l0 = int&(*)();
> +
> +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> +
> +using func_type_ii = int(*)(int);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> +
> +using func_type_il = int(*)(int&);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> +
> +using func_type_ir = int(*)(int&&);
> +
> +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> +
> +struct A { };
> +
> +using mem_type_i = int A::*;
> +
> +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> +
> +using memfun_type_i = int (A::*)();
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> +
> +using memfun_type_ic = int (A::*)() const;
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> +
> +using memfun_type_iic = int& (A::*)(int&) const;
> +
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> +
> +struct B {
> +  int& operator()();
> +  long& operator()() const;
> +  bool& operator()(int);
> +private:
> +  void operator()(int, int);
> +};
> +using CB = const B;
> +
> +SA(   __is_invocable( reference_wrapper<B> ) );
> +SA(   __is_invocable( reference_wrapper<B>& ) );
> +SA(   __is_invocable( reference_wrapper<B>&& ) );
> +SA(   __is_invocable( reference_wrapper<CB> ) );
> +SA(   __is_invocable( reference_wrapper<CB>& ) );
> +SA(   __is_invocable( reference_wrapper<B>, int ) );
> +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> +
> +struct C : B { int& operator()() = delete; };
> +using CC = const C;
> +
> +SA( ! __is_invocable( reference_wrapper<C> ) );
> +SA( ! __is_invocable( reference_wrapper<C>& ) );
> +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> +SA( ! __is_invocable( reference_wrapper<CC> ) );
> +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> +
> +struct D { B operator*(); };
> +using CD = const D;
> +
> +SA( ! __is_invocable( reference_wrapper<D> ) );
> +SA( ! __is_invocable( reference_wrapper<D>& ) );
> +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> +SA( ! __is_invocable( reference_wrapper<D>* ) );
> +SA( ! __is_invocable( reference_wrapper<D*> ) );
> +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> +
> +std::function<void()> fn = []() {};
> +auto refwrap = std::ref(fn);
> +
> +SA(   __is_invocable( decltype(fn) ) );
> +SA(   __is_invocable( decltype(refwrap) ) );
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> new file mode 100644
> index 00000000000..8699b0a53ca
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> @@ -0,0 +1,51 @@
> +// { dg-do compile { target c++11 } }
> +// __is_invocable should handle incomplete class correctly.
> +
> +#define SA(X) static_assert((X),#X)
> +
> +struct Incomplete;
> +
> +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> +
> +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> +
> +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> +
> +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> +
> +template <typename T>
> +struct Holder { T t; };
> +
> +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> +
> +// Define Incomplete, which is now not incomplete.
> +struct Incomplete { void operator()(); };
> +
> +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> new file mode 100644
> index 00000000000..d1efccf08f8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> @@ -0,0 +1,33 @@
> +// { dg-do compile { target c++11 } }
> +// Failed access check should be a substitution failure, not an error.
> +
> +#define SA(X) static_assert((X),#X)
> +
> +template<bool B>
> +struct bool_constant { static constexpr bool value = B; };
> +
> +template<typename _Fn, typename... _ArgTypes>
> +struct is_invocable
> +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> +{ };
> +
> +#if __cpp_variable_templates
> +template<typename _Fn, typename... _ArgTypes>
> +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> +#endif
> +
> +class Private
> +{
> +  void operator()() const
> +  {
> +    SA( ! is_invocable<Private>::value );
> +#if __cpp_variable_templates
> +    SA( ! is_invocable_v<Private> );
> +#endif
> +  }
> +};
> +
> +SA( ! is_invocable<Private>::value );
> +#if __cpp_variable_templates
> +SA( ! is_invocable_v<Private> );
> +#endif


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v20 23/26] c++: Implement __is_invocable built-in trait
  2024-05-03 19:38                             ` Jason Merrill
@ 2024-05-03 20:28                               ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-03 20:28 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Fri, May 3, 2024 at 12:38 PM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/2/24 23:25, Ken Matsui wrote:
> > Addressed Jason's review comments.  Ok for trunk?
> >
> > -- >8 --
> >
> > This patch implements built-in trait for std::is_invocable.
> >
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __is_invocable.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >       * cp-tree.h (build_invoke): New function.
> >       * method.cc (build_invoke): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >       * g++.dg/ext/is_invocable1.C: New test.
> >       * g++.dg/ext/is_invocable2.C: New test.
> >       * g++.dg/ext/is_invocable3.C: New test.
> >       * g++.dg/ext/is_invocable4.C: New test.
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |   6 +
> >   gcc/cp/cp-trait.def                      |   1 +
> >   gcc/cp/cp-tree.h                         |   2 +
> >   gcc/cp/method.cc                         | 138 +++++++++
> >   gcc/cp/semantics.cc                      |   5 +
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >   10 files changed, 727 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index c28d7bf428e..6d14ef7dcc7 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_FUNCTION:
> >         inform (loc, "  %qT is not a function", t1);
> >         break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> >         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >         break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index b1c875a6e7d..4e420d5390a 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 1938ada0268..83dc20e1130 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7338,6 +7338,8 @@ extern tree get_copy_assign                     (tree);
> >   extern tree get_default_ctor                        (tree);
> >   extern tree get_dtor                                (tree, tsubst_flags_t);
> >   extern tree build_stub_object                       (tree);
> > +extern tree build_invoke                     (tree, const_tree,
> > +                                              tsubst_flags_t);
> >   extern tree strip_inheriting_ctors          (tree);
> >   extern tree inherited_ctor_binfo            (tree);
> >   extern bool base_ctor_omit_inherited_parms  (tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 08a3d34fb01..6bd590707b0 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,144 @@ build_trait_object (tree type)
> >     return build_stub_object (type);
> >   }
> >
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +     fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to data member type %qT can only be invoked with "
> > +            "one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to member function type %qT must be invoked with "
> > +            "at least one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (!(CLASS_TYPE_P (datum_type)
> > +         || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +             && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> > +     {
> > +       if (complain & tf_error)
> > +         error ("first argument type %qT of a pointer to member must be "
> > +                "a class type or a reference/pointer to a class type",
> > +                datum_type);
> > +       return error_mark_node;
> > +     }
> > +
> > +      if (TYPE_REF_P (datum_type))
> > +     datum_type = TREE_TYPE (datum_type);
>
> Unfortunately, stripping the reference here...
>
> > +      bool datum_is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +     {
> > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +       if (decl_in_std_namespace_p (datum_decl))
> > +         {
> > +           const_tree name = DECL_NAME (datum_decl);
> > +           if (name && (id_equal (name, "reference_wrapper")))
> > +             {
> > +               /* Retrieve T from std::reference_wrapper<T>,
> > +                  i.e., decltype(datum.get()).  */
> > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +               datum_is_refwrap = true;
> > +             }
> > +         }
> > +     }
> > +
> > +      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
> > +      const bool ptrmem_is_same_or_base_of_datum =
> > +     (same_type_p (ptrmem_class_type, datum_type)
> > +      || (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
> > +          && NON_UNION_CLASS_TYPE_P (datum_type)
> > +          && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
> > +                                                         datum_type)
> > +              || DERIVED_FROM_P (ptrmem_class_type, datum_type))));
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
>
> ...and then using the result for build_trait_object, loses the
> difference between lvalue and rvalue references.
>
> It should be enough to move this line up before stripping the reference.
>
> And then you could just
>
> datum_type = TREE_TYPE (datum_expr);
>
> since build_trait_object will have done a convert_from_reference.

Oooh, thank you so much!

>
> > +      if (!ptrmem_is_same_or_base_of_datum && !datum_is_refwrap)
> > +     /* 1.3 & 1.6: Try to dereference datum_expr.  */
> > +     datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
> > +                                        RO_UNARY_STAR, NULL_TREE, complain);
> > +      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
> > +
> > +      tree fn_expr = build_trait_object (fn_type);
> > +      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
> > +
> > +      if (error_operand_p (ptrmem_expr))
> > +     return error_mark_node;
> > +
> > +      if (is_ptrdatamem)
> > +     return ptrmem_expr;
> > +    }
> > +
> > +  /* Construct expressions for arguments to INVOKE.  For a pointer to member
> > +     function, the first argument, which is the object, is not arguments to
> > +     the function.  */
> > +  releasing_vec args;
> > +  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
> > +    {
> > +      tree arg_type = TREE_VEC_ELT (arg_types, i);
> > +      tree arg = build_trait_object (arg_type);
> > +      vec_safe_push (args, arg);
> > +    }
> > +
> > +  tree invoke_expr;
> > +  if (is_ptrmemfunc)
> > +    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
> > +                                                complain);
> > +  else  /* 1.7.  */
> > +    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
> > +                                 false, complain);
> > +  return invoke_expr;
> > +}
> > +
> >   /* Determine which function will be called when looking up NAME in TYPE,
> >      called with a single ARGTYPE argument, or no argument if ARGTYPE is
> >      null.  FLAGS and COMPLAIN are as for build_new_method_call.
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index a293e0fc23e..55c2ec4b5b1 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -12561,6 +12561,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
> >       case CPTK_IS_FUNCTION:
> >         return type_code1 == FUNCTION_TYPE;
> >
> > +    case CPTK_IS_INVOCABLE:
> > +      return !error_operand_p (build_invoke (type1, type2, tf_none));
> > +
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> >         return layout_compatible_type_p (type1, type2);
> >
> > @@ -12719,6 +12722,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
> >       case CPTK_IS_CONSTRUCTIBLE:
> >       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
> >       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
> > +    case CPTK_IS_INVOCABLE:
> >       case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
> >       case CPTK_REF_CONVERTS_FROM_TEMPORARY:
> >         to = type1;
> > @@ -12821,6 +12825,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> >
> >       case CPTK_IS_CONSTRUCTIBLE:
> >       case CPTK_IS_CONVERTIBLE:
> > +    case CPTK_IS_INVOCABLE:
> >       case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
> >       case CPTK_IS_NOTHROW_CONVERTIBLE:
> >       case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
> > diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > index bcaa56a7bc5..d1c66267855 100644
> > --- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > +++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
> > @@ -107,6 +107,9 @@
> >   #if !__has_builtin (__is_function)
> >   # error "__has_builtin (__is_function) failed"
> >   #endif
> > +#if !__has_builtin (__is_invocable)
> > +# error "__has_builtin (__is_invocable) failed"
> > +#endif
> >   #if !__has_builtin (__is_layout_compatible)
> >   # error "__has_builtin (__is_layout_compatible) failed"
> >   #endif
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > new file mode 100644
> > index 00000000000..d21ae1d1958
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
> > @@ -0,0 +1,349 @@
> > +// { dg-do compile { target c++11 } }
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +using func_type_v0 = void(*)();
> > +
> > +SA(   __is_invocable( func_type_v0 ) );
> > +SA( ! __is_invocable( func_type_v0, int ) );
> > +
> > +using func_type_i0 = int(*)();
> > +
> > +SA(   __is_invocable( func_type_i0 ) );
> > +SA( ! __is_invocable( func_type_i0, int ) );
> > +
> > +using func_type_l0 = int&(*)();
> > +
> > +SA(   __is_invocable( func_type_l0 ) );
> > +SA( ! __is_invocable( func_type_l0(int) ) );
> > +
> > +using func_type_ii = int(*)(int);
> > +
> > +SA( ! __is_invocable( func_type_ii ) );
> > +SA(   __is_invocable( func_type_ii, int ) );
> > +
> > +using func_type_il = int(*)(int&);
> > +
> > +SA( ! __is_invocable( func_type_il ) );
> > +SA( ! __is_invocable( func_type_il, int ) );
> > +SA(   __is_invocable( func_type_il, int& ) );
> > +
> > +using func_type_ir = int(*)(int&&);
> > +
> > +SA( ! __is_invocable( func_type_ir ) );
> > +SA( ! __is_invocable( func_type_ir, int& ) );
> > +SA(   __is_invocable( func_type_ir, int ) );
> > +SA(   __is_invocable( func_type_ir, int&& ) );
> > +
> > +struct A { };
> > +
> > +using mem_type_i = int A::*;
> > +
> > +SA( ! __is_invocable( mem_type_i ) );
> > +SA( ! __is_invocable( mem_type_i, int ) );
> > +SA( ! __is_invocable( mem_type_i, int* ) );
> > +SA( ! __is_invocable( mem_type_i, int& ) );
> > +SA( ! __is_invocable( mem_type_i, int&& ) );
> > +SA(   __is_invocable( mem_type_i, A ) );
> > +SA(   __is_invocable( mem_type_i, A* ) );
> > +SA(   __is_invocable( mem_type_i, A& ) );
> > +SA(   __is_invocable( mem_type_i, A&& ) );
> > +SA(   __is_invocable( mem_type_i, const A& ) );
> > +SA( ! __is_invocable( mem_type_i, A&, int ) );
> > +
> > +using memfun_type_i = int (A::*)();
> > +
> > +SA( ! __is_invocable( memfun_type_i ) );
> > +SA( ! __is_invocable( memfun_type_i, int ) );
> > +SA( ! __is_invocable( memfun_type_i, int* ) );
> > +SA( ! __is_invocable( memfun_type_i, int& ) );
> > +SA( ! __is_invocable( memfun_type_i, int&& ) );
> > +SA(   __is_invocable( memfun_type_i, A ) );
> > +SA(   __is_invocable( memfun_type_i, A* ) );
> > +SA(   __is_invocable( memfun_type_i, A& ) );
> > +SA(   __is_invocable( memfun_type_i, A&& ) );
> > +SA( ! __is_invocable( memfun_type_i, const A& ) );
> > +SA( ! __is_invocable( memfun_type_i, A&, int ) );
> > +
> > +using memfun_type_ic = int (A::*)() const;
> > +
> > +SA( ! __is_invocable( memfun_type_ic ) );
> > +SA( ! __is_invocable( memfun_type_ic, int ) );
> > +SA( ! __is_invocable( memfun_type_ic, int& ) );
> > +SA(   __is_invocable( memfun_type_ic, A& ) );
> > +SA(   __is_invocable( memfun_type_ic, A* ) );
> > +SA( ! __is_invocable( memfun_type_ic, A&, int ) );
> > +SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
> > +SA(   __is_invocable( memfun_type_ic, const A& ) );
> > +SA(   __is_invocable( memfun_type_ic, const A* ) );
> > +SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
> > +SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
> > +
> > +using memfun_type_iic = int& (A::*)(int&) const;
> > +
> > +SA( ! __is_invocable( memfun_type_iic ) );
> > +SA( ! __is_invocable( memfun_type_iic, int ) );
> > +SA( ! __is_invocable( memfun_type_iic, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, A&, int ) );
> > +SA(   __is_invocable( memfun_type_iic, A&, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, A*, int ) );
> > +SA(   __is_invocable( memfun_type_iic, A*, int& ) );
> > +SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
> > +SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
> > +SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
> > +SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
> > +
> > +struct B {
> > +  int& operator()();
> > +  long& operator()() const;
> > +  bool& operator()(int);
> > +private:
> > +  void operator()(int, int);
> > +};
> > +using CB = const B;
> > +
> > +SA(   __is_invocable( B ) );
> > +SA(   __is_invocable( B& ) );
> > +SA(   __is_invocable( B&& ) );
> > +SA( ! __is_invocable( B* ) );
> > +SA(   __is_invocable( CB ) );
> > +SA(   __is_invocable( CB& ) );
> > +SA( ! __is_invocable( CB* ) );
> > +
> > +SA(   __is_invocable( B, int ) );
> > +SA(   __is_invocable( B&, int ) );
> > +SA(   __is_invocable( B&&, int ) );
> > +SA( ! __is_invocable( B*, int ) );
> > +SA( ! __is_invocable( CB, int ) );
> > +SA( ! __is_invocable( CB&, int ) );
> > +SA( ! __is_invocable( CB*, int ) );
> > +
> > +SA( ! __is_invocable( B, int, int ) );
> > +SA( ! __is_invocable( B&, int, int ) );
> > +SA( ! __is_invocable( B&&, int, int ) );
> > +SA( ! __is_invocable( B*, int, int ) );
> > +SA( ! __is_invocable( CB, int, int ) );
> > +SA( ! __is_invocable( CB&, int, int ) );
> > +SA( ! __is_invocable( CB*, int, int ) );
> > +
> > +struct C : B { int& operator()() = delete; };
> > +using CC = const C;
> > +
> > +SA( ! __is_invocable( C ) );
> > +SA( ! __is_invocable( C& ) );
> > +SA( ! __is_invocable( C&& ) );
> > +SA( ! __is_invocable( C* ) );
> > +SA( ! __is_invocable( CC ) );
> > +SA( ! __is_invocable( CC& ) );
> > +SA( ! __is_invocable( CC* ) );
> > +
> > +struct D { B operator*(); };
> > +using CD = const D;
> > +
> > +SA( ! __is_invocable( D ) );
> > +
> > +struct E { void v(); };
> > +using CE = const E;
> > +
> > +SA( ! __is_invocable( E ) );
> > +SA( ! __is_invocable( void (E::*)() ) );
> > +SA(   __is_invocable( void (E::*)(), E ) );
> > +SA(   __is_invocable( void (E::*)(), E* ) );
> > +SA( ! __is_invocable( void (E::*)(), CE ) );
> > +
> > +struct F : E {};
> > +using CF = const F;
> > +
> > +SA( ! __is_invocable( F ) );
> > +SA(   __is_invocable( void (E::*)(), F ) );
> > +SA(   __is_invocable( void (E::*)(), F* ) );
> > +SA( ! __is_invocable( void (E::*)(), CF ) );
> > +
> > +struct G { E operator*(); };
> > +using CG = const G;
> > +
> > +SA( ! __is_invocable( G ) );
> > +SA(   __is_invocable( void (E::*)(), G ) );
> > +SA( ! __is_invocable( void (E::*)(), G* ) );
> > +SA( ! __is_invocable( void (E::*)(), CG ) );
> > +
> > +struct H { E& operator*(); };
> > +using CH = const H;
> > +
> > +SA( ! __is_invocable( H ) );
> > +SA(   __is_invocable( void (E::*)(), H ) );
> > +SA( ! __is_invocable( void (E::*)(), H* ) );
> > +SA( ! __is_invocable( void (E::*)(), CH ) );
> > +
> > +struct I { E&& operator*(); };
> > +using CI = const I;
> > +
> > +SA( ! __is_invocable( I ) );
> > +SA(   __is_invocable( void (E::*)(), I ) );
> > +SA( ! __is_invocable( void (E::*)(), I* ) );
> > +SA( ! __is_invocable( void (E::*)(), CI ) );
> > +
> > +struct K { E* operator*(); };
> > +using CK = const K;
> > +
> > +SA( ! __is_invocable( K ) );
> > +SA( ! __is_invocable( void (E::*)(), K ) );
> > +SA( ! __is_invocable( void (E::*)(), K* ) );
> > +SA( ! __is_invocable( void (E::*)(), CK ) );
> > +
> > +struct L { CE operator*(); };
> > +using CL = const L;
> > +
> > +SA( ! __is_invocable( L ) );
> > +SA( ! __is_invocable( void (E::*)(), L ) );
> > +SA( ! __is_invocable( void (E::*)(), L* ) );
> > +SA( ! __is_invocable( void (E::*)(), CL ) );
> > +
> > +struct M {
> > +  int i;
> > +private:
> > +  long l;
> > +};
> > +using CM = const M;
> > +
> > +SA( ! __is_invocable( M ) );
> > +SA( ! __is_invocable( M& ) );
> > +SA( ! __is_invocable( M&& ) );
> > +SA( ! __is_invocable( M* ) );
> > +SA( ! __is_invocable( CM ) );
> > +SA( ! __is_invocable( CM& ) );
> > +SA( ! __is_invocable( CM* ) );
> > +
> > +SA( ! __is_invocable( int M::* ) );
> > +SA(   __is_invocable( int M::*, M ) );
> > +SA(   __is_invocable( int M::*, M& ) );
> > +SA(   __is_invocable( int M::*, M&& ) );
> > +SA(   __is_invocable( int M::*, M* ) );
> > +SA(   __is_invocable( int M::*, CM ) );
> > +SA(   __is_invocable( int M::*, CM& ) );
> > +SA(   __is_invocable( int M::*, CM* ) );
> > +SA( ! __is_invocable( int M::*, int ) );
> > +
> > +SA( ! __is_invocable( int CM::* ) );
> > +SA(   __is_invocable( int CM::*, M ) );
> > +SA(   __is_invocable( int CM::*, M& ) );
> > +SA(   __is_invocable( int CM::*, M&& ) );
> > +SA(   __is_invocable( int CM::*, M* ) );
> > +SA(   __is_invocable( int CM::*, CM ) );
> > +SA(   __is_invocable( int CM::*, CM& ) );
> > +SA(   __is_invocable( int CM::*, CM* ) );
> > +SA( ! __is_invocable( int CM::*, int ) );
> > +
> > +SA( ! __is_invocable( long M::* ) );
> > +SA(   __is_invocable( long M::*, M ) );
> > +SA(   __is_invocable( long M::*, M& ) );
> > +SA(   __is_invocable( long M::*, M&& ) );
> > +SA(   __is_invocable( long M::*, M* ) );
> > +SA(   __is_invocable( long M::*, CM ) );
> > +SA(   __is_invocable( long M::*, CM& ) );
> > +SA(   __is_invocable( long M::*, CM* ) );
> > +SA( ! __is_invocable( long M::*, long ) );
> > +
> > +SA( ! __is_invocable( long CM::* ) );
> > +SA(   __is_invocable( long CM::*, M ) );
> > +SA(   __is_invocable( long CM::*, M& ) );
> > +SA(   __is_invocable( long CM::*, M&& ) );
> > +SA(   __is_invocable( long CM::*, M* ) );
> > +SA(   __is_invocable( long CM::*, CM ) );
> > +SA(   __is_invocable( long CM::*, CM& ) );
> > +SA(   __is_invocable( long CM::*, CM* ) );
> > +SA( ! __is_invocable( long CM::*, long ) );
> > +
> > +SA( ! __is_invocable( short M::* ) );
> > +SA(   __is_invocable( short M::*, M ) );
> > +SA(   __is_invocable( short M::*, M& ) );
> > +SA(   __is_invocable( short M::*, M&& ) );
> > +SA(   __is_invocable( short M::*, M* ) );
> > +SA(   __is_invocable( short M::*, CM ) );
> > +SA(   __is_invocable( short M::*, CM& ) );
> > +SA(   __is_invocable( short M::*, CM* ) );
> > +SA( ! __is_invocable( short M::*, short ) );
> > +
> > +SA( ! __is_invocable( short CM::* ) );
> > +SA(   __is_invocable( short CM::*, M ) );
> > +SA(   __is_invocable( short CM::*, M& ) );
> > +SA(   __is_invocable( short CM::*, M&& ) );
> > +SA(   __is_invocable( short CM::*, M* ) );
> > +SA(   __is_invocable( short CM::*, CM ) );
> > +SA(   __is_invocable( short CM::*, CM& ) );
> > +SA(   __is_invocable( short CM::*, CM* ) );
> > +SA( ! __is_invocable( short CM::*, short ) );
> > +
> > +struct N { M operator*(); };
> > +SA(   __is_invocable( int M::*, N ) );
> > +SA( ! __is_invocable( int M::*, N* ) );
> > +
> > +struct O { M& operator*(); };
> > +SA(   __is_invocable( int M::*, O ) );
> > +SA( ! __is_invocable( int M::*, O* ) );
> > +
> > +struct P { M&& operator*(); };
> > +SA(   __is_invocable( int M::*, P ) );
> > +SA( ! __is_invocable( int M::*, P* ) );
> > +
> > +struct Q { M* operator*(); };
> > +SA( ! __is_invocable( int M::*, Q ) );
> > +SA( ! __is_invocable( int M::*, Q* ) );
> > +
> > +struct R { void operator()(int = 0); };
> > +
> > +SA(   __is_invocable( R ) );
> > +SA(   __is_invocable( R, int ) );
> > +SA( ! __is_invocable( R, int, int ) );
> > +
> > +struct S { void operator()(int, ...); };
> > +
> > +SA( ! __is_invocable( S ) );
> > +SA(   __is_invocable( S, int ) );
> > +SA(   __is_invocable( S, int, int ) );
> > +SA(   __is_invocable( S, int, int, int ) );
> > +
> > +void fn1() {}
> > +
> > +SA(   __is_invocable( decltype(fn1) ) );
> > +
> > +void fn2(int arr[10]);
> > +
> > +SA(   __is_invocable( decltype(fn2), int[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
> > +SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
> > +SA(   __is_invocable( decltype(fn2), int[] ) );
> > +
> > +auto lambda = []() {};
> > +
> > +SA(   __is_invocable( decltype(lambda) ) );
> > +
> > +template <typename Func, typename... Args>
> > +struct can_invoke {
> > +    static constexpr bool value = __is_invocable( Func, Args... );
> > +};
> > +
> > +SA(   can_invoke<decltype(lambda)>::value );
> > +
> > +struct T {
> > +  void func() const {}
> > +  int data;
> > +};
> > +
> > +SA(   __is_invocable( decltype(&T::func)&, T& ) );
> > +SA(   __is_invocable( decltype(&T::data)&, T& ) );
> > +
> > +struct U { };
> > +struct V : U { U& operator*() = delete; };
> > +SA(   __is_invocable( int U::*, V ) );
> > +
> > +struct W : private U { U& operator*(); };
> > +SA( ! __is_invocable( int U::*, W ) );
> > +
> > +struct X { int m; };
> > +struct Y { X& operator*(); };
> > +struct Z : Y { };
> > +SA(   __is_invocable(int X::*, Z) );
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > new file mode 100644
> > index 00000000000..a68aefd3e13
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
> > @@ -0,0 +1,139 @@
> > +// { dg-do compile { target c++11 } }
> > +// __is_invocable should handle std::reference_wrapper correctly.
> > +
> > +#include <functional>
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +using std::reference_wrapper;
> > +
> > +using func_type_v0 = void(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
> > +
> > +using func_type_i0 = int(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
> > +
> > +using func_type_l0 = int&(*)();
> > +
> > +SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
> > +
> > +using func_type_ii = int(*)(int);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
> > +
> > +using func_type_il = int(*)(int&);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
> > +
> > +using func_type_ir = int(*)(int&&);
> > +
> > +SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
> > +SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
> > +SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
> > +
> > +struct A { };
> > +
> > +using mem_type_i = int A::*;
> > +
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
> > +
> > +using memfun_type_i = int (A::*)();
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
> > +
> > +using memfun_type_ic = int (A::*)() const;
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
> > +
> > +using memfun_type_iic = int& (A::*)(int&) const;
> > +
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
> > +SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
> > +SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
> > +
> > +struct B {
> > +  int& operator()();
> > +  long& operator()() const;
> > +  bool& operator()(int);
> > +private:
> > +  void operator()(int, int);
> > +};
> > +using CB = const B;
> > +
> > +SA(   __is_invocable( reference_wrapper<B> ) );
> > +SA(   __is_invocable( reference_wrapper<B>& ) );
> > +SA(   __is_invocable( reference_wrapper<B>&& ) );
> > +SA(   __is_invocable( reference_wrapper<CB> ) );
> > +SA(   __is_invocable( reference_wrapper<CB>& ) );
> > +SA(   __is_invocable( reference_wrapper<B>, int ) );
> > +SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
> > +
> > +struct C : B { int& operator()() = delete; };
> > +using CC = const C;
> > +
> > +SA( ! __is_invocable( reference_wrapper<C> ) );
> > +SA( ! __is_invocable( reference_wrapper<C>& ) );
> > +SA( ! __is_invocable( reference_wrapper<C>&& ) );
> > +SA( ! __is_invocable( reference_wrapper<CC> ) );
> > +SA( ! __is_invocable( reference_wrapper<CC>& ) );
> > +
> > +struct D { B operator*(); };
> > +using CD = const D;
> > +
> > +SA( ! __is_invocable( reference_wrapper<D> ) );
> > +SA( ! __is_invocable( reference_wrapper<D>& ) );
> > +SA( ! __is_invocable( reference_wrapper<D>&& ) );
> > +SA( ! __is_invocable( reference_wrapper<D>* ) );
> > +SA( ! __is_invocable( reference_wrapper<D*> ) );
> > +SA( ! __is_invocable( reference_wrapper<D*>* ) );
> > +
> > +std::function<void()> fn = []() {};
> > +auto refwrap = std::ref(fn);
> > +
> > +SA(   __is_invocable( decltype(fn) ) );
> > +SA(   __is_invocable( decltype(refwrap) ) );
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > new file mode 100644
> > index 00000000000..8699b0a53ca
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
> > @@ -0,0 +1,51 @@
> > +// { dg-do compile { target c++11 } }
> > +// __is_invocable should handle incomplete class correctly.
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +struct Incomplete;
> > +
> > +SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
> > +SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
> > +
> > +SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +
> > +SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
> > +SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > +SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
> > +
> > +template <typename T>
> > +struct Holder { T t; };
> > +
> > +SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
> > +
> > +// Define Incomplete, which is now not incomplete.
> > +struct Incomplete { void operator()(); };
> > +
> > +SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
> > diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > new file mode 100644
> > index 00000000000..d1efccf08f8
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
> > @@ -0,0 +1,33 @@
> > +// { dg-do compile { target c++11 } }
> > +// Failed access check should be a substitution failure, not an error.
> > +
> > +#define SA(X) static_assert((X),#X)
> > +
> > +template<bool B>
> > +struct bool_constant { static constexpr bool value = B; };
> > +
> > +template<typename _Fn, typename... _ArgTypes>
> > +struct is_invocable
> > +: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
> > +{ };
> > +
> > +#if __cpp_variable_templates
> > +template<typename _Fn, typename... _ArgTypes>
> > +constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
> > +#endif
> > +
> > +class Private
> > +{
> > +  void operator()() const
> > +  {
> > +    SA( ! is_invocable<Private>::value );
> > +#if __cpp_variable_templates
> > +    SA( ! is_invocable_v<Private> );
> > +#endif
> > +  }
> > +};
> > +
> > +SA( ! is_invocable<Private>::value );
> > +#if __cpp_variable_templates
> > +SA( ! is_invocable_v<Private> );
> > +#endif
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v21 20/23] c++: Implement __is_invocable built-in trait
  2024-05-03  3:25                           ` [PATCH v20 " Ken Matsui
  2024-05-03 19:38                             ` Jason Merrill
@ 2024-05-03 20:52                             ` Ken Matsui
  2024-05-07 18:36                               ` Jason Merrill
  2024-05-08  5:04                               ` [PATCH v22 " Ken Matsui
  1 sibling, 2 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-03 20:52 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

Fixed datum reference problem.  Ok for trunk?

-- >8 --

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 137 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 726 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index b1c875a6e7d..4e420d5390a 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 52d6841559c..8aa41f7147f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7340,6 +7340,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..80791227a0a 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,143 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree deref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = deref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be "
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      datum_type = TREE_TYPE (datum_expr);
+
+      bool datum_is_refwrap = false;
+      if (CLASS_TYPE_P (datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
+		  datum_is_refwrap = true;
+		}
+	    }
+	}
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_same_or_base_of_datum =
+	(same_type_p (ptrmem_class_type, datum_type)
+	 || (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, datum_type))));
+
+      if (!ptrmem_is_same_or_base_of_datum && !datum_is_refwrap)
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9ec1ba41d6e..05d215dbba4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12563,6 +12563,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12721,6 +12724,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12823,6 +12827,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index bcaa56a7bc5..d1c66267855 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -107,6 +107,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v18 02/26] libstdc++: Optimize std::is_const compilation performance
  2024-05-02 20:12                       ` [PATCH v18 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
@ 2024-05-07 13:55                         ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-07 13:55 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches, libstdc++, jason, Ken Matsui

Hi Jonathan,

Since __is_const, __is_volatile, and __is_pointer were approved, could
you please review these patches for libstdc++?  I guess that you
already reviewed almost equivalent patches, but I wanted to make sure.

Sincerely,
Ken Matsui


On Thu, May 2, 2024 at 1:16 PM Ken Matsui <kmatsui@gcc.gnu.org> wrote:
>
> This patch optimizes the compilation performance of std::is_const
> by dispatching to the new __is_const built-in trait.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/type_traits (is_const): Use __is_const built-in
>         trait.
>         (is_const_v): Likewise.
>
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>  libstdc++-v3/include/std/type_traits | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
>
> diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> index b441bf9908f..8df0cf3ac3b 100644
> --- a/libstdc++-v3/include/std/type_traits
> +++ b/libstdc++-v3/include/std/type_traits
> @@ -835,6 +835,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    // Type properties.
>
>    /// is_const
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
> +  template<typename _Tp>
> +    struct is_const
> +    : public __bool_constant<__is_const(_Tp)>
> +    { };
> +#else
>    template<typename>
>      struct is_const
>      : public false_type { };
> @@ -842,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    template<typename _Tp>
>      struct is_const<_Tp const>
>      : public true_type { };
> +#endif
>
>    /// is_volatile
>    template<typename>
> @@ -3331,10 +3338,15 @@ template <typename _Tp>
>    inline constexpr bool is_member_pointer_v = is_member_pointer<_Tp>::value;
>  #endif
>
> +#if _GLIBCXX_USE_BUILTIN_TRAIT(__is_const)
> +template <typename _Tp>
> +  inline constexpr bool is_const_v = __is_const(_Tp);
> +#else
>  template <typename _Tp>
>    inline constexpr bool is_const_v = false;
>  template <typename _Tp>
>    inline constexpr bool is_const_v<const _Tp> = true;
> +#endif
>
>  #if _GLIBCXX_USE_BUILTIN_TRAIT(__is_function)
>  template <typename _Tp>
> --
> 2.44.0
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v21 20/23] c++: Implement __is_invocable built-in trait
  2024-05-03 20:52                             ` [PATCH v21 20/23] " Ken Matsui
@ 2024-05-07 18:36                               ` Jason Merrill
  2024-05-07 18:46                                 ` Ken Matsui
  2024-05-08  5:04                               ` [PATCH v22 " Ken Matsui
  1 sibling, 1 reply; 244+ messages in thread
From: Jason Merrill @ 2024-05-07 18:36 UTC (permalink / raw)
  To: Ken Matsui, gcc-patches; +Cc: libstdc++

On 5/3/24 16:52, Ken Matsui wrote:
> Fixed datum reference problem.  Ok for trunk?
> 
> -- >8 --
> 
> This patch implements built-in trait for std::is_invocable.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-trait.def: Define __is_invocable.
> 	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> 	* semantics.cc (trait_expr_value): Likewise.
> 	(finish_trait_expr): Likewise.
> 	* cp-tree.h (build_invoke): New function.
> 	* method.cc (build_invoke): New function.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> 	* g++.dg/ext/is_invocable1.C: New test.
> 	* g++.dg/ext/is_invocable2.C: New test.
> 	* g++.dg/ext/is_invocable3.C: New test.
> 	* g++.dg/ext/is_invocable4.C: New test.
> 
> Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> ---
>   gcc/cp/constraint.cc                     |   6 +
>   gcc/cp/cp-trait.def                      |   1 +
>   gcc/cp/cp-tree.h                         |   2 +
>   gcc/cp/method.cc                         | 137 +++++++++
>   gcc/cp/semantics.cc                      |   5 +
>   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
>   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
>   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
>   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
>   10 files changed, 726 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
>   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index c28d7bf428e..6d14ef7dcc7 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
>       case CPTK_IS_FUNCTION:
>         inform (loc, "  %qT is not a function", t1);
>         break;
> +    case CPTK_IS_INVOCABLE:
> +      if (!t2)
> +    inform (loc, "  %qT is not invocable", t1);
> +      else
> +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> +      break;
>       case CPTK_IS_LAYOUT_COMPATIBLE:
>         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
>         break;
> diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> index b1c875a6e7d..4e420d5390a 100644
> --- a/gcc/cp/cp-trait.def
> +++ b/gcc/cp/cp-trait.def
> @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
>   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
>   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
>   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
>   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
>   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
>   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 52d6841559c..8aa41f7147f 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7340,6 +7340,8 @@ extern tree get_copy_assign			(tree);
>   extern tree get_default_ctor			(tree);
>   extern tree get_dtor				(tree, tsubst_flags_t);
>   extern tree build_stub_object			(tree);
> +extern tree build_invoke			(tree, const_tree,
> +						 tsubst_flags_t);
>   extern tree strip_inheriting_ctors		(tree);
>   extern tree inherited_ctor_binfo		(tree);
>   extern bool base_ctor_omit_inherited_parms	(tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 08a3d34fb01..80791227a0a 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1928,6 +1928,143 @@ build_trait_object (tree type)
>     return build_stub_object (type);
>   }
>   
> +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> +   given is not invocable, returns error_mark_node.  */
> +
> +tree
> +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> +{
> +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> +    return error_mark_node;
> +
> +  gcc_assert (TYPE_P (fn_type));
> +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> +
> +  /* Access check is required to determine if the given is invocable.  */
> +  deferring_access_check_sentinel acs (dk_no_deferred);
> +
> +  /* INVOKE is an unevaluated context.  */
> +  cp_unevaluated cp_uneval_guard;
> +
> +  bool is_ptrdatamem;
> +  bool is_ptrmemfunc;
> +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> +    {
> +      tree deref_fn_type = TREE_TYPE (fn_type);
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> +
> +      /* Dereference fn_type if it is a pointer to member.  */
> +      if (is_ptrdatamem || is_ptrmemfunc)
> +	fn_type = deref_fn_type;
> +    }
> +  else
> +    {
> +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> +    }
> +
> +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to data member type %qT can only be invoked with "
> +	       "one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> +    {
> +      if (complain & tf_error)
> +	error ("pointer to member function type %qT must be invoked with "
> +	       "at least one argument", fn_type);
> +      return error_mark_node;
> +    }
> +
> +  /* Construct an expression of a pointer to member.  */
> +  tree ptrmem_expr;
> +  if (is_ptrdatamem || is_ptrmemfunc)
> +    {
> +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> +
> +      /* datum must be a class type or a reference/pointer to a class type.  */
> +      if (!(CLASS_TYPE_P (datum_type)
> +	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> +		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> +	{
> +	  if (complain & tf_error)
> +	    error ("first argument type %qT of a pointer to member must be "
> +		   "a class type or a reference/pointer to a class type",
> +		   datum_type);
> +	  return error_mark_node;
> +	}
> +
> +      tree datum_expr = build_trait_object (datum_type);
> +      datum_type = TREE_TYPE (datum_expr);
> +
> +      bool datum_is_refwrap = false;
> +      if (CLASS_TYPE_P (datum_type))
> +	{
> +	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> +	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> +	  if (decl_in_std_namespace_p (datum_decl))
> +	    {
> +	      const_tree name = DECL_NAME (datum_decl);
> +	      if (name && (id_equal (name, "reference_wrapper")))
> +		{
> +		  /* Retrieve T from std::reference_wrapper<T>,
> +		     i.e., decltype(datum.get()).  */
> +		  datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> +		  datum_is_refwrap = true;

Hmm, with datum_expr moved up as I suggested, it's now wrong for the 
reference_wrapper case.  So either we need to override it here, or move 
it back down and do something else to handle reference to 
reference_wrapper, like another local variable non_ref_datum_type or 
something.

Jason


^ permalink raw reply	[flat|nested] 244+ messages in thread

* Re: [PATCH v21 20/23] c++: Implement __is_invocable built-in trait
  2024-05-07 18:36                               ` Jason Merrill
@ 2024-05-07 18:46                                 ` Ken Matsui
  0 siblings, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-07 18:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Ken Matsui, gcc-patches, libstdc++

On Tue, May 7, 2024 at 11:36 AM Jason Merrill <jason@redhat.com> wrote:
>
> On 5/3/24 16:52, Ken Matsui wrote:
> > Fixed datum reference problem.  Ok for trunk?
> >
> > -- >8 --
> >
> > This patch implements built-in trait for std::is_invocable.
> >
> > gcc/cp/ChangeLog:
> >
> >       * cp-trait.def: Define __is_invocable.
> >       * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
> >       * semantics.cc (trait_expr_value): Likewise.
> >       (finish_trait_expr): Likewise.
> >       * cp-tree.h (build_invoke): New function.
> >       * method.cc (build_invoke): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
> >       * g++.dg/ext/is_invocable1.C: New test.
> >       * g++.dg/ext/is_invocable2.C: New test.
> >       * g++.dg/ext/is_invocable3.C: New test.
> >       * g++.dg/ext/is_invocable4.C: New test.
> >
> > Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
> > ---
> >   gcc/cp/constraint.cc                     |   6 +
> >   gcc/cp/cp-trait.def                      |   1 +
> >   gcc/cp/cp-tree.h                         |   2 +
> >   gcc/cp/method.cc                         | 137 +++++++++
> >   gcc/cp/semantics.cc                      |   5 +
> >   gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
> >   gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
> >   gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
> >   gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
> >   10 files changed, 726 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
> >   create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C
> >
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index c28d7bf428e..6d14ef7dcc7 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
> >       case CPTK_IS_FUNCTION:
> >         inform (loc, "  %qT is not a function", t1);
> >         break;
> > +    case CPTK_IS_INVOCABLE:
> > +      if (!t2)
> > +    inform (loc, "  %qT is not invocable", t1);
> > +      else
> > +    inform (loc, "  %qT is not invocable by %qE", t1, t2);
> > +      break;
> >       case CPTK_IS_LAYOUT_COMPATIBLE:
> >         inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
> >         break;
> > diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
> > index b1c875a6e7d..4e420d5390a 100644
> > --- a/gcc/cp/cp-trait.def
> > +++ b/gcc/cp/cp-trait.def
> > @@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
> >   DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
> >   DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
> >   DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
> > +DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
> >   DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
> >   DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
> >   DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 52d6841559c..8aa41f7147f 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7340,6 +7340,8 @@ extern tree get_copy_assign                     (tree);
> >   extern tree get_default_ctor                        (tree);
> >   extern tree get_dtor                                (tree, tsubst_flags_t);
> >   extern tree build_stub_object                       (tree);
> > +extern tree build_invoke                     (tree, const_tree,
> > +                                              tsubst_flags_t);
> >   extern tree strip_inheriting_ctors          (tree);
> >   extern tree inherited_ctor_binfo            (tree);
> >   extern bool base_ctor_omit_inherited_parms  (tree);
> > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> > index 08a3d34fb01..80791227a0a 100644
> > --- a/gcc/cp/method.cc
> > +++ b/gcc/cp/method.cc
> > @@ -1928,6 +1928,143 @@ build_trait_object (tree type)
> >     return build_stub_object (type);
> >   }
> >
> > +/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
> > +   given is not invocable, returns error_mark_node.  */
> > +
> > +tree
> > +build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> > +{
> > +  if (error_operand_p (fn_type) || error_operand_p (arg_types))
> > +    return error_mark_node;
> > +
> > +  gcc_assert (TYPE_P (fn_type));
> > +  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
> > +
> > +  /* Access check is required to determine if the given is invocable.  */
> > +  deferring_access_check_sentinel acs (dk_no_deferred);
> > +
> > +  /* INVOKE is an unevaluated context.  */
> > +  cp_unevaluated cp_uneval_guard;
> > +
> > +  bool is_ptrdatamem;
> > +  bool is_ptrmemfunc;
> > +  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
> > +    {
> > +      tree deref_fn_type = TREE_TYPE (fn_type);
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (deref_fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (deref_fn_type);
> > +
> > +      /* Dereference fn_type if it is a pointer to member.  */
> > +      if (is_ptrdatamem || is_ptrmemfunc)
> > +     fn_type = deref_fn_type;
> > +    }
> > +  else
> > +    {
> > +      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
> > +      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
> > +    }
> > +
> > +  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to data member type %qT can only be invoked with "
> > +            "one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
> > +    {
> > +      if (complain & tf_error)
> > +     error ("pointer to member function type %qT must be invoked with "
> > +            "at least one argument", fn_type);
> > +      return error_mark_node;
> > +    }
> > +
> > +  /* Construct an expression of a pointer to member.  */
> > +  tree ptrmem_expr;
> > +  if (is_ptrdatamem || is_ptrmemfunc)
> > +    {
> > +      tree datum_type = TREE_VEC_ELT (arg_types, 0);
> > +
> > +      /* datum must be a class type or a reference/pointer to a class type.  */
> > +      if (!(CLASS_TYPE_P (datum_type)
> > +         || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
> > +             && CLASS_TYPE_P (TREE_TYPE (datum_type)))))
> > +     {
> > +       if (complain & tf_error)
> > +         error ("first argument type %qT of a pointer to member must be "
> > +                "a class type or a reference/pointer to a class type",
> > +                datum_type);
> > +       return error_mark_node;
> > +     }
> > +
> > +      tree datum_expr = build_trait_object (datum_type);
> > +      datum_type = TREE_TYPE (datum_expr);
> > +
> > +      bool datum_is_refwrap = false;
> > +      if (CLASS_TYPE_P (datum_type))
> > +     {
> > +       /* 1.2 & 1.5: Handle std::reference_wrapper.  */
> > +       tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (datum_type));
> > +       if (decl_in_std_namespace_p (datum_decl))
> > +         {
> > +           const_tree name = DECL_NAME (datum_decl);
> > +           if (name && (id_equal (name, "reference_wrapper")))
> > +             {
> > +               /* Retrieve T from std::reference_wrapper<T>,
> > +                  i.e., decltype(datum.get()).  */
> > +               datum_type = TREE_VEC_ELT (TYPE_TI_ARGS (datum_type), 0);
> > +               datum_is_refwrap = true;
>
> Hmm, with datum_expr moved up as I suggested, it's now wrong for the
> reference_wrapper case.  So either we need to override it here, or move
> it back down and do something else to handle reference to
> reference_wrapper, like another local variable non_ref_datum_type or
> something.

Ahh, I totally missed this!  Thank you for your review!  Let me think of this.

>
> Jason
>

^ permalink raw reply	[flat|nested] 244+ messages in thread

* [PATCH v22 20/23] c++: Implement __is_invocable built-in trait
  2024-05-03 20:52                             ` [PATCH v21 20/23] " Ken Matsui
  2024-05-07 18:36                               ` Jason Merrill
@ 2024-05-08  5:04                               ` Ken Matsui
  1 sibling, 0 replies; 244+ messages in thread
From: Ken Matsui @ 2024-05-08  5:04 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, jason, Ken Matsui

Fixed the reference_wrapper case.  I used non_ref_datum_type to avoid
potentially multiple build_trait_object calls.

-- >8 --

This patch implements built-in trait for std::is_invocable.

gcc/cp/ChangeLog:

	* cp-trait.def: Define __is_invocable.
	* constraint.cc (diagnose_trait_expr): Handle CPTK_IS_INVOCABLE.
	* semantics.cc (trait_expr_value): Likewise.
	(finish_trait_expr): Likewise.
	* cp-tree.h (build_invoke): New function.
	* method.cc (build_invoke): New function.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/has-builtin-1.C: Test existence of __is_invocable.
	* g++.dg/ext/is_invocable1.C: New test.
	* g++.dg/ext/is_invocable2.C: New test.
	* g++.dg/ext/is_invocable3.C: New test.
	* g++.dg/ext/is_invocable4.C: New test.

Signed-off-by: Ken Matsui <kmatsui@gcc.gnu.org>
---
 gcc/cp/constraint.cc                     |   6 +
 gcc/cp/cp-trait.def                      |   1 +
 gcc/cp/cp-tree.h                         |   2 +
 gcc/cp/method.cc                         | 142 +++++++++
 gcc/cp/semantics.cc                      |   5 +
 gcc/testsuite/g++.dg/ext/has-builtin-1.C |   3 +
 gcc/testsuite/g++.dg/ext/is_invocable1.C | 349 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/ext/is_invocable2.C | 139 +++++++++
 gcc/testsuite/g++.dg/ext/is_invocable3.C |  51 ++++
 gcc/testsuite/g++.dg/ext/is_invocable4.C |  33 +++
 10 files changed, 731 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable2.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable4.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index c28d7bf428e..6d14ef7dcc7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3792,6 +3792,12 @@ diagnose_trait_expr (tree expr, tree args)
     case CPTK_IS_FUNCTION:
       inform (loc, "  %qT is not a function", t1);
       break;
+    case CPTK_IS_INVOCABLE:
+      if (!t2)
+    inform (loc, "  %qT is not invocable", t1);
+      else
+    inform (loc, "  %qT is not invocable by %qE", t1, t2);
+      break;
     case CPTK_IS_LAYOUT_COMPATIBLE:
       inform (loc, "  %qT is not layout compatible with %qT", t1, t2);
       break;
diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def
index b1c875a6e7d..4e420d5390a 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -75,6 +75,7 @@ DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1)
 DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1)
 DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1)
 DEFTRAIT_EXPR (IS_FUNCTION, "__is_function", 1)
+DEFTRAIT_EXPR (IS_INVOCABLE, "__is_invocable", -1)
 DEFTRAIT_EXPR (IS_LAYOUT_COMPATIBLE, "__is_layout_compatible", 2)
 DEFTRAIT_EXPR (IS_LITERAL_TYPE, "__is_literal_type", 1)
 DEFTRAIT_EXPR (IS_MEMBER_FUNCTION_POINTER, "__is_member_function_pointer", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 52d6841559c..8aa41f7147f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7340,6 +7340,8 @@ extern tree get_copy_assign			(tree);
 extern tree get_default_ctor			(tree);
 extern tree get_dtor				(tree, tsubst_flags_t);
 extern tree build_stub_object			(tree);
+extern tree build_invoke			(tree, const_tree,
+						 tsubst_flags_t);
 extern tree strip_inheriting_ctors		(tree);
 extern tree inherited_ctor_binfo		(tree);
 extern bool base_ctor_omit_inherited_parms	(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 08a3d34fb01..1c3233ca5d7 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1928,6 +1928,148 @@ build_trait_object (tree type)
   return build_stub_object (type);
 }
 
+/* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If the
+   given is not invocable, returns error_mark_node.  */
+
+tree
+build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
+{
+  if (error_operand_p (fn_type) || error_operand_p (arg_types))
+    return error_mark_node;
+
+  gcc_assert (TYPE_P (fn_type));
+  gcc_assert (TREE_CODE (arg_types) == TREE_VEC);
+
+  /* Access check is required to determine if the given is invocable.  */
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
+  /* INVOKE is an unevaluated context.  */
+  cp_unevaluated cp_uneval_guard;
+
+  bool is_ptrdatamem;
+  bool is_ptrmemfunc;
+  if (TREE_CODE (fn_type) == REFERENCE_TYPE)
+    {
+      tree non_ref_fn_type = TREE_TYPE (fn_type);
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (non_ref_fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (non_ref_fn_type);
+
+      /* Dereference fn_type if it is a pointer to member.  */
+      if (is_ptrdatamem || is_ptrmemfunc)
+	fn_type = non_ref_fn_type;
+    }
+  else
+    {
+      is_ptrdatamem = TYPE_PTRDATAMEM_P (fn_type);
+      is_ptrmemfunc = TYPE_PTRMEMFUNC_P (fn_type);
+    }
+
+  if (is_ptrdatamem && TREE_VEC_LENGTH (arg_types) != 1)
+    {
+      if (complain & tf_error)
+	error ("pointer to data member type %qT can only be invoked with "
+	       "one argument", fn_type);
+      return error_mark_node;
+    }
+
+  if (is_ptrmemfunc && TREE_VEC_LENGTH (arg_types) == 0)
+    {
+      if (complain & tf_error)
+	error ("pointer to member function type %qT must be invoked with "
+	       "at least one argument", fn_type);
+      return error_mark_node;
+    }
+
+  /* Construct an expression of a pointer to member.  */
+  tree ptrmem_expr;
+  if (is_ptrdatamem || is_ptrmemfunc)
+    {
+      tree datum_type = TREE_VEC_ELT (arg_types, 0);
+
+      /* datum must be a class type or a reference/pointer to a class type.  */
+      if (!(CLASS_TYPE_P (datum_type)
+	    || ((TYPE_REF_P (datum_type) || POINTER_TYPE_P (datum_type))
+		&& CLASS_TYPE_P (TREE_TYPE (datum_type)))))
+	{
+	  if (complain & tf_error)
+	    error ("first argument type %qT of a pointer to member must be "
+		   "a class type or a reference/pointer to a class type",
+		   datum_type);
+	  return error_mark_node;
+	}
+
+      tree non_ref_datum_type = datum_type;
+      if (TYPE_REF_P (datum_type))
+	non_ref_datum_type = TREE_TYPE (datum_type);
+
+      bool datum_is_refwrap = false;
+      if (CLASS_TYPE_P (non_ref_datum_type))
+	{
+	  /* 1.2 & 1.5: Handle std::reference_wrapper.  */
+	  tree datum_decl = TYPE_NAME (TYPE_MAIN_VARIANT (non_ref_datum_type));
+	  if (decl_in_std_namespace_p (datum_decl))
+	    {
+	      const_tree name = DECL_NAME (datum_decl);
+	      if (name && (id_equal (name, "reference_wrapper")))
+		{
+		  /* Retrieve T from std::reference_wrapper<T>,
+		     i.e., decltype(datum.get()).  */
+		  datum_type =
+		    TREE_VEC_ELT (TYPE_TI_ARGS (non_ref_datum_type), 0);
+		  datum_is_refwrap = true;
+		}
+	    }
+	}
+
+      tree datum_expr = build_trait_object (datum_type);
+      non_ref_datum_type = TREE_TYPE (datum_expr);
+
+      tree ptrmem_class_type = TYPE_PTRMEM_CLASS_TYPE (fn_type);
+      const bool ptrmem_is_same_or_base_of_datum =
+	(same_type_p (ptrmem_class_type, non_ref_datum_type)
+	 || (NON_UNION_CLASS_TYPE_P (ptrmem_class_type)
+	     && NON_UNION_CLASS_TYPE_P (non_ref_datum_type)
+	     && (same_type_ignoring_top_level_qualifiers_p (ptrmem_class_type,
+							    non_ref_datum_type)
+		 || DERIVED_FROM_P (ptrmem_class_type, non_ref_datum_type))));
+
+      if (!ptrmem_is_same_or_base_of_datum && !datum_is_refwrap)
+	/* 1.3 & 1.6: Try to dereference datum_expr.  */
+	datum_expr = build_x_indirect_ref (UNKNOWN_LOCATION, datum_expr,
+					   RO_UNARY_STAR, NULL_TREE, complain);
+      /* 1.1, 1.2, 1.4, & 1.5: Otherwise.  */
+
+      tree fn_expr = build_trait_object (fn_type);
+      ptrmem_expr = build_m_component_ref (datum_expr, fn_expr, complain);
+
+      if (error_operand_p (ptrmem_expr))
+	return error_mark_node;
+
+      if (is_ptrdatamem)
+	return ptrmem_expr;
+    }
+
+  /* Construct expressions for arguments to INVOKE.  For a pointer to member
+     function, the first argument, which is the object, is not arguments to
+     the function.  */
+  releasing_vec args;
+  for (int i = is_ptrmemfunc ? 1 : 0; i < TREE_VEC_LENGTH (arg_types); ++i)
+    {
+      tree arg_type = TREE_VEC_ELT (arg_types, i);
+      tree arg = build_trait_object (arg_type);
+      vec_safe_push (args, arg);
+    }
+
+  tree invoke_expr;
+  if (is_ptrmemfunc)
+    invoke_expr = build_offset_ref_call_from_tree (ptrmem_expr, &args,
+						   complain);
+  else  /* 1.7.  */
+    invoke_expr = finish_call_expr (build_trait_object (fn_type), &args, false,
+				    false, complain);
+  return invoke_expr;
+}
+
 /* Determine which function will be called when looking up NAME in TYPE,
    called with a single ARGTYPE argument, or no argument if ARGTYPE is
    null.  FLAGS and COMPLAIN are as for build_new_method_call.
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 9ec1ba41d6e..05d215dbba4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -12563,6 +12563,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_FUNCTION:
       return type_code1 == FUNCTION_TYPE;
 
+    case CPTK_IS_INVOCABLE:
+      return !error_operand_p (build_invoke (type1, type2, tf_none));
+
     case CPTK_IS_LAYOUT_COMPATIBLE:
       return layout_compatible_type_p (type1, type2);
 
@@ -12721,6 +12724,7 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
     case CPTK_REF_CONVERTS_FROM_TEMPORARY:
       to = type1;
@@ -12823,6 +12827,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
 
     case CPTK_IS_CONSTRUCTIBLE:
     case CPTK_IS_CONVERTIBLE:
+    case CPTK_IS_INVOCABLE:
     case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
     case CPTK_IS_NOTHROW_CONVERTIBLE:
     case CPTK_IS_TRIVIALLY_CONSTRUCTIBLE:
diff --git a/gcc/testsuite/g++.dg/ext/has-builtin-1.C b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
index bcaa56a7bc5..d1c66267855 100644
--- a/gcc/testsuite/g++.dg/ext/has-builtin-1.C
+++ b/gcc/testsuite/g++.dg/ext/has-builtin-1.C
@@ -107,6 +107,9 @@
 #if !__has_builtin (__is_function)
 # error "__has_builtin (__is_function) failed"
 #endif
+#if !__has_builtin (__is_invocable)
+# error "__has_builtin (__is_invocable) failed"
+#endif
 #if !__has_builtin (__is_layout_compatible)
 # error "__has_builtin (__is_layout_compatible) failed"
 #endif
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable1.C b/gcc/testsuite/g++.dg/ext/is_invocable1.C
new file mode 100644
index 00000000000..d21ae1d1958
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable1.C
@@ -0,0 +1,349 @@
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert((X),#X)
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( func_type_v0 ) );
+SA( ! __is_invocable( func_type_v0, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( func_type_i0 ) );
+SA( ! __is_invocable( func_type_i0, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( func_type_l0 ) );
+SA( ! __is_invocable( func_type_l0(int) ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( func_type_ii ) );
+SA(   __is_invocable( func_type_ii, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( func_type_il ) );
+SA( ! __is_invocable( func_type_il, int ) );
+SA(   __is_invocable( func_type_il, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( func_type_ir ) );
+SA( ! __is_invocable( func_type_ir, int& ) );
+SA(   __is_invocable( func_type_ir, int ) );
+SA(   __is_invocable( func_type_ir, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( mem_type_i ) );
+SA( ! __is_invocable( mem_type_i, int ) );
+SA( ! __is_invocable( mem_type_i, int* ) );
+SA( ! __is_invocable( mem_type_i, int& ) );
+SA( ! __is_invocable( mem_type_i, int&& ) );
+SA(   __is_invocable( mem_type_i, A ) );
+SA(   __is_invocable( mem_type_i, A* ) );
+SA(   __is_invocable( mem_type_i, A& ) );
+SA(   __is_invocable( mem_type_i, A&& ) );
+SA(   __is_invocable( mem_type_i, const A& ) );
+SA( ! __is_invocable( mem_type_i, A&, int ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( memfun_type_i ) );
+SA( ! __is_invocable( memfun_type_i, int ) );
+SA( ! __is_invocable( memfun_type_i, int* ) );
+SA( ! __is_invocable( memfun_type_i, int& ) );
+SA( ! __is_invocable( memfun_type_i, int&& ) );
+SA(   __is_invocable( memfun_type_i, A ) );
+SA(   __is_invocable( memfun_type_i, A* ) );
+SA(   __is_invocable( memfun_type_i, A& ) );
+SA(   __is_invocable( memfun_type_i, A&& ) );
+SA( ! __is_invocable( memfun_type_i, const A& ) );
+SA( ! __is_invocable( memfun_type_i, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( memfun_type_ic ) );
+SA( ! __is_invocable( memfun_type_ic, int ) );
+SA( ! __is_invocable( memfun_type_ic, int& ) );
+SA(   __is_invocable( memfun_type_ic, A& ) );
+SA(   __is_invocable( memfun_type_ic, A* ) );
+SA( ! __is_invocable( memfun_type_ic, A&, int ) );
+SA( ! __is_invocable( memfun_type_ic, A*, int& ) );
+SA(   __is_invocable( memfun_type_ic, const A& ) );
+SA(   __is_invocable( memfun_type_ic, const A* ) );
+SA( ! __is_invocable( memfun_type_ic, const A&, int& ) );
+SA( ! __is_invocable( memfun_type_ic, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( memfun_type_iic ) );
+SA( ! __is_invocable( memfun_type_iic, int ) );
+SA( ! __is_invocable( memfun_type_iic, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A&, int ) );
+SA(   __is_invocable( memfun_type_iic, A&, int& ) );
+SA( ! __is_invocable( memfun_type_iic, A*, int ) );
+SA(   __is_invocable( memfun_type_iic, A*, int& ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int ) );
+SA( ! __is_invocable( memfun_type_iic, const A&, int&, int ) );
+SA(   __is_invocable( memfun_type_iic, const A&, int& ) );
+SA(   __is_invocable( memfun_type_iic, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( B ) );
+SA(   __is_invocable( B& ) );
+SA(   __is_invocable( B&& ) );
+SA( ! __is_invocable( B* ) );
+SA(   __is_invocable( CB ) );
+SA(   __is_invocable( CB& ) );
+SA( ! __is_invocable( CB* ) );
+
+SA(   __is_invocable( B, int ) );
+SA(   __is_invocable( B&, int ) );
+SA(   __is_invocable( B&&, int ) );
+SA( ! __is_invocable( B*, int ) );
+SA( ! __is_invocable( CB, int ) );
+SA( ! __is_invocable( CB&, int ) );
+SA( ! __is_invocable( CB*, int ) );
+
+SA( ! __is_invocable( B, int, int ) );
+SA( ! __is_invocable( B&, int, int ) );
+SA( ! __is_invocable( B&&, int, int ) );
+SA( ! __is_invocable( B*, int, int ) );
+SA( ! __is_invocable( CB, int, int ) );
+SA( ! __is_invocable( CB&, int, int ) );
+SA( ! __is_invocable( CB*, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( C ) );
+SA( ! __is_invocable( C& ) );
+SA( ! __is_invocable( C&& ) );
+SA( ! __is_invocable( C* ) );
+SA( ! __is_invocable( CC ) );
+SA( ! __is_invocable( CC& ) );
+SA( ! __is_invocable( CC* ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( D ) );
+
+struct E { void v(); };
+using CE = const E;
+
+SA( ! __is_invocable( E ) );
+SA( ! __is_invocable( void (E::*)() ) );
+SA(   __is_invocable( void (E::*)(), E ) );
+SA(   __is_invocable( void (E::*)(), E* ) );
+SA( ! __is_invocable( void (E::*)(), CE ) );
+
+struct F : E {};
+using CF = const F;
+
+SA( ! __is_invocable( F ) );
+SA(   __is_invocable( void (E::*)(), F ) );
+SA(   __is_invocable( void (E::*)(), F* ) );
+SA( ! __is_invocable( void (E::*)(), CF ) );
+
+struct G { E operator*(); };
+using CG = const G;
+
+SA( ! __is_invocable( G ) );
+SA(   __is_invocable( void (E::*)(), G ) );
+SA( ! __is_invocable( void (E::*)(), G* ) );
+SA( ! __is_invocable( void (E::*)(), CG ) );
+
+struct H { E& operator*(); };
+using CH = const H;
+
+SA( ! __is_invocable( H ) );
+SA(   __is_invocable( void (E::*)(), H ) );
+SA( ! __is_invocable( void (E::*)(), H* ) );
+SA( ! __is_invocable( void (E::*)(), CH ) );
+
+struct I { E&& operator*(); };
+using CI = const I;
+
+SA( ! __is_invocable( I ) );
+SA(   __is_invocable( void (E::*)(), I ) );
+SA( ! __is_invocable( void (E::*)(), I* ) );
+SA( ! __is_invocable( void (E::*)(), CI ) );
+
+struct K { E* operator*(); };
+using CK = const K;
+
+SA( ! __is_invocable( K ) );
+SA( ! __is_invocable( void (E::*)(), K ) );
+SA( ! __is_invocable( void (E::*)(), K* ) );
+SA( ! __is_invocable( void (E::*)(), CK ) );
+
+struct L { CE operator*(); };
+using CL = const L;
+
+SA( ! __is_invocable( L ) );
+SA( ! __is_invocable( void (E::*)(), L ) );
+SA( ! __is_invocable( void (E::*)(), L* ) );
+SA( ! __is_invocable( void (E::*)(), CL ) );
+
+struct M {
+  int i;
+private:
+  long l;
+};
+using CM = const M;
+
+SA( ! __is_invocable( M ) );
+SA( ! __is_invocable( M& ) );
+SA( ! __is_invocable( M&& ) );
+SA( ! __is_invocable( M* ) );
+SA( ! __is_invocable( CM ) );
+SA( ! __is_invocable( CM& ) );
+SA( ! __is_invocable( CM* ) );
+
+SA( ! __is_invocable( int M::* ) );
+SA(   __is_invocable( int M::*, M ) );
+SA(   __is_invocable( int M::*, M& ) );
+SA(   __is_invocable( int M::*, M&& ) );
+SA(   __is_invocable( int M::*, M* ) );
+SA(   __is_invocable( int M::*, CM ) );
+SA(   __is_invocable( int M::*, CM& ) );
+SA(   __is_invocable( int M::*, CM* ) );
+SA( ! __is_invocable( int M::*, int ) );
+
+SA( ! __is_invocable( int CM::* ) );
+SA(   __is_invocable( int CM::*, M ) );
+SA(   __is_invocable( int CM::*, M& ) );
+SA(   __is_invocable( int CM::*, M&& ) );
+SA(   __is_invocable( int CM::*, M* ) );
+SA(   __is_invocable( int CM::*, CM ) );
+SA(   __is_invocable( int CM::*, CM& ) );
+SA(   __is_invocable( int CM::*, CM* ) );
+SA( ! __is_invocable( int CM::*, int ) );
+
+SA( ! __is_invocable( long M::* ) );
+SA(   __is_invocable( long M::*, M ) );
+SA(   __is_invocable( long M::*, M& ) );
+SA(   __is_invocable( long M::*, M&& ) );
+SA(   __is_invocable( long M::*, M* ) );
+SA(   __is_invocable( long M::*, CM ) );
+SA(   __is_invocable( long M::*, CM& ) );
+SA(   __is_invocable( long M::*, CM* ) );
+SA( ! __is_invocable( long M::*, long ) );
+
+SA( ! __is_invocable( long CM::* ) );
+SA(   __is_invocable( long CM::*, M ) );
+SA(   __is_invocable( long CM::*, M& ) );
+SA(   __is_invocable( long CM::*, M&& ) );
+SA(   __is_invocable( long CM::*, M* ) );
+SA(   __is_invocable( long CM::*, CM ) );
+SA(   __is_invocable( long CM::*, CM& ) );
+SA(   __is_invocable( long CM::*, CM* ) );
+SA( ! __is_invocable( long CM::*, long ) );
+
+SA( ! __is_invocable( short M::* ) );
+SA(   __is_invocable( short M::*, M ) );
+SA(   __is_invocable( short M::*, M& ) );
+SA(   __is_invocable( short M::*, M&& ) );
+SA(   __is_invocable( short M::*, M* ) );
+SA(   __is_invocable( short M::*, CM ) );
+SA(   __is_invocable( short M::*, CM& ) );
+SA(   __is_invocable( short M::*, CM* ) );
+SA( ! __is_invocable( short M::*, short ) );
+
+SA( ! __is_invocable( short CM::* ) );
+SA(   __is_invocable( short CM::*, M ) );
+SA(   __is_invocable( short CM::*, M& ) );
+SA(   __is_invocable( short CM::*, M&& ) );
+SA(   __is_invocable( short CM::*, M* ) );
+SA(   __is_invocable( short CM::*, CM ) );
+SA(   __is_invocable( short CM::*, CM& ) );
+SA(   __is_invocable( short CM::*, CM* ) );
+SA( ! __is_invocable( short CM::*, short ) );
+
+struct N { M operator*(); };
+SA(   __is_invocable( int M::*, N ) );
+SA( ! __is_invocable( int M::*, N* ) );
+
+struct O { M& operator*(); };
+SA(   __is_invocable( int M::*, O ) );
+SA( ! __is_invocable( int M::*, O* ) );
+
+struct P { M&& operator*(); };
+SA(   __is_invocable( int M::*, P ) );
+SA( ! __is_invocable( int M::*, P* ) );
+
+struct Q { M* operator*(); };
+SA( ! __is_invocable( int M::*, Q ) );
+SA( ! __is_invocable( int M::*, Q* ) );
+
+struct R { void operator()(int = 0); };
+
+SA(   __is_invocable( R ) );
+SA(   __is_invocable( R, int ) );
+SA( ! __is_invocable( R, int, int ) );
+
+struct S { void operator()(int, ...); };
+
+SA( ! __is_invocable( S ) );
+SA(   __is_invocable( S, int ) );
+SA(   __is_invocable( S, int, int ) );
+SA(   __is_invocable( S, int, int, int ) );
+
+void fn1() {}
+
+SA(   __is_invocable( decltype(fn1) ) );
+
+void fn2(int arr[10]);
+
+SA(   __is_invocable( decltype(fn2), int[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int(&&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&)[10] ) );
+SA( ! __is_invocable( decltype(fn2), int(*&&)[10] ) );
+SA(   __is_invocable( decltype(fn2), int[] ) );
+
+auto lambda = []() {};
+
+SA(   __is_invocable( decltype(lambda) ) );
+
+template <typename Func, typename... Args>
+struct can_invoke {
+    static constexpr bool value = __is_invocable( Func, Args... );
+};
+
+SA(   can_invoke<decltype(lambda)>::value );
+
+struct T {
+  void func() const {}
+  int data;
+};
+
+SA(   __is_invocable( decltype(&T::func)&, T& ) );
+SA(   __is_invocable( decltype(&T::data)&, T& ) );
+
+struct U { };
+struct V : U { U& operator*() = delete; };
+SA(   __is_invocable( int U::*, V ) );
+
+struct W : private U { U& operator*(); };
+SA( ! __is_invocable( int U::*, W ) );
+
+struct X { int m; };
+struct Y { X& operator*(); };
+struct Z : Y { };
+SA(   __is_invocable(int X::*, Z) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable2.C b/gcc/testsuite/g++.dg/ext/is_invocable2.C
new file mode 100644
index 00000000000..a68aefd3e13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable2.C
@@ -0,0 +1,139 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle std::reference_wrapper correctly.
+
+#include <functional>
+
+#define SA(X) static_assert((X),#X)
+
+using std::reference_wrapper;
+
+using func_type_v0 = void(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_v0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_v0>, int ) );
+
+using func_type_i0 = int(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_i0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_i0>, int ) );
+
+using func_type_l0 = int&(*)();
+
+SA(   __is_invocable( reference_wrapper<func_type_l0> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_l0(int)> ) );
+
+using func_type_ii = int(*)(int);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ii> ) );
+SA(   __is_invocable( reference_wrapper<func_type_ii>, int ) );
+
+using func_type_il = int(*)(int&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_il> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_il>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_il>, int& ) );
+
+using func_type_ir = int(*)(int&&);
+
+SA( ! __is_invocable( reference_wrapper<func_type_ir> ) );
+SA( ! __is_invocable( reference_wrapper<func_type_ir>, int& ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int ) );
+SA(   __is_invocable( reference_wrapper<func_type_ir>, int&& ) );
+
+struct A { };
+
+using mem_type_i = int A::*;
+
+SA( ! __is_invocable( reference_wrapper<mem_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<mem_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<mem_type_i>, A&& ) );
+
+using memfun_type_i = int (A::*)();
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_i> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, int&& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A* ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_i>, A&& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, const A& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_i>, A&, int ) );
+
+using memfun_type_ic = int (A::*)() const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, A*, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_ic>, const A* ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_ic>, const A*, int ) );
+
+using memfun_type_iic = int& (A::*)(int&) const;
+
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic> ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A&, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, A*, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, A*, int& ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int ) );
+SA( ! __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int&, int ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A&, int& ) );
+SA(   __is_invocable( reference_wrapper<memfun_type_iic>, const A*, int& ) );
+
+struct B {
+  int& operator()();
+  long& operator()() const;
+  bool& operator()(int);
+private:
+  void operator()(int, int);
+};
+using CB = const B;
+
+SA(   __is_invocable( reference_wrapper<B> ) );
+SA(   __is_invocable( reference_wrapper<B>& ) );
+SA(   __is_invocable( reference_wrapper<B>&& ) );
+SA(   __is_invocable( reference_wrapper<CB> ) );
+SA(   __is_invocable( reference_wrapper<CB>& ) );
+SA(   __is_invocable( reference_wrapper<B>, int ) );
+SA( ! __is_invocable( reference_wrapper<B>&, int, int ) );
+
+struct C : B { int& operator()() = delete; };
+using CC = const C;
+
+SA( ! __is_invocable( reference_wrapper<C> ) );
+SA( ! __is_invocable( reference_wrapper<C>& ) );
+SA( ! __is_invocable( reference_wrapper<C>&& ) );
+SA( ! __is_invocable( reference_wrapper<CC> ) );
+SA( ! __is_invocable( reference_wrapper<CC>& ) );
+
+struct D { B operator*(); };
+using CD = const D;
+
+SA( ! __is_invocable( reference_wrapper<D> ) );
+SA( ! __is_invocable( reference_wrapper<D>& ) );
+SA( ! __is_invocable( reference_wrapper<D>&& ) );
+SA( ! __is_invocable( reference_wrapper<D>* ) );
+SA( ! __is_invocable( reference_wrapper<D*> ) );
+SA( ! __is_invocable( reference_wrapper<D*>* ) );
+
+std::function<void()> fn = []() {};
+auto refwrap = std::ref(fn);
+
+SA(   __is_invocable( decltype(fn) ) );
+SA(   __is_invocable( decltype(refwrap) ) );
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable3.C b/gcc/testsuite/g++.dg/ext/is_invocable3.C
new file mode 100644
index 00000000000..8699b0a53ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// __is_invocable should handle incomplete class correctly.
+
+#define SA(X) static_assert((X),#X)
+
+struct Incomplete;
+
+SA( ! __is_invocable( Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int, Incomplete, int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int, Incomplete ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete() ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int), int ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( Incomplete, Incomplete(int, int), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( Incomplete, Incomplete(), int, int ) ); // { dg-error "incomplete type" }
+
+SA( ! __is_invocable( int(Incomplete), Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), int, Incomplete ) ); // { dg-error "incomplete type" }
+SA( ! __is_invocable( int(int, Incomplete), Incomplete, int ) ); // { dg-error "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(Incomplete&&), Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, Incomplete&&), int, Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&&), const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&&), int, const Incomplete&& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int(const Incomplete&), Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( int(int, const Incomplete&), int, Incomplete& ) ); // { dg-bogus "incomplete type" }
+
+SA(   __is_invocable( int Incomplete::*, const Incomplete& ) ); // { dg-bogus "incomplete type" }
+SA( ! __is_invocable( void (Incomplete::*)(long&), const Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+SA(   __is_invocable( void (Incomplete::*)(long&) const, Incomplete*, long& ) ); // { dg-bogus "incomplete type" }
+
+template <typename T>
+struct Holder { T t; };
+
+SA(   __is_invocable( int(Holder<Incomplete>&), Holder<Incomplete>& ) ); // { dg-bogus "incomplete type" }
+
+// Define Incomplete, which is now not incomplete.
+struct Incomplete { void operator()(); };
+
+SA(   __is_invocable( Incomplete ) ); // { dg-bogus "incomplete type" }
diff --git a/gcc/testsuite/g++.dg/ext/is_invocable4.C b/gcc/testsuite/g++.dg/ext/is_invocable4.C
new file mode 100644
index 00000000000..d1efccf08f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/is_invocable4.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+// Failed access check should be a substitution failure, not an error.
+
+#define SA(X) static_assert((X),#X)
+
+template<bool B>
+struct bool_constant { static constexpr bool value = B; };
+
+template<typename _Fn, typename... _ArgTypes>
+struct is_invocable
+: public bool_constant<__is_invocable(_Fn, _ArgTypes...)>
+{ };
+
+#if __cpp_variable_templates
+template<typename _Fn, typename... _ArgTypes>
+constexpr bool is_invocable_v = __is_invocable(_Fn, _ArgTypes...);
+#endif
+
+class Private
+{
+  void operator()() const
+  {
+    SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+    SA( ! is_invocable_v<Private> );
+#endif
+  }
+};
+
+SA( ! is_invocable<Private>::value );
+#if __cpp_variable_templates
+SA( ! is_invocable_v<Private> );
+#endif
-- 
2.44.0


^ permalink raw reply	[flat|nested] 244+ messages in thread

end of thread, other threads:[~2024-05-08  5:08 UTC | newest]

Thread overview: 244+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-23 22:02 [PATCH v2 0/8] Optimize more type traits Ken Matsui
2023-12-23 22:02 ` [PATCH v2 1/8] c++: Implement __is_const built-in trait Ken Matsui
2023-12-23 22:02 ` [PATCH v2 2/8] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2023-12-23 22:02 ` [PATCH v2 3/8] c++: Implement __is_volatile built-in trait Ken Matsui
2023-12-23 22:02 ` [PATCH v2 4/8] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2023-12-23 22:02 ` [PATCH v2 5/8] c++: Implement __is_pointer built-in trait Ken Matsui
2023-12-23 22:02 ` [PATCH v2 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-01-04 22:06   ` Patrick Palka
2024-01-04 22:16     ` Patrick Palka
2024-01-06  4:59       ` Ken Matsui
2024-01-04 22:13   ` Jonathan Wakely
2024-01-06  4:59     ` Ken Matsui
2023-12-23 22:02 ` [PATCH v2 7/8] c++: Implement __is_unbounded_array built-in trait Ken Matsui
2023-12-23 22:02 ` [PATCH v2 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-01-06  5:05 ` [PATCH v3 0/8] Optimize more type traits Ken Matsui
2024-01-06  5:05   ` [PATCH v3 1/8] c++: Implement __is_const built-in trait Ken Matsui
2024-01-06  5:05   ` [PATCH v3 2/8] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-01-06  5:05   ` [PATCH v3 3/8] c++: Implement __is_volatile built-in trait Ken Matsui
2024-01-06  5:05   ` [PATCH v3 4/8] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-01-06  5:05   ` [PATCH v3 5/8] c++: Implement __is_pointer built-in trait Ken Matsui
2024-01-06  5:05   ` [PATCH v3 6/8] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-01-06  5:05   ` [PATCH v3 7/8] c++: Implement __is_unbounded_array built-in trait Ken Matsui
2024-01-06  5:05   ` [PATCH v3 8/8] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-02-15  5:07   ` PING: [PATCH v3 0/8] Optimize more type traits Ken Matsui
2024-02-19 13:51   ` [PATCH v7 01/22] c++: Implement __is_const built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 02/22] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 03/22] c++: Implement __is_volatile built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 04/22] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 05/22] c++: Implement __is_pointer built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 06/22] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 07/22] c++: Implement __is_unbounded_array built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 08/22] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 09/22] c++: Implement __add_pointer built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 10/22] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 11/22] c++: Implement __remove_extent built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 12/22] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 13/22] c++: Implement __remove_all_extents built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 14/22] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 15/22] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 16/22] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 17/22] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 18/22] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 19/22] c++: Implement __decay built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 20/22] libstdc++: Optimize std::decay compilation performance Ken Matsui
2024-02-19 13:51     ` [PATCH v7 21/22] c++: Implement __rank built-in trait Ken Matsui
2024-02-19 13:51     ` [PATCH v7 22/22] libstdc++: Optimize std::rank compilation performance Ken Matsui
2024-02-20  5:20     ` [PATCH v8 23/24] c++: Implement __is_invocable built-in trait Ken Matsui
2024-02-20 15:52       ` [PATCH v9 " Ken Matsui
2024-02-21  6:20         ` [PATCH v11 " Ken Matsui
2024-02-21  8:51           ` [PATCH v12 25/26] c++: Implement __is_nothrow_invocable " Ken Matsui
2024-02-21  8:51           ` [PATCH v12 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
2024-02-21  9:35             ` [PATCH v13 01/26] c++: Implement __is_const built-in trait Ken Matsui
2024-02-21  9:35               ` [PATCH v13 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-02-21  9:35               ` [PATCH v13 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
2024-02-21  9:35               ` [PATCH v13 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-02-21  9:35               ` [PATCH v13 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
2024-02-21  9:35               ` [PATCH v13 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-02-21  9:35               ` [PATCH v13 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
2024-02-21  9:35               ` [PATCH v13 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-02-21  9:35               ` [PATCH v13 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 19/26] c++: Implement __decay built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 21/26] c++: Implement __rank built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
2024-02-21  9:36               ` [PATCH v13 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
2024-02-21  9:36               ` [PATCH v13 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
2024-02-28 19:26               ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 01/26] c++: Implement __is_const built-in trait Ken Matsui
2024-04-30 20:54                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
2024-04-30 20:54                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
2024-04-30 20:56                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
2024-04-30 20:56                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
2024-04-30 20:58                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
2024-04-30 20:59                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
2024-04-30 21:00                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
2024-04-30 21:00                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
2024-04-30 21:02                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 19/26] c++: Implement __decay built-in trait Ken Matsui
2024-04-30 21:03                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 21/26] c++: Implement __rank built-in trait Ken Matsui
2024-04-30 21:08                   ` Jason Merrill
2024-05-02 15:00                     ` Ken Matsui
2024-05-02 15:15                     ` Patrick Palka
2024-05-02 15:34                       ` Ken Matsui
2024-05-02 16:20                         ` Ken Matsui
2024-05-02 16:45                           ` Jason Merrill
2024-05-02 16:56                             ` Ken Matsui
2024-05-02 17:12                             ` Trait built-in naming convention Jason Merrill
2024-05-02 17:24                               ` Ken Matsui
2024-05-02 17:37                                 ` Ville Voutilainen
2024-05-02 17:54                                   ` Marek Polacek
2024-05-02 19:30                                     ` Ken Matsui
2024-05-02 19:36                                       ` Iain Sandoe
2024-05-02 19:48                                         ` Jason Merrill
2024-05-02 19:52                                           ` Ken Matsui
2024-05-02 20:09                                             ` Jakub Jelinek
2024-05-02 20:15                                               ` Ken Matsui
2024-05-02 19:17                                   ` Jonathan Wakely
2024-05-02 19:25                                     ` Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
2024-03-08 17:17                   ` Patrick Palka
2024-03-08 17:38                     ` Ken Matsui
2024-03-15  1:53                     ` Ken Matsui
2024-03-15  6:34                       ` Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
2024-02-28 19:26                 ` [PATCH v14 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
2024-04-30 21:09                   ` Jason Merrill
2024-02-28 19:26                 ` [PATCH v14 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
2024-03-08 17:22                   ` Patrick Palka
2024-03-08 17:35                     ` Ken Matsui
2024-03-07 21:33                 ` [PATCH v14 00/26] Optimize more type traits Ken Matsui
2024-05-02 16:13                 ` [PATCH v16 " Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 01/26] c++: Implement __builtin_is_const trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 03/26] c++: Implement __builtin_is_volatile trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 05/26] c++: Implement __builtin_is_pointer trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 07/26] c++: Implement __builtin_is_unbounded_array trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 09/26] c++: Implement __builtin_add_pointer trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 11/26] c++: Implement __builtin_remove_extent trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 13/26] c++: Implement __builtin_remove_all_extents trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 15/26] c++: Implement __builtin_add_lvalue_reference trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 17/26] c++: Implement __builtin_add_rvalue_reference trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 19/26] c++: Implement __builtin_decay trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 21/26] c++: Implement __builtin_rank trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 23/26] c++: Implement __builtin_is_invocable trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 25/26] c++: Implement __builtin_is_nothrow_invocable trait Ken Matsui
2024-05-02 16:13                   ` [PATCH v16 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
2024-05-02 18:42                   ` [PATCH v17 00/26] Optimize more type traits Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 01/26] c++: Implement __is_const built-in trait Ken Matsui
2024-05-02 19:49                       ` Jason Merrill
2024-05-02 18:42                     ` [PATCH v17 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
2024-05-02 19:49                       ` Jason Merrill
2024-05-02 18:42                     ` [PATCH v17 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
2024-05-02 19:50                       ` Jason Merrill
2024-05-02 18:42                     ` [PATCH v17 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 07/26] c++: Implement __builtin_is_unbounded_array trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 19/26] c++: Implement __decay built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 21/26] c++: Implement __array_rank built-in trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 23/26] c++: Implement __builtin_is_invocable trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 25/26] c++: Implement __builtin_is_nothrow_invocable trait Ken Matsui
2024-05-02 18:42                     ` [PATCH v17 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
2024-05-02 20:12                     ` [PATCH v18 00/26] Optimize more type traits Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 01/26] c++: Implement __is_const built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 02/26] libstdc++: Optimize std::is_const compilation performance Ken Matsui
2024-05-07 13:55                         ` Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 03/26] c++: Implement __is_volatile built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 04/26] libstdc++: Optimize std::is_volatile compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 05/26] c++: Implement __is_pointer built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 06/26] libstdc++: Optimize std::is_pointer compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 07/26] c++: Implement __is_unbounded_array built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 08/26] libstdc++: Optimize std::is_unbounded_array compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 09/26] c++: Implement __add_pointer built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 10/26] libstdc++: Optimize std::add_pointer compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 11/26] c++: Implement __remove_extent built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 12/26] libstdc++: Optimize std::remove_extent compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 13/26] c++: Implement __remove_all_extents built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 14/26] libstdc++: Optimize std::remove_all_extents compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 15/26] c++: Implement __add_lvalue_reference built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 16/26] libstdc++: Optimize std::add_lvalue_reference compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 17/26] c++: Implement __add_rvalue_reference built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 18/26] libstdc++: Optimize std::add_rvalue_reference compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 19/26] c++: Implement __decay built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 20/26] libstdc++: Optimize std::decay compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 21/26] c++: Implement __array_rank built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 22/26] libstdc++: Optimize std::rank compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 23/26] c++: Implement __is_invocable built-in trait Ken Matsui
2024-05-02 20:38                         ` Jason Merrill
2024-05-02 20:45                           ` Marek Polacek
2024-05-02 20:50                             ` Ken Matsui
2024-05-02 20:47                           ` Ken Matsui
2024-05-03  2:43                             ` Jason Merrill
2024-05-03  2:46                               ` Ken Matsui
2024-05-02 20:54                           ` Ken Matsui
2024-05-03  2:09                         ` [PATCH v19 " Ken Matsui
2024-05-03  2:33                           ` Jason Merrill
2024-05-03  3:25                           ` [PATCH v20 " Ken Matsui
2024-05-03 19:38                             ` Jason Merrill
2024-05-03 20:28                               ` Ken Matsui
2024-05-03 20:52                             ` [PATCH v21 20/23] " Ken Matsui
2024-05-07 18:36                               ` Jason Merrill
2024-05-07 18:46                                 ` Ken Matsui
2024-05-08  5:04                               ` [PATCH v22 " Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 24/26] libstdc++: Optimize std::is_invocable compilation performance Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 25/26] c++: Implement __is_nothrow_invocable built-in trait Ken Matsui
2024-05-02 20:12                       ` [PATCH v18 26/26] libstdc++: Optimize std::is_nothrow_invocable compilation performance Ken Matsui
2024-02-20  5:20     ` [PATCH v8 24/24] libstdc++: Optimize std::is_invocable " Ken Matsui
2024-02-20 22:17       ` [PATCH v10 " Ken Matsui

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).