https://gcc.gnu.org/g:9a8b436b005b56e5ec3d8274284ae3afb4e7ba72

commit r16-3227-g9a8b436b005b56e5ec3d8274284ae3afb4e7ba72
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Fri Aug 15 22:38:41 2025 +0200

    c++: Implement __builtin_structured_binding_size trait
    
    clang++ apparently added a SFINAE-friendly __builtin_structured_binding_size
    trait to return the structured binding size (or error if not in SFINAE
    contexts if a type doesn't have a structured binding size).
    
    The expansion statement patch already anticipated this through adding
    complain argument to cp_finish_decomp.
    
    The following patch implements it.
    
    2025-08-15  Jakub Jelinek  <ja...@redhat.com>
    
    gcc/
            * doc/extend.texi (Type Traits): Document
            __builtin_structured_binding_size.
    gcc/cp/
            * cp-trait.def (STRUCTURED_BINDING_SIZE): New unary trait.
            * cp-tree.h (finish_structured_binding_size): Declare.
            * semantics.cc (trait_expr_value): Handle
            CPTK_STRUCTURED_BINDING_SIZE.
            (finish_structured_binding_size): New function.
            (finish_trait_expr): Handle CPTK_RANK and CPTK_TYPE_ORDER
            in the switch instead of just doing break; for those and
            ifs at the end to handle them.  Handle CPTK_STRUCTURED_BINDING_SIZE.
            * pt.cc (tsubst_expr): Likewise.
            * constraint.cc (diagnose_trait_expr): Likewise.
            * decl.cc (get_tuple_size): Use mce_true for maybe_const_value.
            (cp_decomp_size): Diagnose incomplete types not just if
            processing_template_decl, and use error_at instead of pedwarn.
            If btype is NULL, just return 0 instead of diagnosing an error.
    gcc/testsuite/
            * g++.dg/cpp26/expansion-stmt15.C: Expect different diagnostics
            for zero size destructuring expansion statement.
            * g++.dg/ext/builtin-structured-binding-size1.C: New test.
            * g++.dg/ext/builtin-structured-binding-size2.C: New test.
            * g++.dg/ext/builtin-structured-binding-size3.C: New test.
            * g++.dg/ext/builtin-structured-binding-size4.C: New test.

Diff:
---
 gcc/cp/constraint.cc                               |  3 ++
 gcc/cp/cp-trait.def                                |  1 +
 gcc/cp/cp-tree.h                                   |  1 +
 gcc/cp/decl.cc                                     | 15 ++----
 gcc/cp/pt.cc                                       |  7 +++
 gcc/cp/semantics.cc                                | 59 +++++++++++++++-------
 gcc/doc/extend.texi                                |  6 +++
 gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C      |  2 +-
 .../g++.dg/ext/builtin-structured-binding-size1.C  | 56 ++++++++++++++++++++
 .../g++.dg/ext/builtin-structured-binding-size2.C  | 51 +++++++++++++++++++
 .../g++.dg/ext/builtin-structured-binding-size3.C  | 51 +++++++++++++++++++
 .../g++.dg/ext/builtin-structured-binding-size4.C  | 32 ++++++++++++
 12 files changed, 255 insertions(+), 29 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index cbdfafc90c08..4b20b791e5d1 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3304,6 +3304,9 @@ diagnose_trait_expr (location_t loc, tree expr, tree args)
     case CPTK_TYPE_ORDER:
       inform (loc, "%qT and %qT cannot be ordered", t1, t2);
       break;
+    case CPTK_STRUCTURED_BINDING_SIZE:
+      inform (loc, "%qT is not destructurable", 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 9fedfd71a38f..5e4493a84a0f 100644
--- a/gcc/cp/cp-trait.def
+++ b/gcc/cp/cp-trait.def
@@ -117,6 +117,7 @@ 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_EXPR (STRUCTURED_BINDING_SIZE, "__builtin_structured_binding_size", 1)
 DEFTRAIT_EXPR (TYPE_ORDER, "__builtin_type_order", 2)
 DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1)
 DEFTRAIT_TYPE (UNDERLYING_TYPE, "__underlying_type", 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 126e123f2157..e7749fd7839a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8292,6 +8292,7 @@ extern void finish_static_assert                (tree, 
tree, location_t,
 extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
 extern tree fold_builtin_is_corresponding_member (location_t, int, tree *);
 extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, 
int, tree *);
+extern tree finish_structured_binding_size     (location_t, tree, 
tsubst_flags_t);
 extern tree finish_trait_expr                  (location_t, enum 
cp_trait_kind, tree, tree);
 extern tree finish_trait_type                  (enum cp_trait_kind, tree, 
tree, tsubst_flags_t);
 extern tree build_lambda_expr                   (void);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 728b61c62323..48547fba11c6 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -9762,7 +9762,7 @@ get_tuple_size (tree type)
   if (val == error_mark_node)
     return NULL_TREE;
   if (VAR_P (val) || TREE_CODE (val) == CONST_DECL)
-    val = maybe_constant_value (val);
+    val = maybe_constant_value (val, NULL_TREE, mce_true);
   if (TREE_CODE (val) == INTEGER_CST)
     return val;
   else
@@ -9997,11 +9997,11 @@ cp_decomp_size (location_t loc, tree type, 
tsubst_flags_t complain)
     }
   else if (processing_template_decl && complete_type (type) == error_mark_node)
     return -1;
-  else if (processing_template_decl && !COMPLETE_TYPE_P (type))
+  else if (!COMPLETE_TYPE_P (type))
     {
       if (complain & tf_error)
-       pedwarn (loc, 0, "structured binding refers to incomplete class type "
-                        "%qT", type);
+       error_at (loc, "structured binding refers to incomplete class type "
+                 "%qT", type);
       return -1;
     }
   else
@@ -10010,12 +10010,7 @@ cp_decomp_size (location_t loc, tree type, 
tsubst_flags_t complain)
       if (btype == error_mark_node)
        return -1;
       else if (btype == NULL_TREE)
-       {
-         if (complain & tf_error)
-           error_at (loc, "cannot decompose class type %qT without non-static "
-                          "data members", type);
-         return -1;
-       }
+       return 0;
       for (tree field = TYPE_FIELDS (btype); field; field = TREE_CHAIN (field))
        if (TREE_CODE (field) != FIELD_DECL
            || DECL_ARTIFICIAL (field)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 116bdac2bd56..bb2d0b48fd42 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -22655,6 +22655,13 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
          type1 = tsubst_expr (type1, args, complain, in_decl);
        tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args,
                             complain, in_decl);
+       if (TRAIT_EXPR_KIND (t) == CPTK_STRUCTURED_BINDING_SIZE
+           && type1 != error_mark_node
+           && !processing_template_decl)
+         /* __builtin_structured_binding_size handled separately
+            to make it SFINAE friendly.  */
+         RETURN (finish_structured_binding_size (TRAIT_EXPR_LOCATION (t),
+                                                 type1, complain));
        RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t),
                                   TRAIT_EXPR_KIND (t), type1, type2));
       }
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 5e73eac94144..26709c7a33bf 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -13710,10 +13710,11 @@ trait_expr_value (cp_trait_kind kind, tree type1, 
tree type2)
     case CPTK_IS_DEDUCIBLE:
       return type_targs_deducible_from (type1, type2);
 
