diff mbox

C++ PATCH for C++17 selection statements with initializer

Message ID 20161005131425.GQ3223@redhat.com
State New
Headers show

Commit Message

Marek Polacek Oct. 5, 2016, 1:14 p.m. UTC
This is my attempt to implement P0305R1, Selection statements with initializer.
It allows the users to write

  if (init; cond) // ...

which is equivalent to

  {
    init
    if (cond) // ...
  }

Similarly for if-else, if constexpr, and switch.

The approach I had taken was to tentatively parse "init" and if no semicolon
follows, abort the parse and just parse the condition.  But this didn't work.
An init-statement is either a simple-declaration or an expression-statement,
and parsing either has irreversible side-effects--e.g. adding a new statement
or a new declaration, or duplicating side-effects.  Using firewalls here
doesn't help.  Since an init-statement ends with a semicolon, I decided to
write cp_parser_init_statement_p, which checks if there's a semicolon in the if
condition that is not nested in () or in {} (think of ({}) or lambda
expressions).  It seemed a bit weird but seems to work well.  And then it's
just a simple matter of calling cp_parser_init_statement that parses either a
simple declaration or an expression statement and does the right thing wrt
scopes.

Since "for-init-statement" is no longer in the standard, I renamed all the
occurrences to "init-statement".

There's also this -ffor-scope thing, but I didn't touch it and it's probably
not necessary to.

Bootstrapped/regtested on x86_64-linux and ppc64-linux, ok for trunk? 

2016-10-05  Marek Polacek  <polacek@redhat.com>

	Implement P0305R1, Selection statements with initializer.
	* cp-array-notation.c (create_an_loop): Call finish_init_stmt
	instead of finish_for_init_stmt.
	* cp-tree.h (finish_for_init_stmt): Rename to finish_init_stmt.
	* decl.c (poplevel): Adjust a comment.
	* init.c (build_vec_init): Call finish_init_stmt instead of
	finish_for_init_stmt.
	* name-lookup.c (pushdecl_maybe_friend_1): Adjust a comment.
	* name-lookup.h (enum scope_kind): Likewise.
	* parser.c (cp_parser_statement): Update commentary.
	(cp_parser_init_statement_p): New function.
	(cp_parser_selection_statement): Parse the optional init-statement.
	(cp_parser_for): Call finish_init_stmt instead of finish_for_init_stmt.
	(cp_parser_c_for): Likewise.
	(cp_convert_range_for): Call finish_init_stmt instead of finish_for_init_stmt.
	(cp_parser_range_for_member_function): Update commentary.
	(cp_parser_iteration_statement):
	(cp_parser_for_init_statement): Rename to cp_parser_init_statement.
	* pt.c (tsubst_omp_for_iterator): Update commentary.
	(tsubst_expr): Call finish_init_stmt instead of finish_for_init_stmt.
	* semantics.c (finish_for_init_stmt): Rename to finish_init_stmt.
	Update commentary.

	* g++.dg/cpp1z/init-statement1.C: New test.
	* g++.dg/cpp1z/init-statement2.C: New test.
	* g++.dg/cpp1z/init-statement3.C: New test.
	* g++.dg/cpp1z/init-statement4.C: New test.
	* g++.dg/cpp1z/init-statement5.C: New test.
	* g++.dg/cpp1z/init-statement6.C: New test.
	* g++.dg/cpp1z/init-statement7.C: New test.


	Marek

Comments

Jakub Jelinek Oct. 5, 2016, 1:31 p.m. UTC | #1
On Wed, Oct 05, 2016 at 03:14:25PM +0200, Marek Polacek wrote:
> This is my attempt to implement P0305R1, Selection statements with initializer.
> It allows the users to write
> 
>   if (init; cond) // ...
> 
> which is equivalent to
> 
>   {
>     init
>     if (cond) // ...
>   }

Well, it isn't exactly equivalent, because unlike { init; if (cond) /* ... */; }
there aren't two scopes, but just one.  So I'd say you should have tests
that verify that init and cond are indeed in the same scope, e.g. by trying
something like if (int c = 5; int c = 5) ... and verifying dg-error is
reported.

> +	case CPP_CLOSE_PAREN:
> +	  /* If the next token is a non-nested '(', then we have reached
> +	     the end of the if condition.  */

Looks like typo, shouldn't that be ')' ?

Also, do you really need two counters?

