diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 416c60b7311..4bb3e9c4989 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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) \ diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index c7457fae7b0..29616100cfe 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -710,7 +710,7 @@ poplevel (int keep, int reverse, int functionbody) && ! 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) @@ -7573,7 +7573,7 @@ check_array_initializer (tree decl, tree type, tree init) 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), @@ -9438,6 +9438,12 @@ cp_finish_decomp (tree decl, cp_decomp *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); @@ -9537,6 +9543,30 @@ cp_finish_decomp (tree decl, cp_decomp *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) { diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index cdd2b8aada2..6d674684931 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -5893,7 +5893,7 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */) 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) diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index ea7ad0c1f29..dc5d046f04d 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -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)) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 26734beacce..31ae9c2fb54 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -2449,7 +2449,7 @@ static void cp_parser_statement_seq_opt static tree cp_parser_selection_statement (cp_parser *, bool *, vec *); 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* parser, bool *if_p, } /* 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_parser* parser, 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_parser* parser, 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 scope, tree init, bool ivdep, /* 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* parser, bool *if_p, bool ivdep, 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* 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_parser *parser, } } - 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_parser *parser, 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); } } diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 44de4c9a529..12d79bdbb3f 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -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 switch_stmt) { /* 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) { diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 55ee867d329..5519d7705e9 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -10661,7 +10661,7 @@ maybe_warn_about_returning_address_of_local (tree retval, location_t loc) || 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 diff --git a/gcc/testsuite/g++.dg/cpp1z/decomp16.C b/gcc/testsuite/g++.dg/cpp1z/decomp16.C index fe0be16e167..9781f87a253 100644 --- a/gcc/testsuite/g++.dg/cpp1z/decomp16.C +++ b/gcc/testsuite/g++.dg/cpp1z/decomp16.C @@ -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 '' 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 '' 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 '' 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 '' 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; } diff --git a/gcc/testsuite/g++.dg/cpp26/decomp10.C b/gcc/testsuite/g++.dg/cpp26/decomp10.C new file mode 100644 index 00000000000..84811c2e502 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp10.C @@ -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 '' from '\[^\n\r]*' to 'bool'" "" { target *-*-* } .-1 } +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp3.C b/gcc/testsuite/g++.dg/cpp26/decomp3.C new file mode 100644 index 00000000000..688e288883f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp3.C @@ -0,0 +1,168 @@ +// P0963R3 - Structured binding declaration as a condition +// { dg-do run { target c++11 } } +// { dg-options "" } + +namespace std { + template struct tuple_size; + template 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 &get () { if (d != 42) __builtin_abort (); return I ? a : b; } +}; +int T::d = 0; + +template<> struct std::tuple_size { static const int value = 2; }; +template struct std::tuple_element { 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 }); +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp4.C b/gcc/testsuite/g++.dg/cpp26/decomp4.C new file mode 100644 index 00000000000..e0e924cda44 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp4.C @@ -0,0 +1,74 @@ +// P0963R3 - Structured binding declaration as a condition +// { dg-do run { target c++11 } } +// { dg-options "" } + +namespace std { + template struct tuple_size; + template 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 &get () { if (d != 42) __builtin_abort (); return I ? a : b; } +}; +int T::d = 0; + +template<> struct std::tuple_size { static const int value = 2; }; +template struct std::tuple_element { 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 }); +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp5.C b/gcc/testsuite/g++.dg/cpp26/decomp5.C new file mode 100644 index 00000000000..abf7b96d06f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp5.C @@ -0,0 +1,171 @@ +// P0963R3 - Structured binding declaration as a condition +// { dg-do run { target c++11 } } +// { dg-options "" } + +namespace std { + template struct tuple_size; + template 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 &get () { if (d != 42) __builtin_abort (); return I ? a : b; } +}; +int T::d = 0; + +template<> struct std::tuple_size { static const int value = 2; }; +template struct std::tuple_element { using type = int; }; + +template +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 +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 +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 }); +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp6.C b/gcc/testsuite/g++.dg/cpp26/decomp6.C new file mode 100644 index 00000000000..520458130c4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp6.C @@ -0,0 +1,75 @@ +// P0963R3 - Structured binding declaration as a condition +// { dg-do run { target c++11 } } +// { dg-options "" } + +namespace std { + template struct tuple_size; + template 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 &get () { if (d != 42) __builtin_abort (); return I ? a : b; } +}; +int T::d = 0; + +template<> struct std::tuple_size { static const int value = 2; }; +template struct std::tuple_element { using type = int; }; + +template +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 }); +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp7.C b/gcc/testsuite/g++.dg/cpp26/decomp7.C new file mode 100644 index 00000000000..7c8ac69642d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp7.C @@ -0,0 +1,171 @@ +// P0963R3 - Structured binding declaration as a condition +// { dg-do run { target c++11 } } +// { dg-options "" } + +namespace std { + template struct tuple_size; + template 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 &get () { if (d != 42) __builtin_abort (); return I ? a : b; } +}; +int T::d = 0; + +template<> struct std::tuple_size { static const int value = 2; }; +template struct std::tuple_element { using type = int; }; + +template +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 +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 +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 }); +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp8.C b/gcc/testsuite/g++.dg/cpp26/decomp8.C new file mode 100644 index 00000000000..2c74bed1a3c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp8.C @@ -0,0 +1,75 @@ +// P0963R3 - Structured binding declaration as a condition +// { dg-do run { target c++11 } } +// { dg-options "" } + +namespace std { + template struct tuple_size; + template 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 &get () { if (d != 42) __builtin_abort (); return I ? a : b; } +}; +int T::d = 0; + +template<> struct std::tuple_size { static const int value = 2; }; +template struct std::tuple_element { using type = int; }; + +template +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 }); +} diff --git a/gcc/testsuite/g++.dg/cpp26/decomp9.C b/gcc/testsuite/g++.dg/cpp26/decomp9.C new file mode 100644 index 00000000000..5629c4c9d57 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/decomp9.C @@ -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 '' 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 '' 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 '' 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 '' 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 } +}