-    /* __array_rank and __builtin_type_order are handled in
-       finish_trait_expr.  */
+    /* __array_rank, __builtin_type_order and __builtin_structured_binding_size
+       are handled in finish_trait_expr.  */
     case CPTK_RANK:
     case CPTK_TYPE_ORDER:
+    case CPTK_STRUCTURED_BINDING_SIZE:
       gcc_unreachable ();
 
 #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \
@@ -13818,6 +13819,27 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, 
tree type2)
              (non_reference (to), non_reference (from))));
 }
 
+/* Helper for finish_trait_expr and tsubst_expr.  Handle
+   CPTK_STRUCTURED_BINDING_SIZE in possibly SFINAE-friendly
+   way.  */
+
+tree
+finish_structured_binding_size (location_t loc, tree type,
+                               tsubst_flags_t complain)
+{
+  if (TYPE_REF_P (type))
+    {
+      if (complain & tf_error)
+       error_at (loc, "%qs argument %qT is a reference",
+                 "__builtin_structured_binding_size", type);
+      return error_mark_node;
+    }
+  HOST_WIDE_INT ret = cp_decomp_size (loc, type, complain);
+  if (ret == -1)
+    return error_mark_node;
+  return maybe_wrap_with_location (build_int_cst (size_type_node, ret), loc);
+}
+
 /* Process a trait expression.  */
 
 tree