> +	  if (paren_depth-- == 0)
> +	    return false;
> +	  break;
> +
> +	case CPP_OPEN_PAREN:
> +	  ++paren_depth;
> +	  break;
> +
> +	case CPP_CLOSE_BRACE:
> +	  --brace_depth;

I mean, shouldn't you also stop before } when seeing if (int a = }; b)
rather than wrapping around?

> +      /* Consume the token.  */
> +      cp_lexer_consume_token (parser->lexer);
> +    }

Also, do you really need to consume all the tokens and then rollback, rather
than just use peek_nth_token with the index increasing in each iteration?

	Jakub
Jason Merrill Oct. 5, 2016, 2:48 p.m. UTC | #2
On Wed, Oct 5, 2016 at 9:14 AM, Marek Polacek <polacek@redhat.com> wrote:
> +/* Return true if we're looking at (init; cond), false otherwise.  */
> +
> +static bool
> +cp_parser_init_statement_p (cp_parser *parser)
> +{
> +  unsigned paren_depth = 0;
> +  unsigned brace_depth = 0;

Do we really need another one of these token scanning functions?
Can't you write this in terms of
cp_parser_skip_to_closing_parenthesis?

> +       /* Parse the optional init-statement.  */
> +       tree decl;
> +       cp_lexer_save_tokens (parser->lexer);
> +       const bool init_stmt_p = cp_parser_init_statement_p (parser);
> +       /* Roll back the tokens we skipped.  */
> +       cp_lexer_rollback_tokens (parser->lexer);

The save/rollback should be in the the predicate function, not the caller.

Jason
Marek Polacek Oct. 5, 2016, 3:29 p.m. UTC | #3
On Wed, Oct 05, 2016 at 10:48:19AM -0400, Jason Merrill wrote:
> On Wed, Oct 5, 2016 at 9:14 AM, Marek Polacek <polacek@redhat.com> wrote:
> > +/* Return true if we're looking at (init; cond), false otherwise.  */
> > +
> > +static bool
> > +cp_parser_init_statement_p (cp_parser *parser)
> > +{
> > +  unsigned paren_depth = 0;
> > +  unsigned brace_depth = 0;
> 
> Do we really need another one of these token scanning functions?
> Can't you write this in terms of
> cp_parser_skip_to_closing_parenthesis?
> 
> > +       /* Parse the optional init-statement.  */
> > +       tree decl;
> > +       cp_lexer_save_tokens (parser->lexer);
> > +       const bool init_stmt_p = cp_parser_init_statement_p (parser);
> > +       /* Roll back the tokens we skipped.  */
> > +       cp_lexer_rollback_tokens (parser->lexer);
> 
> The save/rollback should be in the the predicate function, not the caller.

How about the version I just posted, i.e.
<https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00252.html>?

	Marek
Jason Merrill Oct. 5, 2016, 4:11 p.m. UTC | #4
On Wed, Oct 5, 2016 at 11:29 AM, Marek Polacek <polacek@redhat.com> wrote:
> On Wed, Oct 05, 2016 at 10:48:19AM -0400, Jason Merrill wrote:
>> On Wed, Oct 5, 2016 at 9:14 AM, Marek Polacek <polacek@redhat.com> wrote:
>> > +/* Return true if we're looking at (init; cond), false otherwise.  */
>> > +
>> > +static bool
>> > +cp_parser_init_statement_p (cp_parser *parser)
>> > +{
>> > +  unsigned paren_depth = 0;
>> > +  unsigned brace_depth = 0;
>>
>> Do we really need another one of these token scanning functions?
>> Can't you write this in terms of
>> cp_parser_skip_to_closing_parenthesis?
>>
>> > +       /* Parse the optional init-statement.  */
>> > +       tree decl;
>> > +       cp_lexer_save_tokens (parser->lexer);
>> > +       const bool init_stmt_p = cp_parser_init_statement_p (parser);
>> > +       /* Roll back the tokens we skipped.  */
>> > +       cp_lexer_rollback_tokens (parser->lexer);
>>
>> The save/rollback should be in the the predicate function, not the caller.
>
> How about the version I just posted, i.e.
> <https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00252.html>?

That doesn't address my first comment.

Jason
diff mbox

Patch

diff --git gcc/cp/cp-array-notation.c gcc/cp/cp-array-notation.c
index 4687ced..633ab09 100644
--- gcc/cp/cp-array-notation.c
+++ gcc/cp/cp-array-notation.c
@@ -66,7 +66,7 @@  create_an_loop (tree init, tree cond, tree incr, tree body)
 
   finish_expr_stmt (init);
   for_stmt = begin_for_stmt (NULL_TREE, NULL_TREE);
-  finish_for_init_stmt (for_stmt);
+  finish_init_stmt (for_stmt);
   finish_for_cond (cond, for_stmt, false);
   finish_for_expr (incr, for_stmt);
   finish_expr_stmt (body);
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 3fbe1d9..92e4017 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6297,7 +6297,7 @@  extern void finish_do_stmt			(tree, tree, bool);
 extern tree finish_return_stmt			(tree);
 extern tree begin_for_scope			(tree *);
 extern tree begin_for_stmt			(tree, tree);
-extern void finish_for_init_stmt		(tree);
+extern void finish_init_stmt			(tree);
 extern void finish_for_cond			(tree, tree, bool);
 extern void finish_for_expr			(tree, tree);
 extern void finish_for_stmt			(tree);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 6646062..6a08d8f 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -639,9 +639,8 @@  poplevel (int keep, int reverse, int functionbody)
       BLOCK_SUPERCONTEXT (link) = block;
 
   /* We still support the old for-scope rules, whereby the variables
-     in a for-init statement were in scope after the for-statement
-     ended.  We only use the new rules if flag_new_for_scope is
-     nonzero.  */
+     in a init statement were in scope after the for-statement ended.
+     We only use the new rules if flag_new_for_scope is nonzero.  */
   leaving_for_scope
     = current_binding_level->kind == sk_for && flag_new_for_scope == 1;
 
diff --git gcc/cp/init.c gcc/cp/init.c
index 2d5877d..d1c8274 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -4052,7 +4052,7 @@  build_vec_init (tree base, tree maxindex, tree init,
       tree to;
 
       for_stmt = begin_for_stmt (NULL_TREE, NULL_TREE);
-      finish_for_init_stmt (for_stmt);
+      finish_init_stmt (for_stmt);
       finish_for_cond (build2 (GT_EXPR, boolean_type_node, iterator,
 			       build_int_cst (TREE_TYPE (iterator), -1)),
 		       for_stmt, false);
diff --git gcc/cp/name-lookup.c gcc/cp/name-lookup.c
index 1bce63b..9e84a1b 100644
--- gcc/cp/name-lookup.c
+++ gcc/cp/name-lookup.c
@@ -1156,7 +1156,7 @@  pushdecl_maybe_friend_1 (tree x, bool is_friend)
 		   }
 		}
 	      /* Error if redeclaring a local declared in a
-		 for-init-statement or in the condition of an if or
+		 init-statement or in the condition of an if or
 		 switch statement when the new declaration is in the
 		 outermost block of the controlled statement.
 		 Redeclaring a variable from a for or while condition is
diff --git gcc/cp/name-lookup.h gcc/cp/name-lookup.h
index 2cb129c..fd71038 100644
--- gcc/cp/name-lookup.h
+++ gcc/cp/name-lookup.h
@@ -107,7 +107,7 @@  enum scope_kind {
   sk_try,	     /* A try-block.  */
   sk_catch,	     /* A catch-block.  */
   sk_for,	     /* The scope of the variable declared in a
-			for-init-statement.  */
+			init-statement.  */
   sk_cond,	     /* The scope of the variable declared in the condition
 			of an if or switch statement.  */
   sk_function_parms, /* The scope containing function parameters.  */
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 683a6dd..03c007e 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -2117,7 +2117,7 @@  static tree cp_parser_condition
   (cp_parser *);
 static tree cp_parser_iteration_statement
   (cp_parser *, bool *, bool);
-static bool cp_parser_for_init_statement
+static bool cp_parser_init_statement
   (cp_parser *, tree *decl);
 static tree cp_parser_for
   (cp_parser *, bool);
@@ -2642,6 +2642,8 @@  static bool cp_parser_compound_literal_p
   (cp_parser *);
 static bool cp_parser_array_designator_p
   (cp_parser *);
+static bool cp_parser_init_statement_p
+  (cp_parser *);
 static bool cp_parser_skip_to_closing_square_bracket
   (cp_parser *);
 
@@ -10396,6 +10398,10 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
     declaration-statement
     attribute-specifier-seq (opt) try-block
 
+  init-statement:
+    expression-statement
+    simple-declaration
+
   TM Extension:
 
    statement:
@@ -10936,12 +10942,65 @@  cp_parser_statement_seq_opt (cp_parser* parser, tree in_statement_expr)
     }
 }
 
