Message ID | ZoRbAHtoCDNpv3hQ@tucnak |
---|---|
State | New |
Headers | show |
Series | c++: Implement C++26 P0963R3 - Structured binding declaration as a condition [PR115745] | expand |
On 7/2/24 3:54 PM, Jakub Jelinek wrote: > Hi! > > This C++26 paper allows structured bindings declaration in > if/while/for/switch conditions, where the structured binding shouldn't > be initialized by array (so in the standard only non-union class types; > as extension _Complex will also work and vectors will be diagnosed because > of conversion issues) and the decision variable is the artificial variable > (e in the standard) itself contextually converted to bool or converted to > some integer/enumeration type. > The standard requires that the conversion is evaluated before the get calls > in case of std::tuple* using class, so the largest part of the patch is making > sure this can be done during instantiation without duplicating too much > code. > In cp_parser_condition, creating a TARGET_EXPR to hold temporarily the > bool or int/enum result of the conversion across the get calls is easy, it > could be just added in between cp_finish_decl and cp_finish_decomp, but for > pt.cc there was no easy spot to add that. > In the end, the patch uses DECL_DECOMP_BASE for this. That tree is used > primarily for the user vars or var proxies to point back at the > DECL_ARTIFICIAL e variable, before this patch it has been NULL_TREE on > the base. In some places code was checking if DECL_DECOMP_BASE is NULL_TREE > to find out if it is the base or user var/var proxy. > The patch introduces DECL_DECOMP_IS_BASE macro for what used to be > !DECL_DECOMP_BASE and can stick something else in the base's > DECL_DECOMP_BASE as long as it is not a VAR_DECL. > The patch uses integer_zero_node to mark if/while/for condition structured > binding, integer_one_node to mark switch condition structured binding and > finally cp_finish_decomp sets it to TARGET_EXPR if some get method calls are > emitted and from there the callers can pick that up. This way I also > avoided code duplication between !processing_template_decl parsing and > pt.cc. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? OK. > 2024-07-02 Jakub Jelinek <jakub@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 >
--- 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 } +}