@@ -13830,7 +13852,7 @@ 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);
-      if (kind == CPTK_RANK)
+      if (kind == CPTK_RANK || kind == CPTK_STRUCTURED_BINDING_SIZE)
        TREE_TYPE (trait_expr) = size_type_node;
       else if (kind == CPTK_TYPE_ORDER)
        {
@@ -13947,9 +13969,22 @@ 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:
+      break;
+
     case CPTK_RANK:
+      {
+       size_t rank = 0;
+       for (; TREE_CODE (type1) == ARRAY_TYPE; type1 = TREE_TYPE (type1))
+         ++rank;
+       return maybe_wrap_with_location (build_int_cst (size_type_node, rank),
+                                        loc);
+      }
+
     case CPTK_TYPE_ORDER:
-      break;
+      return maybe_wrap_with_location (type_order_value (type1, type2), loc);
+
+    case CPTK_STRUCTURED_BINDING_SIZE:
+      return finish_structured_binding_size (loc, type1, tf_warning_or_error);
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
       if (!array_of_unknown_bound_p (type1)
@@ -13980,20 +14015,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, 
tree type1, tree type2)
       gcc_unreachable ();
     }
 
-  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 if (kind == CPTK_TYPE_ORDER)
-    val = type_order_value (type1, type2);
-  else
-    val = (trait_expr_value (kind, type1, type2)
-          ? boolean_true_node : boolean_false_node);
-
+  tree val = (trait_expr_value (kind, type1, type2)
+             ? boolean_true_node : boolean_false_node);
   return maybe_wrap_with_location (val, loc);
 }
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index f64418479c7b..3b9b4286f529 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -30846,6 +30846,12 @@ A binary type trait: @code{true} whenever the 
@var{type1} and
 @var{type2} refer to the same type.
 @enddefbuiltin
 
+@defbuiltin{size_t __builtin_structured_binding_size (@var{type})}
+This trait returns the structured binding size ([dcl.struct.bind])
+of @var{type}.  If a type does not have a structured binding size,
+an error is diagnosed unless it is used in SFINAE contexts.
+@enddefbuiltin
+
 
 @node Deprecated Features
 @section Deprecated Features
diff --git a/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C 
b/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C
index 8f8ed752e078..0c69afafa49f 100644
--- a/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C
+++ b/gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C
@@ -27,7 +27,7 @@ foo (int n)
   int e = 42;
   d[0] = 42;
   template for (auto a : A {})         // { dg-warning "'template for' only 
available with" "" { target c++23_down } }
-    ;                                  // { dg-error "cannot decompose class 
type 'A' without non-static data members" "" { target *-*-* } .-1 }
+    ;                                  // { dg-error "empty structured 
binding" "" { target *-*-* } .-1 }
   template for (int b : B {})          // { dg-warning "'template for' only 
available with" "" { target c++23_down } }
     ;
   template for (int i : c)             // { dg-warning "'template for' only 