+/* Return true if we're looking at (init; cond), false otherwise.  */
+
+static bool
+cp_parser_init_statement_p (cp_parser *parser)
+{
+  unsigned paren_depth = 0;
+  unsigned brace_depth = 0;
+
+  /* Look for ';' that is not nested in () or {}.  */
+  while (true)
+    {
+      cp_token *token = cp_lexer_peek_token (parser->lexer);
+
+      switch (token->type)
+	{
+	case CPP_EOF:
+	case CPP_PRAGMA_EOL:
+	  /* If we've run out of tokens, stop.  */
+	  return false;
+
+	case CPP_SEMICOLON:
+	  if (paren_depth == 0 && brace_depth == 0)
+	    return true;
+	  break;
+
+	case CPP_CLOSE_PAREN:
+	  /* If the next token is a non-nested '(', then we have reached
+	     the end of the if condition.  */
+	  if (paren_depth-- == 0)
+	    return false;
+	  break;
+
+	case CPP_OPEN_PAREN:
+	  ++paren_depth;
+	  break;
+
+	case CPP_CLOSE_BRACE:
+	  --brace_depth;
+	  break;
+
+	case CPP_OPEN_BRACE:
+	  ++brace_depth;
+	  break;
+
+	default:
+	  break;
+	}
+
+      /* Consume the token.  */
+      cp_lexer_consume_token (parser->lexer);
+    }
+}
+
 /* Parse a selection-statement.
 
    selection-statement:
-     if ( condition ) statement
-     if ( condition ) statement else statement
-     switch ( condition ) statement
+     if ( init-statement [opt] condition ) statement
+     if ( init-statement [opt] condition ) statement else statement
+     switch ( init-statement [opt] condition ) statement
 
    Returns the new IF_STMT or SWITCH_STMT.
 
@@ -11006,6 +11065,21 @@  cp_parser_selection_statement (cp_parser* parser, bool *if_p,
 	else
 	  statement = begin_switch_stmt ();
 
+	/* Parse the optional init-statement.  */
+	tree decl;
+	cp_lexer_save_tokens (parser->lexer);
+	const bool init_stmt_p = cp_parser_init_statement_p (parser);
+	/* Roll back the tokens we skipped.  */
+	cp_lexer_rollback_tokens (parser->lexer);
+	if (init_stmt_p)
+	  {
+	    if (cxx_dialect < cxx1z)
+	      pedwarn (cp_lexer_peek_token (parser->lexer)->location, 0,
+		       "init-statement in selection statements only available "
+		       "with -std=c++1z or -std=gnu++1z");
+	    cp_parser_init_statement (parser, &decl);
+	  }
+
 	/* Parse the condition.  */
 	condition = cp_parser_condition (parser);
 	/* Look for the `)'.  */
