On 8/15/25 8:40 AM, Jakub Jelinek wrote:
Hi!

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.

Lightly tested so far, ok for trunk if it passes full bootstrap/regtest?

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.

--- gcc/doc/extend.texi.jj      2025-07-27 23:31:09.956003968 +0200
+++ gcc/doc/extend.texi 2025-08-15 17:30:10.251098390 +0200
@@ -30815,6 +30815,12 @@ A binary type trait: @code{true} wheneve
  @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
--- gcc/cp/cp-trait.def.jj      2025-07-11 19:01:25.844532378 +0200
+++ gcc/cp/cp-trait.def 2025-08-15 13:47:22.189927367 +0200
@@ -117,6 +117,7 @@ DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_c
  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)
--- gcc/cp/cp-tree.h.jj 2025-08-14 22:30:03.003336583 +0200
+++ gcc/cp/cp-tree.h    2025-08-15 15:29:03.477054136 +0200
@@ -8292,6 +8292,7 @@ extern void finish_static_assert
  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);
--- gcc/cp/semantics.cc.jj      2025-08-14 22:30:03.046336062 +0200
+++ gcc/cp/semantics.cc 2025-08-15 15:26:53.151660976 +0200
@@ -13710,10 +13710,11 @@ trait_expr_value (cp_trait_kind kind, tr
      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
              (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_tr
    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_tr
      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_tr
        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);
  }
--- gcc/cp/pt.cc.jj 2025-08-14 22:30:03.042336110 +0200
+++ gcc/cp/pt.cc        2025-08-15 15:28:24.412535782 +0200
@@ -22655,6 +22655,13 @@ tsubst_expr (tree t, tree args, tsubst_f
          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.  */

Hmm, I'm surprised finish_trait_expr isn't already 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));
        }
--- gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C.jj    2025-08-13 
22:10:18.897791624 +0200
+++ gcc/testsuite/g++.dg/cpp26/expansion-stmt15.C       2025-08-15 
14:12:40.587939623 +0200
@@ -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 }

I expect you're planning to implement
https://cplusplus.github.io/CWG/issues/3048.html ?

The patch is OK.

Jason

Reply via email to