]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Diagnose [basic.scope.block]/2 violations even in compound-stmt of function...
authorJakub Jelinek <jakub@redhat.com>
Tue, 5 Sep 2023 15:26:59 +0000 (17:26 +0200)
committerJakub Jelinek <jakub@redhat.com>
Tue, 5 Sep 2023 15:26:59 +0000 (17:26 +0200)
As the following testcase shows, while check_local_shadow diagnoses most of
the [basic.scope.block]/2 violations, it doesn't diagnose when parameter's
name is redeclared inside of the compound-stmt of a function-try-block.

There is in that case an extra scope (sk_try with parent artificial
sk_block with for FUNCTION_NEEDS_BODY_BLOCK another sk_block and only then
sk_function_param).

The in_function_try_handler case doesn't work correctly
void
foo (int x)
try {
}
catch (int)
{
  try {
  } catch (int x)
  {
  }
  try {
  } catch (int)
  {
    int x;
  }
}
(which is valid) is rejected, because
                || (TREE_CODE (old) == PARM_DECL
                    && (current_binding_level->kind == sk_catch
                        || current_binding_level->level_chain->kind == sk_catch)
                    && in_function_try_handler))
is true but nothing verified that for the first case
current_binding_level->level_chain->kind == sk_function_params
(with perhaps artificial scopes in between and in the latter case
with one extra level in between).

The patch also changes behavior where for catch handlers of function-try-block
the diagnostics will have the shadows function parameter wording as pedwarn
rather than the old redeclaration permerror.

2023-09-05  Jakub Jelinek  <jakub@redhat.com>

PR c++/52953
* name-lookup.h (struct cp_binding_level): Add artificial bit-field.
Formatting fixes.
* name-lookup.cc (check_local_shadow): Skip artificial bindings when
checking if parameter scope is parent scope.  Don't special case
FUNCTION_NEEDS_BODY_BLOCK.  Diagnose the in_function_try_handler
cases in the b->kind == sk_function_parms test and verify no
non-artificial intervening scopes.  Add missing auto_diagnostic_group.
* decl.cc (begin_function_body): Set
current_binding_level->artificial.
* semantics.cc (begin_function_try_block): Likewise.

* g++.dg/diagnostic/redeclaration-1.C: Expect different diagnostic
wording.
* g++.dg/diagnostic/redeclaration-3.C: New test.
* g++.dg/parse/pr31952-1.C: Expect different diagnostic wording.
* g++.dg/parse/pr31952-3.C: Likewise.

gcc/cp/decl.cc
gcc/cp/name-lookup.cc
gcc/cp/name-lookup.h
gcc/cp/semantics.cc
gcc/testsuite/g++.dg/diagnostic/redeclaration-1.C
gcc/testsuite/g++.dg/diagnostic/redeclaration-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/pr31952-1.C
gcc/testsuite/g++.dg/parse/pr31952-3.C

index 89e8b85e3f8931f2e377ea6acf006913ae68c5a9..255c4026bdbdf565ca5ecab1bc2f9df114ceb103 100644 (file)
@@ -18002,6 +18002,7 @@ begin_function_body (void)
     keep_next_level (true);
 
   tree stmt = begin_compound_stmt (BCS_FN_BODY);
+  current_binding_level->artificial = 1;
 
   if (processing_template_decl)
     /* Do nothing now.  */;
index 2d747561e1f5a4ffb01655e66e41843964a51798..a2bcd4f2633ddab4a697789b73a2878e9f005aaa 100644 (file)
@@ -3146,14 +3146,20 @@ check_local_shadow (tree decl)
             them there.  */
          cp_binding_level *b = current_binding_level->level_chain;
 