@@ -11306,7 +11380,7 @@  cp_parser_for (cp_parser *parser, bool ivdep)
   scope = begin_for_scope (&init);
 
   /* Parse the initialization.  */
-  is_range_for = cp_parser_for_init_statement (parser, &decl);
+  is_range_for = cp_parser_init_statement (parser, &decl);
 
   if (is_range_for)
     return cp_parser_range_for (parser, scope, init, decl, ivdep);
@@ -11323,9 +11397,9 @@  cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep)
   tree stmt;
 
   stmt = begin_for_stmt (scope, init);
-  /* The for-init-statement has already been parsed in
-     cp_parser_for_init_statement, so no work is needed here.  */
-  finish_for_init_stmt (stmt);
+  /* The init-statement has already been parsed in
+     cp_parser_init_statement, so no work is needed here.  */
+  finish_init_stmt (stmt);
 
   /* If there's a condition, process it.  */
   if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
@@ -11354,7 +11428,7 @@  cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep)
     decl-specifier-seq declarator : expression
 
   The decl-specifier-seq declarator and the `:' are already parsed by
-  cp_parser_for_init_statement. If processing_template_decl it returns a
+  cp_parser_init_statement.  If processing_template_decl it returns a
   newly created RANGE_FOR_STMT; if not, it is converted to a
   regular FOR_STMT.  */
 
@@ -11552,7 +11626,7 @@  cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
 		  /*is_constant_init*/false, NULL_TREE,
 		  LOOKUP_ONLYCONVERTING);
 
-  finish_for_init_stmt (statement);
+  finish_init_stmt (statement);
 
   /* The new for condition.  */
   condition = build_x_binary_op (input_location, NE_EXPR,
@@ -11726,7 +11800,7 @@  cp_parser_range_for_member_function (tree range, tree identifier)
    iteration-statement:
      while ( condition ) statement
      do statement while ( expression ) ;
-     for ( for-init-statement condition [opt] ; expression [opt] )
+     for ( init-statement condition [opt] ; expression [opt] )
        statement
 
    Returns the new WHILE_STMT, DO_STMT, FOR_STMT or RANGE_FOR_STMT.  */
@@ -11832,15 +11906,15 @@  cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep)
   return statement;
 }
 
-/* Parse a for-init-statement or the declarator of a range-based-for.
+/* Parse a init-statement or the declarator of a range-based-for.
    Returns true if a range-based-for declaration is seen.
 
-   for-init-statement:
+   init-statement:
      expression-statement
      simple-declaration  */
 
 static bool
-cp_parser_for_init_statement (cp_parser* parser, tree *decl)
+cp_parser_init_statement (cp_parser* parser, tree *decl)
 {
   /* If the next token is a `;', then we have an empty
      expression-statement.  Grammatically, this is also a
