--- gcc/gimplify.cc.jj	2023-08-23 11:22:28.115592483 +0200
+++ gcc/gimplify.cc	2023-08-25 13:43:58.711847414 +0200
@@ -2588,17 +2588,33 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
       *handled_ops_p = false;
       break;
     case GIMPLE_CALL:
+      static_cast<location_t *>(wi->info)[0] = UNKNOWN_LOCATION;
       if (gimple_call_internal_p (stmt, IFN_FALLTHROUGH))
 	{
+	  location_t loc = gimple_location (stmt);
 	  gsi_remove (gsi_p, true);
+	  wi->removed_stmt = true;
+
+	  /* nothrow flag is added by genericize_c_loop to mark fallthrough
+	     statement at the end of some loop's body.  Those should be
+	     always diagnosed, either because they indeed don't precede
+	     a case label or default label, or because the next statement
+	     is not within the same iteration statement.  */
+	  if ((stmt->subcode & GF_CALL_NOTHROW) != 0)
+	    {
+	      pedwarn (loc, 0, "attribute %<fallthrough%> not preceding "
+			       "a case label or default label");
+	      break;
+	    }
+
 	  if (gsi_end_p (*gsi_p))
 	    {
-	      *static_cast<location_t *>(wi->info) = gimple_location (stmt);
-	      return integer_zero_node;
+	      static_cast<location_t *>(wi->info)[0] = BUILTINS_LOCATION;
+	      static_cast<location_t *>(wi->info)[1] = loc;
+	      break;
 	    }
 
 	  bool found = false;
-	  location_t loc = gimple_location (stmt);
 
 	  gimple_stmt_iterator gsi2 = *gsi_p;
 	  stmt = gsi_stmt (gsi2);
@@ -2648,6 +2664,7 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
 	}
       break;
     default:
+      static_cast<location_t *>(wi->info)[0] = UNKNOWN_LOCATION;
       break;
     }
   return NULL_TREE;
@@ -2659,14 +2676,16 @@ static void
 expand_FALLTHROUGH (gimple_seq *seq_p)
 {
   struct walk_stmt_info wi;
-  location_t loc;
+  location_t loc[2];
   memset (&wi, 0, sizeof (wi));
-  wi.info = (void *) &loc;
+  loc[0] = UNKNOWN_LOCATION;
+  loc[1] = UNKNOWN_LOCATION;
+  wi.info = (void *) &loc[0];
   walk_gimple_seq_mod (seq_p, expand_FALLTHROUGH_r, NULL, &wi);
-  if (wi.callback_result == integer_zero_node)
+  if (loc[0] != UNKNOWN_LOCATION)
     /* We've found [[fallthrough]]; at the end of a switch, which the C++
        standard says is ill-formed; see [dcl.attr.fallthrough].  */
-    pedwarn (loc, 0, "attribute %<fallthrough%> not preceding "
+    pedwarn (loc[1], 0, "attribute %<fallthrough%> not preceding "
 	     "a case label or default label");
 }
 
--- gcc/gimple-walk.cc.jj	2023-01-02 09:32:28.298199849 +0100
+++ gcc/gimple-walk.cc	2023-08-25 14:21:10.264376130 +0200
@@ -56,11 +56,21 @@ walk_gimple_seq_mod (gimple_seq *pseq, w
 	  gcc_assert (wi);
 	  wi->callback_result = ret;
 
-	  return wi->removed_stmt ? NULL : gsi_stmt (gsi);
+	  gimple *g;
+	  if (!wi->removed_stmt)
+	    g = gsi_stmt (gsi);
+	  else
+	    {
+	      g = NULL;
+	      wi->removed_stmt = false;
+	    }
+	  return g;
 	}
 
       if (!wi->removed_stmt)
 	gsi_next (&gsi);
+      else
+	wi->removed_stmt = false;
     }
 
   if (wi)
--- gcc/c-family/c-gimplify.cc.jj	2023-07-11 13:40:37.594467535 +0200
+++ gcc/c-family/c-gimplify.cc	2023-08-25 12:38:02.406574469 +0200
@@ -307,6 +307,27 @@ genericize_c_loop (tree *stmt_p, locatio
     }
 
   append_to_statement_list (body, &stmt_list);
+  if (c_dialect_cxx ()
+      && stmt_list
+      && TREE_CODE (stmt_list) == STATEMENT_LIST)
+    {
+      tree_stmt_iterator tsi = tsi_last (stmt_list);
+      if (!tsi_end_p (tsi))
+	{
+	  tree t = *tsi;
+	  while (TREE_CODE (t) == CLEANUP_POINT_EXPR
+		 || TREE_CODE (t) == EXPR_STMT
+		 || CONVERT_EXPR_CODE_P (TREE_CODE (t)))
+	    t = TREE_OPERAND (t, 0);
+	  /* For C++, if iteration statement body ends with fallthrough
+	     statement, mark it such that we diagnose it even if next
+	     statement would be labeled statement with case/default label.  */
+	  if (TREE_CODE (t) == CALL_EXPR
+	      && !CALL_EXPR_FN (t)
+	      && CALL_EXPR_IFN (t) == IFN_FALLTHROUGH)
+	    TREE_NOTHROW (t) = 1;
+	}
+    }
   finish_bc_block (&stmt_list, bc_continue, clab);
   if (incr)
     {
--- gcc/testsuite/g++.dg/DRs/dr2406.C.jj	2023-08-25 14:16:53.095670934 +0200
+++ gcc/testsuite/g++.dg/DRs/dr2406.C	2023-08-25 14:16:04.732290555 +0200
@@ -0,0 +1,81 @@
+// DR 2406 - [[fallthrough]] attribute and iteration statements
+// { dg-do compile { target c++11 } }
+// { dg-options "-pedantic-errors -Wimplicit-fallthrough" }
+
+void bar ();
+void baz ();
+void qux ();
+
+void
+foo (int n)
+{
+  switch (n)
+    {
+    case 1:
+    case 2:
+      bar ();
+      [[fallthrough]];
+    case 3:
+      do
+	{
+	  [[fallthrough]];	// { dg-error "attribute 'fallthrough' not preceding a case label or default label" }
+	}
+      while (false);
+    case 6:
+      do
+	{
+	  [[fallthrough]];	// { dg-error "attribute 'fallthrough' not preceding a case label or default label" }
+	}
+      while (n--);
+    case 7:
+      while (false)
+	{
+	  [[fallthrough]];	// { dg-error "attribute 'fallthrough' not preceding a case label or default label" }
+	}
+    case 5:
+      baz ();			// { dg-warning "this statement may fall through" }
+    case 4:			// { dg-message "here" }
+      qux ();
+      [[fallthrough]];		// { dg-error "attribute 'fallthrough' not preceding a case label or default label" }
+    }
+}
+
+void
+corge (int n)
+{
+  switch (n)
+    {
+    case 1:
+      {
+	int i = 0;
+	do
+	  {
+	    [[fallthrough]];	// { dg-error "attribute 'fallthrough' not preceding a case label or default label" }
+	  }
+	while (false);
+      }
+    case 2:
+      bar ();
+      break;
+    default:
+      break;
+    }
+}
+
+void
+fred (int n)
+{
+  switch (n)
+    {
+    case 1:
+      {
+	int i = 0;
+	[[fallthrough]];
+      }
+    case 2:
+      bar ();
+      break;
+    default:
+      break;
+    }
+}