-         if (FUNCTION_NEEDS_BODY_BLOCK (current_function_decl))
-           /* Skip the ctor/dtor cleanup level.  */
+         if (in_function_try_handler && b->kind == sk_catch)
+           b = b->level_chain;
+
+         /* Skip artificially added scopes which aren't present
+            in the C++ standard, e.g. for function-try-block or
+            ctor/dtor cleanups.  */
+         while (b->artificial)
            b = b->level_chain;
 
          /* [basic.scope.param] A parameter name shall not be redeclared
             in the outermost block of the function definition.  */
          if (b->kind == sk_function_parms)
            {
+             auto_diagnostic_group d;
              error_at (DECL_SOURCE_LOCATION (decl),
                        "declaration of %q#D shadows a parameter", decl);
              inform (DECL_SOURCE_LOCATION (old),
@@ -3194,17 +3200,10 @@ check_local_shadow (tree decl)
         shall not be redeclared in the outermost block of the handler.
         3.3.3/2:  A parameter name shall not be redeclared (...) in
         the outermost block of any handler associated with a
-        function-try-block.
-        3.4.1/15: The function parameter names shall not be redeclared
-        in the exception-declaration nor in the outermost block of a
-        handler for the function-try-block.  */
-      else if ((TREE_CODE (old) == VAR_DECL
-               && old_scope == current_binding_level->level_chain
-               && old_scope->kind == sk_catch)
-              || (TREE_CODE (old) == PARM_DECL
-                  && (current_binding_level->kind == sk_catch
-                      || current_binding_level->level_chain->kind == sk_catch)
-                  && in_function_try_handler))
+        function-try-block.  */
+      else if (TREE_CODE (old) == VAR_DECL
+              && old_scope == current_binding_level->level_chain
+              && old_scope->kind == sk_catch)
        {
          auto_diagnostic_group d;
          if (permerror (DECL_SOURCE_LOCATION (decl),
index 613745ba501ae7eaa449c87e4d468cb9127ed359..14e791597f3931d966ddae78133a72f669c3a00e 100644 (file)
@@ -292,11 +292,11 @@ struct GTY(()) cp_binding_level {
       only valid if KIND == SK_TEMPLATE_PARMS.  */
   BOOL_BITFIELD explicit_spec_p : 1;
 
-  /* true means make a BLOCK for this level regardless of all else.  */
+  /* True means make a BLOCK for this level regardless of all else.  */
   unsigned keep : 1;
 
   /* Nonzero if this level can safely have additional
-      cleanup-needing variables added to it.  */
+     cleanup-needing variables added to it.  */
   unsigned more_cleanups_ok : 1;
   unsigned have_cleanups : 1;
 
@@ -308,9 +308,13 @@ struct GTY(()) cp_binding_level {
   unsigned defining_class_p : 1;
 
   /* True for SK_FUNCTION_PARMS of a requires-expression.  */
-  unsigned requires_expression: 1;
+  unsigned requires_expression : 1;
 
-  /* 22 bits left to fill a 32-bit word.  */
+  /* True for artificial blocks which should be ignored when finding
+     parent scope.  */
+  unsigned artificial : 1;
+
+  /* 21 bits left to fill a 32-bit word.  */
 };
 
 /* The binding level currently in effect.  */
index 5b539ceefbf7da5f79bf9d05f85fb1b58a1dc90c..0f7f4e87ae424fd74b94b43ffc07eb4049097b79 100644 (file)
@@ -1624,6 +1624,7 @@ begin_function_try_block (tree *compound_stmt)
   /* This outer scope does not exist in the C++ standard, but we need
      a place to put __FUNCTION__ and similar variables.  */
   *compound_stmt = begin_compound_stmt (0);
+  current_binding_level->artificial = 1;
   r = begin_try_block ();
   FN_TRY_BLOCK_P (r) = 1;
   return r;
index f0e43d414a864599434ebad5760f500dffe0c028..515fa39b4a7efc22cfb5d3d578f963ad4600e3e0 100644 (file)
@@ -15,6 +15,6 @@ bar (int i)  // { dg-message "10:.int i. previously declared here" }
     { }
   catch (...)
     {
-      int i  // { dg-error "11:redeclaration of .int i." }
+      int i  // { dg-error "11:declaration of .int i. shadows a parameter" }
        (0);
     }
diff --git a/gcc/testsuite/g++.dg/diagnostic/redeclaration-3.C b/gcc/testsuite/g++.dg/diagnostic/redeclaration-3.C
new file mode 100644 (file)
index 0000000..0934431
--- /dev/null
@@ -0,0 +1,225 @@
+// PR c++/52953
+// { dg-do compile }
+// { dg-options "-pedantic-errors -Wno-switch-unreachable" }
+
+void
+foo (int x)                            // { dg-message "'int x' previously declared here" }
+{
+  int x;                               // { dg-error "declaration of 'int x' shadows a parameter" }
+}
+
+void
+bar (int x)                            // { dg-message "'int x' previously declared here" }
+try
+{
+  int x;                               // { dg-error "declaration of 'int x' shadows a parameter" }
+}
+catch (...)
+{
+}
+
+volatile int v;
+
+void
+baz ()
+{
+#if __cplusplus >= 201103L
+  auto f = [] (int x) { int x; };      // { dg-error "declaration of 'int x' shadows a parameter" "" { target c++11 } }
+                                       // { dg-message "'int x' previously declared here" "" { target c++11 } .-1 }
+#endif
+  if (int x = 1)                       // { dg-message "'int x' previously declared here" }
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    }
+  if (int x = 0)                       // { dg-message "'int x' previously declared here" }
+    ;
+  else
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    }
+  if (int x = 1)                       // { dg-message "'int x' previously declared here" }
+    int x;                             // { dg-error "redeclaration of 'int x'" }
+  if (int x = 0)                       // { dg-message "'int x' previously declared here" }
+    ;
+  else
+    int x;                             // { dg-error "redeclaration of 'int x'" }
+  switch (int x = 1)                   // { dg-message "'int x' previously declared here" }
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    default:;
+    }
+  switch (int x = 1)                   // { dg-message "'int x' previously declared here" }
+    int x;                             // { dg-error "redeclaration of 'int x'" }
+  while (int x = v)                    // { dg-message "'int x' previously declared here" }
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    }
+  while (int x = v)                    // { dg-message "'int x' previously declared here" }
+    int x;                             // { dg-error "redeclaration of 'int x'" }
+  for (int x = v; x; ++x)              // { dg-message "'int x' previously declared here" }
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    }
+  for (int x = v; x; ++x)              // { dg-message "'int x' previously declared here" }
+    int x;                             // { dg-error "redeclaration of 'int x'" }
+  for (; int x = v; )                  // { dg-message "'int x' previously declared here" }
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    }
+  for (; int x = v; )                  // { dg-message "'int x' previously declared here" }
+    int x;                             // { dg-error "redeclaration of 'int x'" }
+  try
+    {
+    }
+  catch (int x)                                // { dg-message "'int x' previously declared here" }
+    {
+      int x;                           // { dg-error "redeclaration of 'int x'" }
+    }
+  if (int x = 1)
+    if (int x = 1)
+      ;
+  if (int x = 0)
+    ;
+  else
+    if (int x = 1)
+      ;
+  if (int x = 1)
+    switch (int x = 1)
+      ;
+  if (int x = 0)
+    while (int x = v)
+      ;
+  if (int x = 0)
+    for (int x = v; x; ++x)
+      ;
+  switch (int x = 1)
+    switch (int x = 1)
+      {
+      case 1:;
+      }
+  while (int x = 0)
+    if (int x = 1)
+      ;
+  for (int x = v; x; ++x)
+    for (int x = v; x; ++x)
+      ;
+}
+
+void
+qux (int x)                            // { dg-message "'int x' previously declared here" }
+try
+{
+}
+catch (int x)                          // { dg-error "declaration of 'int x' shadows a parameter" }
+{
+}
+
+void
+corge (int x)                          // { dg-message "'int x' previously declared here" }
+try
+{
+}
+catch (...)
+{
+  int x;                               // { dg-error "declaration of 'int x' shadows a parameter" }
+}
+
+void
+fred (int x)                           // { dg-message "'int x' previously declared here" }
+try
+{
+}
+catch (int)
+{
+}
+catch (long)
+{
+  int x;                               // { dg-error "declaration of 'int x' shadows a parameter" }
+}
+
+void
+garply (int x)
+{
+  try
+    {
+      int x;
+    }
+  catch (...)
+    {
+      int x;
+    }
+}
+
+struct S
+{
+  S (int x)                            // { dg-message "'int x' previously declared here" }
+  try : s (x)
+  {
+    int x;                             // { dg-error "declaration of 'int x' shadows a parameter" }
+  }
+  catch (...)
+  {
+  }
+  int s;
+};
+
+struct T
+{
+  T (int x)                            // { dg-message "'int x' previously declared here" }
+  try : t (x)
+  {
+  }
+  catch (...)
+  {
+    int x;                             // { dg-error "declaration of 'int x' shadows a parameter" }
+  }
+  int t;
+};
+
+struct U
+{
+  U (int x) : u (x)
+  {
+    try
+    {
+      int x;
+    }
+    catch (...)
+    {
+      int x;
+    }
+  }
+  int u;
+};
+
+struct V
+{
+  V (int x) : v (x)
+  {
+    {
+      int x;
+    }
+  }
+  int v;
+};
+
+void
+foobar (int x)
+try
+{
+}
+catch (int)
+{
+  try
+  {
+  }
+  catch (int x)
+  {
+  }
+  try
+  {
+  } catch (int)
+  {
+    int x;
+  }
+}
index aad3a11a77deaf047d2971d81dbcf925badf106b..bae4713c4b7e95873d0a7b1c3349823a822b68e1 100644 (file)
@@ -8,7 +8,7 @@ try
 }
 catch (...)
 {
-  int bar = 0; // { dg-error "redeclaration" }
+  int bar = 0; // { dg-error "shadows a parameter" }
   return 1;
 }
 
index 9fe5f02837860693c243a602a44518cc8ba8d348..b589097d36db64451da5795a795f725c66831988 100644 (file)
@@ -6,7 +6,7 @@ try
 {
   return 0;
 }
-catch (int bar)  // { dg-error "redeclaration" }
+catch (int bar)  // { dg-error "shadows a parameter" }
 {
   return 1;
 }