diff --git gcc/cp/pt.c gcc/cp/pt.c
index e6b1368..e6bacdf 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -14974,7 +14974,7 @@  tsubst_omp_for_iterator (tree t, int i, tree declv, tree orig_declv,
   if (init && TREE_CODE (init) == DECL_EXPR)
     {
       /* We need to jump through some hoops to handle declarations in the
-	 for-init-statement, since we might need to handle auto deduction,
+	 init-statement, since we might need to handle auto deduction,
 	 but we need to keep control of initialization.  */
       decl_expr = init;
       init = DECL_INITIAL (DECL_EXPR_DECL (init));
@@ -15359,7 +15359,7 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
     case FOR_STMT:
       stmt = begin_for_stmt (NULL_TREE, NULL_TREE);
       RECUR (FOR_INIT_STMT (t));
-      finish_for_init_stmt (stmt);
+      finish_init_stmt (stmt);
       tmp = RECUR (FOR_COND (t));
       finish_for_cond (tmp, stmt, false);
       tmp = RECUR (FOR_EXPR (t));
diff --git gcc/cp/semantics.c gcc/cp/semantics.c
index 1d8f336..cae5afc 100644
--- gcc/cp/semantics.c
+++ gcc/cp/semantics.c
@@ -953,11 +953,11 @@  begin_for_stmt (tree scope, tree init)
   return r;
 }
 
-/* Finish the for-init-statement of a for-statement, which may be
+/* Finish the init-statement of a for-statement, which may be
    given by FOR_STMT.  */
 
 void
-finish_for_init_stmt (tree for_stmt)
+finish_init_stmt (tree for_stmt)
 {
   if (processing_template_decl)
     FOR_INIT_STMT (for_stmt) = pop_stmt_list (FOR_INIT_STMT (for_stmt));
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement1.C gcc/testsuite/g++.dg/cpp1z/init-statement1.C
index e69de29..fbe0d8b 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement1.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement1.C
@@ -0,0 +1,14 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern int foo (void);
+extern void bar (int);
+
+void
+f (void)
+{
+  if (auto p = foo (); p > 10) // { dg-warning "init-statement" "" { target c++14_down } }
+    bar (p);
+  else
+    bar (-p);
+}
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement2.C gcc/testsuite/g++.dg/cpp1z/init-statement2.C
index e69de29..8cfe1ab 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement2.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement2.C
@@ -0,0 +1,62 @@ 
+// { dg-options -std=c++1z }
+// Test C++17 selection statements with initializer, basic use.
+
+extern int foo (void);
+extern void bar (int);
+extern int g;
+
+void
+f (void)
+{
+  if (auto p = foo (); p > 10)
+    bar (p);
+  else
+    bar (-p);
+
+  if ((g += 2); g > 6)
+    bar (1);
+
+  if (auto a = 9, b = foo (); a + b > 10)
+    bar (a + b);
+  else
+    bar (a - b);
+
+  if (({ int a; 1;}))
+    bar (0);
+
+  if (auto i = foo (); i > 6)
+    bar (0);
+  else if (i++; i > 8)
+    bar (1);
+}
+
+extern void lock (void);
+
+void
+f2 (int i)
+{
+  if (lock (); i > 10)
+    ++i;
+  else
+    --i;
+}
+
+void
+f3 (int i)
+{
+  switch (i *= 2; auto idx = i)
+    {
+    case 4:
+      bar (3);
+      break;
+    default:
+      break;
+    }
+}
+
+void
+f4 (void)
+{
+  if constexpr (constexpr auto s = sizeof (int); s > 10)
+    foo ();
+}
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement3.C gcc/testsuite/g++.dg/cpp1z/init-statement3.C
index e69de29..c178eaf 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement3.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement3.C
@@ -0,0 +1,16 @@ 
+// { dg-do run }
+// { dg-options -std=c++1z }
+// Test C++17 selection statements with initializer, side-effects.
+
+int
+main ()
+{
+  int g = 0;
+
+  if (g++; g > 1)
+    __builtin_abort ();
+  if (++g; g > 2)
+    __builtin_abort ();
+  if (g != 2)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement4.C gcc/testsuite/g++.dg/cpp1z/init-statement4.C
index e69de29..a5f7d8b 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement4.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement4.C
@@ -0,0 +1,59 @@ 
+// { dg-options -std=c++1z }
+
+extern int foo (void);
+extern void bar (int), die (void);
+
+void
+f (void)
+{
+  if (auto i = foo (); i != -1)
+    bar (1);
+  else
+    die ();
+
+  i = 10; // { dg-error "not declared" }
+}
+
+void
+f2 (void)
+{
+  switch (auto i = foo (); i)
+    {
+    case 0:
+      bar (i + 1);
+      break;
+    case 1:
+      bar (i + 10);
+      break;
+    default:
+      break;
+    }
+
+  i = 10; // { dg-error "not declared" }
+}
+
+void
+f3 (void)
+{
+  if constexpr (constexpr auto i = sizeof (long); i < 2)
+    die ();
+  i = 4; // { dg-error "not declared" }
+}
+
+
+void
+f4 (void)
+{
+  {
+    if (auto i = foo (); i > -1)
+      {
+	if (i > 5)
+	  bar (i);
+	if (auto j = foo (); true)
+	  j++;
+	j--; // { dg-error "not declared" }
+      }
+    i = 10; // { dg-error "not declared" }
+  }
+  i = 10; // { dg-error "not declared" }
+}
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement5.C gcc/testsuite/g++.dg/cpp1z/init-statement5.C
index e69de29..6efa0ed 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement5.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement5.C
@@ -0,0 +1,16 @@ 
+// Testcase from P0305R1
+// { dg-options -std=c++1z }
+
+enum class status_code { SUCCESS };
+extern int get_value ();
+status_code bar (int);
+status_code do_more_stuff (void);
+
+status_code
+foo ()
+{
+  int n = get_value ();
+  if (status_code c = bar (n); c != status_code::SUCCESS) { return c; }
+  if (status_code c = do_more_stuff (); c != status_code::SUCCESS) { return c; }
+  return status_code::SUCCESS;
+}
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement6.C gcc/testsuite/g++.dg/cpp1z/init-statement6.C
index e69de29..53b0d31 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement6.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement6.C
@@ -0,0 +1,25 @@ 
+// Testcase from P0305R1
+// { dg-options -std=c++1z }
+
+#include <string>
+#include <map>
+#include <algorithm>
+
+std::map<int, std::string> m;
+extern int xread (int *);
+extern void publish (int), raise (int);
+
+void
+foo ()
+{
+  if (auto it = m.find (10); it != m.end ()) { std::string s = it->second; }
+  if (char buf[10]; std::fgets(buf, 10, stdin)) { m[0] += buf; }
+  if (int s; int count = xread (&s)) { publish(count); raise(s); }
+
+  const char *s;
+  if (auto keywords = {"if", "for", "while"};
+      std::any_of(keywords.begin(), keywords.end(), [&s](const char* kw) { return s == kw; }))
+    {
+      // whatever
+    }
+}
diff --git gcc/testsuite/g++.dg/cpp1z/init-statement7.C gcc/testsuite/g++.dg/cpp1z/init-statement7.C
index e69de29..a67617e 100644
--- gcc/testsuite/g++.dg/cpp1z/init-statement7.C
+++ gcc/testsuite/g++.dg/cpp1z/init-statement7.C
@@ -0,0 +1,9 @@ 
+// { dg-do run }
+// { dg-options -std=c++1z }
+
+int
+main ()
+{
+  if (int i = 10, &ir = i; [=]{ return ir; }() != 10)
+    __builtin_abort ();
+}