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. */