2024-07-02 Jakub Jelinek <ja...@redhat.com>
PR c++/115745
gcc/cp/
* cp-tree.h: Implement C++26 P0963R3 - Structured binding declaration
as a condition.
(DECL_DECOMP_BASE): Adjust comment.
(DECL_DECOMP_IS_BASE): Define.
* parser.cc (cp_parser_selection_statement): Adjust
cp_parser_condition caller.
(cp_parser_condition): Add KEYWORD argument. Parse
C++26 structured bindings in conditions.
(cp_parser_c_for, cp_parser_iteration_statement): Adjust
cp_parser_condition callers.
(cp_parser_simple_declaration): Adjust
cp_parser_decomposition_declaration caller.
(cp_parser_decomposition_declaration): Add KEYWORD argument.
If it is not RID_MAX, diagnose for C++23 and older rather than C++14
and older. Set DECL_DECOMP_BASE to integer_zero_node for structured
bindings used in if/while/for conditions or integer_one_node for
those used in switch conditions.
* decl.cc (poplevel, check_array_initializer): Use DECL_DECOMP_IS_BASE
instead of !DECL_DECOMP_BASE.
(cp_finish_decomp): Diagnose array initializer for structured bindings
used in conditions. If using std::tuple_{size,element}, emit
conversion to bool or integer/enumeration of e into a TARGET_EXPR
before emitting get method calls.
* decl2.cc (mark_used): Use DECL_DECOMP_IS_BASE instead of
!DECL_DECOMP_BASE.
* module.cc (trees_in::tree_node): Likewise.
* typeck.cc (maybe_warn_about_returning_address_of_local): Likewise.
* semantics.cc (maybe_convert_cond): For structured bindings with
TARGET_EXPR DECL_DECOMP_BASE use that as condition.
(finish_switch_cond): Likewise.
gcc/testsuite/
* g++.dg/cpp1z/decomp16.C: Adjust expected diagnostics.
* g++.dg/cpp26/decomp3.C: New test.
* g++.dg/cpp26/decomp4.C: New test.
* g++.dg/cpp26/decomp5.C: New test.
* g++.dg/cpp26/decomp6.C: New test.
* g++.dg/cpp26/decomp7.C: New test.
* g++.dg/cpp26/decomp8.C: New test.
* g++.dg/cpp26/decomp9.C: New test.
* g++.dg/cpp26/decomp10.C: New test.
--- gcc/cp/cp-tree.h.jj 2024-06-14 19:45:07.909797753 +0200
+++ gcc/cp/cp-tree.h 2024-07-02 08:28:30.500979154 +0200
@@ -4472,10 +4472,18 @@ get_vec_init_expr (tree t)
? DECL_LANG_SPECIFIC (NODE)->u.base.selector == lds_decomp \
: false)
-/* The underlying artificial VAR_DECL for structured binding. */
+/* The underlying artificial VAR_DECL for structured binding. On the
+ artificial base VAR_DECL this can be NULL, or integer_{zero,one}_node
+ for structured binding used in if/while/for resp. switch conditions,
+ or a TARGET_EXPR with the condition value after cp_finish_decomp in
+ those cases. */
#define DECL_DECOMP_BASE(NODE) \
(LANG_DECL_DECOMP_CHECK (NODE)->base)
+/* True for the artificial VAR_DECL for structured binding. */
+#define DECL_DECOMP_IS_BASE(NODE) \
+ (!DECL_DECOMP_BASE (NODE) || !VAR_P (DECL_DECOMP_BASE (NODE)))
+
/* Nonzero if NODE is an inline VAR_DECL. In C++17, static data members
declared with constexpr specifier are implicitly inline variables. */
#define DECL_INLINE_VAR_P(NODE) \
--- gcc/cp/parser.cc.jj 2024-07-01 11:28:22.466241033 +0200
+++ gcc/cp/parser.cc 2024-07-02 09:02:10.635422397 +0200
@@ -2449,7 +2449,7 @@ static void cp_parser_statement_seq_opt
static tree cp_parser_selection_statement
(cp_parser *, bool *, vec<tree> *);
static tree cp_parser_condition
- (cp_parser *);
+ (cp_parser *, enum rid);
static tree cp_parser_iteration_statement
(cp_parser *, bool *, bool, tree, bool);
static bool cp_parser_init_statement
@@ -2562,7 +2562,7 @@ static void cp_parser_static_assert
static tree cp_parser_decltype
(cp_parser *);
static tree cp_parser_decomposition_declaration
- (cp_parser *, cp_decl_specifier_seq *, tree *, location_t *);
+ (cp_parser *, cp_decl_specifier_seq *, tree *, location_t *, enum rid);
/* Declarators [gram.dcl.decl] */
@@ -13690,7 +13690,7 @@ cp_parser_selection_statement (cp_parser
}
/* Parse the condition. */
- condition = cp_parser_condition (parser);
+ condition = cp_parser_condition (parser, keyword);
/* Look for the `)'. */
if (!parens.require_close (parser))
cp_parser_skip_to_closing_parenthesis (parser, true, false,
@@ -13909,6 +13909,7 @@ cp_parser_check_condition_declarator (cp
expression
type-specifier-seq declarator = initializer-clause
type-specifier-seq declarator braced-init-list
+ structured-binding-declaration initializer (C++26)
GNU Extension:
@@ -13919,7 +13920,7 @@ cp_parser_check_condition_declarator (cp
Returns the expression that should be tested. */
static tree
-cp_parser_condition (cp_parser* parser)
+cp_parser_condition (cp_parser* parser, enum rid keyword)
{
cp_decl_specifier_seq type_specifiers;
const char *saved_message;
@@ -13956,6 +13957,26 @@ cp_parser_condition (cp_parser* parser)
tree initializer = NULL_TREE;
location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+ /* Look for C++26 structured binding declaration. */
+ for (size_t n = 1; ; n++)
+ if (cp_lexer_nth_token_is (parser->lexer, n, CPP_AND)
+ || cp_lexer_nth_token_is (parser->lexer, n, CPP_AND_AND))
+ continue;
+ else if (cp_lexer_nth_token_is (parser->lexer, n, CPP_OPEN_SQUARE)
+ && !cp_lexer_nth_token_is (parser->lexer, n + 1,
+ CPP_OPEN_SQUARE)
+ && type_specifiers.any_specifiers_p
+ && cp_parser_parse_definitely (parser))
+ {
+ location_t init_loc;
+ tree decl
+ = cp_parser_decomposition_declaration (parser, &type_specifiers,
+ NULL, &init_loc, keyword);
+ return decl;
+ }
+ else
+ break;
+
/* Parse the declarator. */
declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
CP_PARSER_FLAGS_NONE,
@@ -14095,7 +14116,7 @@ cp_parser_c_for (cp_parser *parser, tree
/* If there's a condition, process it. */
if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
- condition = cp_parser_condition (parser);
+ condition = cp_parser_condition (parser, RID_FOR);
else if (ivdep)
{
cp_parser_error (parser, "missing loop condition in loop with "
@@ -14660,7 +14681,7 @@ cp_parser_iteration_statement (cp_parser
matching_parens parens;
parens.require_open (parser);
/* Parse the condition. */
- condition = cp_parser_condition (parser);
+ condition = cp_parser_condition (parser, RID_WHILE);
finish_while_stmt_cond (condition, statement, ivdep, unroll, novector);
/* Look for the `)'. */
parens.require_close (parser);
@@ -15966,7 +15987,7 @@ cp_parser_simple_declaration (cp_parser*
tree decl
= cp_parser_decomposition_declaration (parser, &decl_specifiers,
maybe_range_for_decl,
- &init_loc);
+ &init_loc, RID_MAX);
/* The next token should be either a `,' or a `;'. */
cp_token *token = cp_lexer_peek_token (parser->lexer);
@@ -16210,7 +16231,7 @@ static tree
cp_parser_decomposition_declaration (cp_parser *parser,
cp_decl_specifier_seq *decl_specifiers,
tree *maybe_range_for_decl,
- location_t *init_loc)
+ location_t *init_loc, enum rid keyword)
{
cp_ref_qualifier ref_qual = cp_parser_ref_qualifier_opt (parser);
location_t loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -16269,7 +16290,11 @@ cp_parser_decomposition_declaration (cp_
}
}
- if (cxx_dialect < cxx17)
+ if (keyword != RID_MAX && cxx_dialect < cxx26)
+ pedwarn (loc, OPT_Wc__26_extensions,
+ "structured bindings in conditions only available with "
+ "%<-std=c++2c%> or %<-std=gnu++2c%>");
+ else if (cxx_dialect < cxx17)
pedwarn (loc, OPT_Wc__17_extensions,
"structured bindings only available with "
"%<-std=c++17%> or %<-std=gnu++17%>");
@@ -16357,6 +16382,9 @@ cp_parser_decomposition_declaration (cp_
cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
(is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
&decomp);
+ if (keyword != RID_MAX)
+ DECL_DECOMP_BASE (decl)
+ = keyword == RID_SWITCH ? integer_one_node : integer_zero_node;
cp_finish_decomp (decl, &decomp);
}
}
--- gcc/cp/decl.cc.jj 2024-06-14 19:45:07.928797504 +0200
+++ gcc/cp/decl.cc 2024-07-02 09:07:29.764226608 +0200
@@ -697,7 +697,7 @@ poplevel (int keep, int reverse, int fun
&& ! DECL_IN_SYSTEM_HEADER (decl)
/* For structured bindings, consider only real variables, not
subobjects. */
- && (DECL_DECOMPOSITION_P (decl) ? !DECL_DECOMP_BASE (decl)
+ && (DECL_DECOMPOSITION_P (decl) ? DECL_DECOMP_IS_BASE (decl)
: (DECL_NAME (decl) && !DECL_ARTIFICIAL (decl)))
/* Don't warn about name-independent declarations. */
&& !name_independent_decl_p (decl)
@@ -7561,7 +7561,7 @@ check_array_initializer (tree decl, tree
to have complete type. */
if (decl
&& DECL_DECOMPOSITION_P (decl)
- && !DECL_DECOMP_BASE (decl)
+ && DECL_DECOMP_IS_BASE (decl)
&& !COMPLETE_TYPE_P (type))
{
error_at (DECL_SOURCE_LOCATION (decl),
@@ -9426,6 +9426,12 @@ cp_finish_decomp (tree decl, cp_decomp *
nelts = array_type_nelts_top (type);
if (nelts == error_mark_node)
goto error_out;
+ if (DECL_DECOMP_BASE (decl))
+ {
+ error_at (loc, "array initializer for structured binding "
+ "declaration in condition");
+ goto error_out;
+ }
if (!tree_fits_uhwi_p (nelts))
{
error_at (loc, "cannot decompose variable length array %qT", type);
@@ -9525,6 +9531,30 @@ cp_finish_decomp (tree decl, cp_decomp *
eltscnt = tree_to_uhwi (tsize);
if (count != eltscnt)
goto cnt_mismatch;
+ if (!processing_template_decl && DECL_DECOMP_BASE (decl))
+ {
+ /* For structured bindings used in conditions we need to evaluate
+ the conversion of decl (aka e in the standard) to bool or
+ integral/enumeral type (the latter for switch conditions)
+ before the get methods. */
+ tree cond = convert_from_reference (decl);
+ if (integer_onep (DECL_DECOMP_BASE (decl)))
+ /* switch condition. */
+ cond = build_expr_type_conversion (WANT_INT | WANT_ENUM,
+ cond, true);
+ else
+ /* if/while/for condition. */
+ cond = contextual_conv_bool (cond, tf_warning_or_error);
+ if (cond && !error_operand_p (cond))
+ {
+ /* Wrap that value into a TARGET_EXPR, emit it right
+ away and save for later uses in the cp_parse_condition
+ or its instantiation. */
+ cond = get_target_expr (cond);
+ add_stmt (cond);
+ DECL_DECOMP_BASE (decl) = cond;
+ }
+ }
int save_read = DECL_READ_P (decl);
for (unsigned i = 0; i < count; ++i)
{
--- gcc/cp/decl2.cc.jj 2024-07-01 11:28:22.446241290 +0200
+++ gcc/cp/decl2.cc 2024-07-02 08:29:23.000288981 +0200
@@ -5893,7 +5893,7 @@ mark_used (tree decl, tsubst_flags_t com
TREE_USED (decl) = true;
/* And for structured bindings also the underlying decl. */
- if (DECL_DECOMPOSITION_P (decl) && DECL_DECOMP_BASE (decl))
+ if (DECL_DECOMPOSITION_P (decl) && !DECL_DECOMP_IS_BASE (decl))
TREE_USED (DECL_DECOMP_BASE (decl)) = true;
if (TREE_CODE (decl) == TEMPLATE_DECL)
--- gcc/cp/module.cc.jj 2024-06-14 19:45:07.958797110 +0200
+++ gcc/cp/module.cc 2024-07-02 08:32:31.041816753 +0200
@@ -10142,7 +10142,7 @@ trees_in::tree_node (bool is_use)
TREE_USED (res) = true;
/* And for structured bindings also the underlying decl. */
- if (DECL_DECOMPOSITION_P (res) && DECL_DECOMP_BASE (res))
+ if (DECL_DECOMPOSITION_P (res) && !DECL_DECOMP_IS_BASE (res))
TREE_USED (DECL_DECOMP_BASE (res)) = true;
if (DECL_CLONED_FUNCTION_P (res))
--- gcc/cp/typeck.cc.jj 2024-07-01 11:28:22.500240597 +0200
+++ gcc/cp/typeck.cc 2024-07-02 08:33:12.541271107 +0200
@@ -10659,7 +10659,7 @@ maybe_warn_about_returning_address_of_lo
|| TREE_PUBLIC (whats_returned)))
{
if (DECL_DECOMPOSITION_P (whats_returned)
- && DECL_DECOMP_BASE (whats_returned)
+ && !DECL_DECOMP_IS_BASE (whats_returned)
&& DECL_HAS_VALUE_EXPR_P (whats_returned))
{
/* When returning address of a structured binding, if the structured
--- gcc/cp/semantics.cc.jj 2024-07-01 11:28:22.497240636 +0200
+++ gcc/cp/semantics.cc 2024-07-02 16:16:21.521924979 +0200
@@ -966,6 +966,15 @@ maybe_convert_cond (tree cond)
if (type_dependent_expression_p (cond))
return cond;
+ /* For structured binding used in condition, the conversion needs to be
+ evaluated before the individual variables are initialized in the
+ std::tuple_{size,elemenet} case. cp_finish_decomp saved the conversion
+ result in a TARGET_EXPR, pick it up from there. */
+ if (DECL_DECOMPOSITION_P (cond)
+ && DECL_DECOMP_IS_BASE (cond)
+ && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
+ cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
+
if (warn_sequence_point && !processing_template_decl)
verify_sequence_points (cond);
@@ -1699,6 +1708,14 @@ finish_switch_cond (tree cond, tree swit
{
/* Convert the condition to an integer or enumeration type. */
tree orig_cond = cond;
+ /* For structured binding used in condition, the conversion needs to be
+ evaluated before the individual variables are initialized in the
+ std::tuple_{size,elemenet} case. cp_finish_decomp saved the
+ conversion result in a TARGET_EXPR, pick it up from there. */
+ if (DECL_DECOMPOSITION_P (cond)
+ && DECL_DECOMP_IS_BASE (cond)
+ && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
+ cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, true);
if (cond == NULL_TREE)
{
--- gcc/testsuite/g++.dg/cpp1z/decomp16.C.jj 2024-07-02 18:04:35.184990721
+0200
+++ gcc/testsuite/g++.dg/cpp1z/decomp16.C 2024-07-02 18:04:39.750932526
+0200
@@ -7,23 +7,23 @@ void
foo ()
{
auto [ a, b ] = A ();
- for (; auto [ a, b ] = A (); ) // { dg-error
"expected" }
- ;
+ for (; auto [ a, b ] = A (); ) // { dg-error "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "could not convert
'<structured bindings>' from 'A' to 'bool'" "" { target *-*-* } .-1 }
for (; false; auto [ a, b ] = A ()) // { dg-error
"expected" }
;
- if (auto [ a, b ] = A ()) // { dg-error
"expected" }
- ;
- if (auto [ a, b ] = A (); auto [ c, d ] = A ()) // { dg-error
"expected" }
- ;
- if (int d = 5; auto [ a, b ] = A ()) // { dg-error
"expected" }
- ;
- switch (auto [ a, b ] = B ()) // { dg-error
"expected" }
- {
+ if (auto [ a, b ] = A ()) // { dg-error "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "could not convert
'<structured bindings>' from 'A' to 'bool'" "" { target *-*-* } .-1 }
+ if (auto [ a, b ] = A (); auto [ c, d ] = A ()) // { dg-error "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "could not convert
'<structured bindings>' from 'A' to 'bool'" "" { target *-*-* } .-1 }
+ if (int d = 5; auto [ a, b ] = A ()) // { dg-error "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "could not convert
'<structured bindings>' from 'A' to 'bool'" "" { target *-*-* } .-1 }
+ switch (auto [ a, b ] = B ()) // { dg-error "structured
bindings in conditions only available with" "" { target c++23_down } }
+ { // { dg-error "switch quantity not an
integer" "" { target *-*-* } .-1 }
case 2:
break;
}
- switch (int d = 5; auto [ a, b ] = B ()) // { dg-error
"expected" }
- {
+ switch (int d = 5; auto [ a, b ] = B ()) // { dg-error "structured bindings in
conditions only available with" "" { target c++23_down } }
+ { // { dg-error "switch quantity not an
integer" "" { target *-*-* } .-1 }
case 2:
break;
}
--- gcc/testsuite/g++.dg/cpp26/decomp3.C.jj 2024-07-01 17:11:02.086877632
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp3.C 2024-07-01 17:22:54.434805423
+0200
@@ -0,0 +1,168 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ int a, b;
+ explicit operator bool () const noexcept { return a == b; }
+};
+
+struct T {
+ int a, b, c;
+ static int d;
+ explicit operator bool () const noexcept { d = 42; return a == b; }
+ template <int I> int &get () { if (d != 42) __builtin_abort (); return I ? a
: b; }
+};
+int T::d = 0;
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = int; };
+
+void
+foo (T t)
+{
+ if (auto [ i, j ] = S { 1, 1 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ if (i != 1 || j != 1)
+ __builtin_abort ();
+ }
+ else
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ T::d = 0;
+ if (int m = 78; auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ if (i != 42 || j != 42 || T::d != 42 || m != 78)
+ __builtin_abort ();
+ }
+ else
+ {
+ ++i;
+ ++j;
+ ++m;
+ __builtin_abort ();
+ }
+ if (auto m = 15; auto [ i, j ] = S { -1, 1 }) // { dg-warning "structured bindings
in conditions only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ ++i;
+ ++j;
+ ++m;
+ __builtin_abort ();
+ }
+ else
+ {
+ if (i != -1 || j != 1 || m != 15)
+ __builtin_abort ();
+ }
+ t.a = -42;
+ T::d = 0;
+ if (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only available
with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ else
+ {
+ if (i != 42 || j != -42 || T::d != 42)
+ __builtin_abort ();
+ }
+}
+
+void
+bar (T t)
+{
+ int cnt = 0;
+ while (auto [ i, j ] = S { 7, 7 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ if (i != 7 || j != 7)
+ __builtin_abort ();
+ if (++cnt == 5)
+ break;
+ }
+ if (cnt != 5)
+ __builtin_abort ();
+ T::d = 0;
+ while (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ if (i != 31 || j != 31 || T::d != 42)
+ __builtin_abort ();
+ T::d = 0;
+ if (++cnt == 10)
+ break;
+ }
+ if (cnt != 10)
+ __builtin_abort ();
+ while (auto [ i, j ] = S { 7, -7 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ t.a = -31;
+ T::d = 0;
+ while (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+}
+
+void
+baz (T t)
+{
+ int cntc = 0;
+ for (int cnt = 0; auto [ i, j ] = S { 12, 12 }; ++cnt) // { dg-warning "structured
bindings in conditions only available with" "" { target c++23_down } }
+ {
+ if (i != 12 || j != 12)
+ __builtin_abort ();
+ cntc = cnt;
+ if (cnt == 5)
+ break;
+ }
+ if (cntc != 5)
+ __builtin_abort ();
+ T::d = 0;
+ for (int cnt = 5; auto & [ i, j ] = t; ++cnt) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ if (i != -15 || j != -15 || T::d != 42)
+ __builtin_abort ();
+ T::d = 0;
+ cntc = cnt;
+ if (cnt == 10)
+ break;
+ }
+ if (cntc != 10)
+ __builtin_abort ();
+ for (int cnt = 0; auto [ i, j ] = S { -27, 27 }; ++cnt) // { dg-warning "structured
bindings in conditions only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ t.a = 15;
+ T::d = 0;
+ for (int cnt = 0; auto & [ i, j ] = t; ++cnt) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo ({ 42, 42, 0 });
+ bar ({ 31, 31, 7 });
+ baz ({ -15, -15, 6 });
+}
--- gcc/testsuite/g++.dg/cpp26/decomp4.C.jj 2024-07-01 17:23:12.987569162
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp4.C 2024-07-01 18:59:19.010012299
+0200
@@ -0,0 +1,74 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ int a, b;
+ operator int () const noexcept { return a * 2; }
+};
+
+struct T {
+ int a, b, c;
+ static int d;
+ operator long long () const noexcept { d = 42; return a * 4; }
+ template <int I> int &get () { if (d != 42) __builtin_abort (); return I ? a
: b; }
+};
+int T::d = 0;
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = int; };
+
+void
+foo (T t)
+{
+ switch (auto [ i, j ] = S { 53, 62 }) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ case 2 * 53:
+ if (i != 53 || j != 62)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+ T::d = 0;
+ switch (int m = 78; auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ case 4 * 42LL:
+ if (i != 43 || j != 42 || T::d != 42 || m != 78)
+ __builtin_abort ();
+ break;
+ default:
+ break;
+ }
+ switch (auto m = 15; auto [ i, j ] = S { -1, 1 }) // { dg-warning "structured bindings
in conditions only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ case 2 * -1:
+ if (i != -1 || j != 1 || m != 15)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+ t.a = -42;
+ T::d = 0;
+ switch (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ case 4LL * -42:
+ if (i != 43 || j != -42 || T::d != 42)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo ({ 42, 43, 0 });
+}
--- gcc/testsuite/g++.dg/cpp26/decomp5.C.jj 2024-07-02 09:10:17.414020872
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp5.C 2024-07-02 09:10:52.370560957
+0200
@@ -0,0 +1,171 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ int a, b;
+ explicit operator bool () const noexcept { return a == b; }
+};
+
+struct T {
+ int a, b, c;
+ static int d;
+ explicit operator bool () const noexcept { d = 42; return a == b; }
+ template <int I> int &get () { if (d != 42) __builtin_abort (); return I ? a
: b; }
+};
+int T::d = 0;
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = int; };
+
+template <int N>
+void
+foo (T t)
+{
+ if (auto [ i, j ] = S { 1, 1 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ if (i != 1 || j != 1)
+ __builtin_abort ();
+ }
+ else
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ T::d = 0;
+ if (int m = 78; auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ if (i != 42 || j != 42 || T::d != 42 || m != 78)
+ __builtin_abort ();
+ }
+ else
+ {
+ ++i;
+ ++j;
+ ++m;
+ __builtin_abort ();
+ }
+ if (auto m = 15; auto [ i, j ] = S { -1, 1 }) // { dg-warning "structured bindings
in conditions only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ ++i;
+ ++j;
+ ++m;
+ __builtin_abort ();
+ }
+ else
+ {
+ if (i != -1 || j != 1 || m != 15)
+ __builtin_abort ();
+ }
+ t.a = -42;
+ T::d = 0;
+ if (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only available
with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ else
+ {
+ if (i != 42 || j != -42 || T::d != 42)
+ __builtin_abort ();
+ }
+}
+
+template <int N>
+void
+bar (T t)
+{
+ int cnt = 0;
+ while (auto [ i, j ] = S { 7, 7 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ if (i != 7 || j != 7)
+ __builtin_abort ();
+ if (++cnt == 5)
+ break;
+ }
+ if (cnt != 5)
+ __builtin_abort ();
+ T::d = 0;
+ while (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ if (i != 31 || j != 31 || T::d != 42)
+ __builtin_abort ();
+ T::d = 0;
+ if (++cnt == 10)
+ break;
+ }
+ if (cnt != 10)
+ __builtin_abort ();
+ while (auto [ i, j ] = S { 7, -7 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ t.a = -31;
+ T::d = 0;
+ while (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+}
+
+template <int N>
+void
+baz (T t)
+{
+ int cntc = 0;
+ for (int cnt = 0; auto [ i, j ] = S { 12, 12 }; ++cnt) // { dg-warning "structured
bindings in conditions only available with" "" { target c++23_down } }
+ {
+ if (i != 12 || j != 12)
+ __builtin_abort ();
+ cntc = cnt;
+ if (cnt == 5)
+ break;
+ }
+ if (cntc != 5)
+ __builtin_abort ();
+ T::d = 0;
+ for (int cnt = 5; auto & [ i, j ] = t; ++cnt) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ if (i != -15 || j != -15 || T::d != 42)
+ __builtin_abort ();
+ T::d = 0;
+ cntc = cnt;
+ if (cnt == 10)
+ break;
+ }
+ if (cntc != 10)
+ __builtin_abort ();
+ for (int cnt = 0; auto [ i, j ] = S { -27, 27 }; ++cnt) // { dg-warning "structured
bindings in conditions only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ t.a = 15;
+ T::d = 0;
+ for (int cnt = 0; auto & [ i, j ] = t; ++cnt) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo<0> ({ 42, 42, 0 });
+ bar<0> ({ 31, 31, 7 });
+ baz<0> ({ -15, -15, 6 });
+}
--- gcc/testsuite/g++.dg/cpp26/decomp6.C.jj 2024-07-02 09:10:22.141958669
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp6.C 2024-07-02 09:11:12.848291540
+0200
@@ -0,0 +1,75 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ int a, b;
+ operator int () const noexcept { return a * 2; }
+};
+
+struct T {
+ int a, b, c;
+ static int d;
+ operator long long () const noexcept { d = 42; return a * 4; }
+ template <int I> int &get () { if (d != 42) __builtin_abort (); return I ? a
: b; }
+};
+int T::d = 0;
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = int; };
+
+template <int N>
+void
+foo (T t)
+{
+ switch (auto [ i, j ] = S { 53, 62 }) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ case 2 * 53:
+ if (i != 53 || j != 62)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+ T::d = 0;
+ switch (int m = 78; auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ case 4 * 42LL:
+ if (i != 43 || j != 42 || T::d != 42 || m != 78)
+ __builtin_abort ();
+ break;
+ default:
+ break;
+ }
+ switch (auto m = 15; auto [ i, j ] = S { -1, 1 }) // { dg-warning "structured bindings
in conditions only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ case 2 * -1:
+ if (i != -1 || j != 1 || m != 15)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+ t.a = -42;
+ T::d = 0;
+ switch (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ case 4LL * -42:
+ if (i != 43 || j != -42 || T::d != 42)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo<0> ({ 42, 43, 0 });
+}
--- gcc/testsuite/g++.dg/cpp26/decomp7.C.jj 2024-07-02 09:11:27.140103504
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp7.C 2024-07-02 09:12:08.027565554
+0200
@@ -0,0 +1,171 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ int a, b;
+ explicit operator bool () const noexcept { return a == b; }
+};
+
+struct T {
+ int a, b, c;
+ static int d;
+ explicit operator bool () const noexcept { d = 42; return a == b; }
+ template <int I> int &get () { if (d != 42) __builtin_abort (); return I ? a
: b; }
+};
+int T::d = 0;
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = int; };
+
+template <typename S, typename T>
+void
+foo (T t)
+{
+ if (auto [ i, j ] = S { 1, 1 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ if (i != 1 || j != 1)
+ __builtin_abort ();
+ }
+ else
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ T::d = 0;
+ if (int m = 78; auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ if (i != 42 || j != 42 || T::d != 42 || m != 78)
+ __builtin_abort ();
+ }
+ else
+ {
+ ++i;
+ ++j;
+ ++m;
+ __builtin_abort ();
+ }
+ if (auto m = 15; auto [ i, j ] = S { -1, 1 }) // { dg-warning "structured bindings
in conditions only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ ++i;
+ ++j;
+ ++m;
+ __builtin_abort ();
+ }
+ else
+ {
+ if (i != -1 || j != 1 || m != 15)
+ __builtin_abort ();
+ }
+ t.a = -42;
+ T::d = 0;
+ if (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only available
with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ else
+ {
+ if (i != 42 || j != -42 || T::d != 42)
+ __builtin_abort ();
+ }
+}
+
+template <typename S, typename T>
+void
+bar (T t)
+{
+ int cnt = 0;
+ while (auto [ i, j ] = S { 7, 7 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ if (i != 7 || j != 7)
+ __builtin_abort ();
+ if (++cnt == 5)
+ break;
+ }
+ if (cnt != 5)
+ __builtin_abort ();
+ T::d = 0;
+ while (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ if (i != 31 || j != 31 || T::d != 42)
+ __builtin_abort ();
+ T::d = 0;
+ if (++cnt == 10)
+ break;
+ }
+ if (cnt != 10)
+ __builtin_abort ();
+ while (auto [ i, j ] = S { 7, -7 }) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ t.a = -31;
+ T::d = 0;
+ while (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+}
+
+template <typename S, typename T>
+void
+baz (T t)
+{
+ int cntc = 0;
+ for (int cnt = 0; auto [ i, j ] = S { 12, 12 }; ++cnt) // { dg-warning "structured
bindings in conditions only available with" "" { target c++23_down } }
+ {
+ if (i != 12 || j != 12)
+ __builtin_abort ();
+ cntc = cnt;
+ if (cnt == 5)
+ break;
+ }
+ if (cntc != 5)
+ __builtin_abort ();
+ T::d = 0;
+ for (int cnt = 5; auto & [ i, j ] = t; ++cnt) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ if (i != -15 || j != -15 || T::d != 42)
+ __builtin_abort ();
+ T::d = 0;
+ cntc = cnt;
+ if (cnt == 10)
+ break;
+ }
+ if (cntc != 10)
+ __builtin_abort ();
+ for (int cnt = 0; auto [ i, j ] = S { -27, 27 }; ++cnt) // { dg-warning "structured
bindings in conditions only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+ t.a = 15;
+ T::d = 0;
+ for (int cnt = 0; auto & [ i, j ] = t; ++cnt) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ ++i;
+ ++j;
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo<S, T> ({ 42, 42, 0 });
+ bar<S, T> ({ 31, 31, 7 });
+ baz<S, T> ({ -15, -15, 6 });
+}
--- gcc/testsuite/g++.dg/cpp26/decomp8.C.jj 2024-07-02 09:11:30.461059812
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp8.C 2024-07-02 09:12:22.298377789
+0200
@@ -0,0 +1,75 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ int a, b;
+ operator int () const noexcept { return a * 2; }
+};
+
+struct T {
+ int a, b, c;
+ static int d;
+ operator long long () const noexcept { d = 42; return a * 4; }
+ template <int I> int &get () { if (d != 42) __builtin_abort (); return I ? a
: b; }
+};
+int T::d = 0;
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = int; };
+
+template <typename S, typename T>
+void
+foo (T t)
+{
+ switch (auto [ i, j ] = S { 53, 62 }) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ case 2 * 53:
+ if (i != 53 || j != 62)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+ T::d = 0;
+ switch (int m = 78; auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ case 4 * 42LL:
+ if (i != 43 || j != 42 || T::d != 42 || m != 78)
+ __builtin_abort ();
+ break;
+ default:
+ break;
+ }
+ switch (auto m = 15; auto [ i, j ] = S { -1, 1 }) // { dg-warning "structured bindings
in conditions only available with" "" { target c++23_down } }
+ { // { dg-warning "init-statement in selection
statements only available with" "" { target c++14_down } .-1 }
+ case 2 * -1:
+ if (i != -1 || j != 1 || m != 15)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+ t.a = -42;
+ T::d = 0;
+ switch (auto & [ i, j ] = t) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ {
+ case 4LL * -42:
+ if (i != 43 || j != -42 || T::d != 42)
+ __builtin_abort ();
+ break;
+ default:
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo<S, T> ({ 42, 43, 0 });
+}
--- gcc/testsuite/g++.dg/cpp26/decomp9.C.jj 2024-07-02 09:14:45.503493673
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp9.C 2024-07-02 09:57:23.430826126
+0200
@@ -0,0 +1,68 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+int a[4];
+struct S { int i, j; };
+struct T { int i, j, k; explicit operator bool () const noexcept; } t;
+enum E { E0, E1 };
+struct U { int i, j, k, l; operator E () const noexcept; } u;
+int w;
+union X { int i; long j; } x;
+
+void
+foo (const S &&s)
+{
+ if (auto [ i, j, k, l ] = a) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "array initializer for
structured binding declaration in condition" "" { target *-*-* } .-1 }
+ if (auto & [a, b, c] = "ht") // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "array initializer for
structured binding declaration in condition" "" { target *-*-* } .-1 }
+ if (auto const & [i, j] = s) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "could not convert '<structured
bindings>' from 'const S' to 'bool'" "" { target *-*-* } .-1 }
+ if (auto const & [i, j, k] = t) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ w = i + j + k;
+ else
+ w = i - j * k;
+ if (auto [i, j, k, l] = u) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ;
+ if (auto [i] = x) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "cannot decompose union type
'X'" "" { target *-*-* } .-1 }
+ // { dg-error "could not convert '<structured
bindings>' from 'X' to 'bool'" "" { target *-*-* } .-2 }
+ if (auto [i, j, k] = s) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "3 names provided for structured
binding" "" { target *-*-* } .-1 }
+ // { dg-error "could not convert '<structured
bindings>' from 'S' to 'bool'" "" { target *-*-* } .-2 }
+ if (auto [i] = s) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "only 1 name provided for
structured binding" "" { target *-*-* } .-1 }
+ // { dg-error "could not convert '<structured
bindings>' from 'S' to 'bool'" "" { target *-*-* } .-2 }
+ switch (auto [a, b, c] = "ht") // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ { // { dg-error "array initializer for
structured binding declaration in condition" "" { target *-*-* } .-1 }
+ default: // { dg-error "switch quantity not an
integer" "" { target *-*-* } .-2 }
+ break;
+ }
+ switch (auto const & [i, j] = s) // { dg-warning "structured bindings in conditions
only available with" "" { target c++23_down } }
+ { // { dg-error "switch quantity not an
integer" "" { target *-*-* } .-1 }
+ case 1:
+ default:
+ break;
+ }
+ switch (auto const & [i, j, k] = t) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ { // { dg-error "switch quantity not an
integer" "" { target *-*-* } .-1 }
+ case 1:
+ default:
+ break;
+ }
+ switch (auto [i, j, k, l] = u) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ {
+ case E0:
+ ++i; ++j;
+ break;
+ default:
+ ++k; ++l;
+ break;
+ }
+ if (static auto [i, j, k] = t) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "'static' invalid in
condition" "" { target *-*-* } .-1 }
+ // { dg-warning "structured binding
declaration can be 'static' only in" "" { target c++17_down } .-2 }
+ if (constexpr auto [i, j, k] = t) // { dg-warning "structured bindings in
conditions only available with" "" { target c++23_down } }
+ ; // { dg-error "structured binding declaration
cannot be 'constexpr'" "" { target *-*-* } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp26/decomp10.C.jj 2024-07-02 10:01:45.298378163
+0200
+++ gcc/testsuite/g++.dg/cpp26/decomp10.C 2024-07-02 10:05:19.586556702
+0200
@@ -0,0 +1,15 @@
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+_Complex int c;
+int __attribute__((__vector_size__ (4 * sizeof (int)))) v;
+
+void
+foo ()
+{
+ if (auto [i,j] = c) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ ;
+ if (auto [i,j,k,l] = v) // { dg-warning "structured bindings in conditions only
available with" "" { target c++23_down } }
+ ; // { dg-error "could not convert '<structured bindings>' from
'\[^\n\r]*' to 'bool'" "" { target *-*-* } .-1 }
+}
Jakub