From: Jakub Jelinek Date: Tue, 5 Sep 2023 15:26:59 +0000 (+0200) Subject: c++: Diagnose [basic.scope.block]/2 violations even in compound-stmt of function... X-Git-Tag: basepoints/gcc-15~6426 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c24982689b8af19da9a64f2283fb99764a1c5db0;p=thirdparty%2Fgcc.git c++: Diagnose [basic.scope.block]/2 violations even in compound-stmt of function-try-block [PR52953] 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 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. --- diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 89e8b85e3f89..255c4026bdbd 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -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. */; diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 2d747561e1f5..a2bcd4f2633d 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -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), diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 613745ba501a..14e791597f39 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -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. */ diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 5b539ceefbf7..0f7f4e87ae42 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -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; diff --git a/gcc/testsuite/g++.dg/diagnostic/redeclaration-1.C b/gcc/testsuite/g++.dg/diagnostic/redeclaration-1.C index f0e43d414a86..515fa39b4a7e 100644 --- a/gcc/testsuite/g++.dg/diagnostic/redeclaration-1.C +++ b/gcc/testsuite/g++.dg/diagnostic/redeclaration-1.C @@ -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 index 000000000000..0934431121b8 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/redeclaration-3.C @@ -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; + } +} diff --git a/gcc/testsuite/g++.dg/parse/pr31952-1.C b/gcc/testsuite/g++.dg/parse/pr31952-1.C index aad3a11a77de..bae4713c4b7e 100644 --- a/gcc/testsuite/g++.dg/parse/pr31952-1.C +++ b/gcc/testsuite/g++.dg/parse/pr31952-1.C @@ -8,7 +8,7 @@ try } catch (...) { - int bar = 0; // { dg-error "redeclaration" } + int bar = 0; // { dg-error "shadows a parameter" } return 1; } diff --git a/gcc/testsuite/g++.dg/parse/pr31952-3.C b/gcc/testsuite/g++.dg/parse/pr31952-3.C index 9fe5f0283786..b589097d36db 100644 --- a/gcc/testsuite/g++.dg/parse/pr31952-3.C +++ b/gcc/testsuite/g++.dg/parse/pr31952-3.C @@ -6,7 +6,7 @@ try { return 0; } -catch (int bar) // { dg-error "redeclaration" } +catch (int bar) // { dg-error "shadows a parameter" } { return 1; }