available with" "" { target c++23_down } }
diff --git a/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size1.C 
b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size1.C
new file mode 100644
index 000000000000..736911bab538
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size1.C
@@ -0,0 +1,56 @@
+// { dg-do compile { target c++11 } }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A { int a, b, c, d, e; };
+struct B {};
+struct C { int a, b; };
+struct D { int a, b, c; static int d; };
+struct E { int a : 1; int : 0; int : 2; int b : 1; int c : 3; int d : 4; };
+typedef float V [[gnu::vector_size (16 * sizeof (float))]];
+template <>
+struct std::tuple_size <C> { static constexpr int value = 42; };
+
+static_assert (__builtin_structured_binding_size (const A) == 5, "");
+static_assert (__is_same_as (decltype (__builtin_structured_binding_size (A)), 
decltype (sizeof (int))), "");
+static_assert (__builtin_structured_binding_size (B) == 0, "");
+static_assert (__builtin_structured_binding_size (C) == 42, "");
+static_assert (__builtin_structured_binding_size (A[17]) == 17, "");
+static_assert (__builtin_structured_binding_size (C[6]) == 6, "");
+static_assert (__builtin_structured_binding_size (volatile _Complex double) == 
2, "");
+static_assert (__builtin_structured_binding_size (V) == 16, "");
+static_assert (__builtin_structured_binding_size (float [[gnu::vector_size (8 
* sizeof (float))]]) == 8, "");
+static_assert (__builtin_structured_binding_size (D) == 3, "");
+static_assert (__builtin_structured_binding_size (E) == 4, "");
+
+struct F {
+  static short f[42];
+  static_assert (__builtin_structured_binding_size (decltype (f)) == 42, "");
+};
+
+template <typename A, typename B, typename C, typename D,
+         typename E, typename F, typename G, typename H,
+         typename I, typename J>
+void
+foo ()
+{
+  static_assert (__builtin_structured_binding_size (const A) == 5, "");
+  static_assert (__builtin_structured_binding_size (B) == 0, "");
+  static_assert (__builtin_structured_binding_size (C) == 42, "");
+  static_assert (__builtin_structured_binding_size (D) == 17, "");
+  static_assert (__builtin_structured_binding_size (E) == 6, "");
+  static_assert (__builtin_structured_binding_size (F) == 2, "");
+  static_assert (__builtin_structured_binding_size (volatile G) == 16, "");
+  static_assert (__builtin_structured_binding_size (H) == 8, "");
+  static_assert (__builtin_structured_binding_size (I) == 3, "");
+  static_assert (__builtin_structured_binding_size (J) == 4, "");
+}
+
+void
+bar ()
+{
+  foo <A, B, C, A[17], C[6], _Complex double, V, float [[gnu::vector_size (8 * 
sizeof (float))]], D, E> ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size2.C 
b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size2.C
new file mode 100644
index 000000000000..9abd80fec9d5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size2.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A;
+struct B { int a; };
+struct C { int a; };
+struct D { int a; };
+union E { int a; long b; };
+struct F : public B { int c; };
+struct G { struct { int d; }; int e; };
+struct H { union { int f; long g; }; int h; };
+struct I { private: int i; };
+struct J { int b; };
+struct K : public B, J {};
+template <>
+struct std::tuple_size <C> { static constexpr double value = 42; };
+template <>
+struct std::tuple_size <D> { static constexpr int value = -1; };
+int a = __builtin_structured_binding_size (A);                 // { dg-error 
"structured binding refers to incomplete class type 'A'" }
+int b = __builtin_structured_binding_size (A &);               // { dg-error 
"'__builtin_structured_binding_size' argument 'A\\\&' is a reference" }
+int c = __builtin_structured_binding_size (B[]);               // { dg-error 
"cannot decompose array of unknown bound 'B \\\[\\\]'" }
+int d = __builtin_structured_binding_size (C);                 // { dg-error 
"'std::tuple_size<C>::value' is not an integral constant expression" }
+int e = __builtin_structured_binding_size (D);                 // { dg-error 
"'std::tuple_size<D>::value' is not an integral constant expression" }
+int f = __builtin_structured_binding_size (E);                 // { dg-error 
"cannot decompose union type 'E'" }
+int g = __builtin_structured_binding_size (float);             // { dg-error 
"cannot decompose non-array non-class type 'float'" }
+int h = __builtin_structured_binding_size (void);              // { dg-error 
"cannot decompose non-array non-class type 'void'" }
+int i = __builtin_structured_binding_size (long &);            // { dg-error 
"'__builtin_structured_binding_size' argument 'long int\\\&' is a reference" }
+int j = __builtin_structured_binding_size (long *);            // { dg-error 
"cannot decompose non-array non-class type 'long int\\\*'" }
+auto k = []() {};
+int l = __builtin_structured_binding_size (decltype (k));      // { dg-error 
"cannot decompose lambda closure type '<lambda\\\(\\\)>'" }
+int m = __builtin_structured_binding_size (F);                 // { dg-error 
"cannot decompose class type 'F': both it and its base class 'B' have 
non-static data members" }
+int n = __builtin_structured_binding_size (G);                 // { dg-error 
"cannot decompose class type 'G' because it has an anonymous struct member" }
+int o = __builtin_structured_binding_size (H);                 // { dg-error 
"cannot decompose class type 'H' because it has an anonymous union member" }
+int p = __builtin_structured_binding_size (I);                 // { dg-error 
"cannot decompose inaccessible member 'I::i' of 'I'" }
+int q = __builtin_structured_binding_size (K);                 // { dg-error 
"cannot decompose class type 'K': its base classes 'B' and 'J' have non-static 
data members" }
+static_assert (__builtin_structured_binding_size (int[0]) == 0);
+void foo (int r[10], int s = __builtin_structured_binding_size (decltype 
(r))); // { dg-error "cannot decompose non-array non-class type 'int\\\*'" }
+
+template <typename T, int N = __builtin_structured_binding_size (T)> // { 
dg-error "cannot decompose non-array non-class type 'int'" }
+// { dg-error "'std::tuple_size<C>::value' is not an integral constant 
expression" "" { target *-*-* } .-1 }
+struct L {
+  static constexpr int value = N;
+};
+L<int> l1;                                                     // { dg-error 
"template argument 2 is invalid" }
+static_assert (L<B>::value == 1, "");
+L<C> l2;                                                       // { dg-error 
"template argument 2 is invalid" }
diff --git a/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size3.C 
b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size3.C
new file mode 100644
index 000000000000..19b66b4db4e7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size3.C
@@ -0,0 +1,51 @@
+// { dg-do compile { target c++11 } }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A { int a, b, c, d, e; };
+struct B {};
+struct C { int a, b; };
+typedef float V [[gnu::vector_size (16 * sizeof (float))]];
+template <>
+struct std::tuple_size <C> { static constexpr int value = 42; };
+
+int a = __builtin_structured_binding_size (const A &);         // { dg-error 
"'__builtin_structured_binding_size' argument 'const A\\\&' is a reference" }
+int b = __builtin_structured_binding_size (B &);               // { dg-error 
"'__builtin_structured_binding_size' argument 'B\\\&' is a reference" }
+int c = __builtin_structured_binding_size (C &);               // { dg-error 
"'__builtin_structured_binding_size' argument 'C\\\&' is a reference" }
+int d = __builtin_structured_binding_size (const A (&)[17]);   // { dg-error 
"'__builtin_structured_binding_size' argument 'const A \\\(\\\&\\\)\\\[17\\\]' 
is a reference" }
+int e = __builtin_structured_binding_size (C (&)[6]);          // { dg-error 
"'__builtin_structured_binding_size' argument 'C \\\(\\\&\\\)\\\[6\\\]' is a 
reference" }
+int f = __builtin_structured_binding_size (_Complex double &); // { dg-error 
"'__builtin_structured_binding_size' argument '__complex__ double\\\&' is a 
reference" }
+int g = __builtin_structured_binding_size (const V &);         // { dg-error 
"'__builtin_structured_binding_size' argument 'const V\\\&'\[^\n\r]* is a 
reference" }
+int h = __builtin_structured_binding_size (float [[gnu::vector_size (8 * 
sizeof (float))]] &); // { dg-error "'__builtin_structured_binding_size' 
argument '__vector\\\(8\\\) float\\\&' is a reference" }
+int i = __builtin_structured_binding_size (A &&);              // { dg-error 
"'__builtin_structured_binding_size' argument 'A\\\&\\\&' is a reference" }
+int j = __builtin_structured_binding_size (B &&);              // { dg-error 
"'__builtin_structured_binding_size' argument 'B\\\&\\\&' is a reference" }
+int k = __builtin_structured_binding_size (C &&);              // { dg-error 
"'__builtin_structured_binding_size' argument 'C\\\&\\\&' is a reference" }
+int l = __builtin_structured_binding_size (A (&&)[17]);                // { 
dg-error "'__builtin_structured_binding_size' argument 'A 
\\\(\\\&\\\&\\\)\\\[17\\\]' is a reference" }
+int m = __builtin_structured_binding_size (C (&&)[6]);         // { dg-error 
"'__builtin_structured_binding_size' argument 'C \\\(\\\&\\\&\\\)\\\[6\\\]' is 
a reference" }
+int n = __builtin_structured_binding_size (_Complex double &&);        // { 
dg-error "'__builtin_structured_binding_size' argument '__complex__ 
double\\\&\\\&' is a reference" }
+int o = __builtin_structured_binding_size (V &&);              // { dg-error 
"'__builtin_structured_binding_size' argument 'V\\\&\\\&'\[^\n\r]* is a 
reference" }
+
+template <typename A, typename B, typename C, typename D,
+         typename E, typename F, typename G, typename H>
+void
+foo ()
+{
+  int a = __builtin_structured_binding_size (A);               // { dg-error 
"'__builtin_structured_binding_size' argument 'A\\\&\\\&?' is a reference" }
+  int b = __builtin_structured_binding_size (B);               // { dg-error 
"'__builtin_structured_binding_size' argument '(const )?B\\\&\\\&?' is a 
reference" }
+  int c = __builtin_structured_binding_size (C);               // { dg-error 
"'__builtin_structured_binding_size' argument 'C\\\&\\\&?' is a reference" }
+  int d = __builtin_structured_binding_size (D);               // { dg-error 
"'__builtin_structured_binding_size' argument 'A \\\(\\\&\\\&?\\\)\\\[17\\\]' 
is a reference" }
+  int e = __builtin_structured_binding_size (E);               // { dg-error 
"'__builtin_structured_binding_size' argument 'C \\\(\\\&\\\&?\\\)\\\[6\\\]' is 
a reference" }
+  int f = __builtin_structured_binding_size (F);               // { dg-error 
"'__builtin_structured_binding_size' argument '(const )?__complex__ 
float\\\&\\\&?' is a reference" }
+  int g = __builtin_structured_binding_size (G);               // { dg-error 
"'__builtin_structured_binding_size' argument '__vector\\\(16\\\) 
float\\\&\\\&?' is a reference" }
+  int h = __builtin_structured_binding_size (H);               // { dg-error 
"'__builtin_structured_binding_size' argument '__vector\\\(8\\\) 
float\\\&\\\&?' is a reference" }
+}
+
+void
+bar ()
+{
+  foo <A &, const B &, C &, A (&)[17], C (&)[6], const _Complex float &, V &, 
float [[gnu::vector_size (8 * sizeof (float))]] &> ();
+  foo <A &&, B &&, C &&, A (&&)[17], C (&&)[6], _Complex float &&, V &&, float 
[[gnu::vector_size (8 * sizeof (float))]] &> ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size4.C 
b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size4.C
new file mode 100644
index 000000000000..18a87e920b99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-structured-binding-size4.C
@@ -0,0 +1,32 @@
+// { dg-do compile { target c++20 } }
+
+namespace std {
+  template <typename T> struct tuple_size;
+  template <int, typename> struct tuple_element;
+}
+
+struct A { int a, b, c, d, e; };
+struct B {};
+struct C { int a, b; };
+struct D { int a; };
+typedef float V [[gnu::vector_size (16 * sizeof (float))]];
+template <>
+struct std::tuple_size <C> { static constexpr int value = 42; };
+template <>
+struct std::tuple_size <D> { static constexpr int value = 0; };
+
+template <typename T>
+concept is_destructurable = requires { { __builtin_structured_binding_size (T) 
}; };
+
+static_assert (is_destructurable <A>);
+static_assert (is_destructurable <const B>);
+static_assert (is_destructurable <C>);
+static_assert (!is_destructurable <A &>);
+static_assert (!is_destructurable <int[]>);
+static_assert (is_destructurable <int[1]>);
+static_assert (is_destructurable <A[42]>);
+static_assert (is_destructurable <float[10]>);
+static_assert (!is_destructurable <int *>);
+static_assert (is_destructurable <D volatile>);
+static_assert (is_destructurable <const D>);
+static_assert (!is_destructurable <C &&>);

Reply via email to