diff mbox series

c++: Implement C++26 P0963R3 - Structured binding declaration as a condition [PR115745]

Message ID ZoRbAHtoCDNpv3hQ@tucnak
State New
Headers show
Series c++: Implement C++26 P0963R3 - Structured binding declaration as a condition [PR115745] | expand

Commit Message

Jakub Jelinek July 2, 2024, 7:54 p.m. UTC
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?

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.


	Jakub

Comments

Jason Merrill July 2, 2024, 8:05 p.m. UTC | #1
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
>
diff mbox series

Patch

--- 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 }
+}