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 &&>);