On Sat, Apr 03, 2021 at 10:47:13AM -0400, Jason Merrill wrote: > On 4/2/21 9:53 PM, Marek Polacek wrote: > > Coming back to > > <https://gcc.gnu.org/pipermail/gcc-patches/2019-August/527699.html>: > > > > This is a crash that points to a GC problem. Consider this test: > > > > __attribute__ ((unused)) struct S { > > S() { } > > } s; > > > > We're parsing a simple-declaration. While parsing the decl specs, we parse > > the attribute, which means creating a TREE_LIST using ggc_alloc_*. > > > > A function body is a complete-class context so when parsing the > > member-specification of this class-specifier, we parse the bodies of the > > functions we'd queued in cp_parser_late_parsing_for_member. This then > > leads to this call chain: > > cp_parser_function_definition_after_declarator -> expand_or_defer_fn -> > > expand_or_defer_fn_1 -> maybe_clone_body -> expand_or_defer_fn -> > > cgraph_node::finalize_function -> ggc_collect. > > > > In this test, the ggc_collect call collects the TREE_LIST we had > > allocated, and a crash duly ensues. > > > > I couldn't do what Richard suggested, that is, attach the attribute list > > to struct S, because we don't pass decl_specs from cp_parser_type_specifier > > down to cp_parser_class_specifier. Therefore I've attempted to do "push the > > decl_specifiers onto a vec that is a GC root", except I couldn't really push > > the decl_specifiers, because first I'd have to mark cp_decl_specifier_seq > > with > > GTY(()) and even that wouldn't be enough for me to be able to create > > > > static GTY(()) vec<cp_decl_specifier_seq *, va_gc> > > > > But here we only care about cp_decl_specifier_seq::attributes, so the > > patch is just this. I've also extended the test so now we test a nested > > class too. > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10.4? > > > > gcc/cp/ChangeLog: > > > > PR c++/91416 > > * parser.c: Create a GC root for attributes in a decl specifier. > > (cp_parser_declaration): Free it. > > (cp_parser_type_specifier): Push ->attributes onto it. > > > > gcc/testsuite/ChangeLog: > > > > PR c++/91416 > > * g++.dg/other/gc7.C: New test. > > --- > > gcc/cp/parser.c | 7 +++++++ > > gcc/testsuite/g++.dg/other/gc7.C | 16 ++++++++++++++++ > > 2 files changed, 23 insertions(+) > > create mode 100644 gcc/testsuite/g++.dg/other/gc7.C > > > > diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c > > index 808e5b61b1e..31da9f56b1b 100644 > > --- a/gcc/cp/parser.c > > +++ b/gcc/cp/parser.c > > @@ -13957,6 +13957,11 @@ cp_parser_declaration_seq_opt (cp_parser* parser) > > } > > } > > +/* Preserve the attributes across a garbage collect (by making it a GC > > + root), which can occur when parsing a member function. */ > > + > > +static GTY(()) vec<tree, va_gc> *cp_parser_decl_specs_attrs; > > + > > /* Parse a declaration. > > declaration: > > @@ -14140,6 +14145,7 @@ cp_parser_declaration (cp_parser* parser, tree > > prefix_attrs) > > /* Free any declarators allocated. */ > > obstack_free (&declarator_obstack, p); > > + vec_free (cp_parser_decl_specs_attrs); > > } > > /* Parse a namespace-scope declaration. */ > > @@ -18438,6 +18444,7 @@ cp_parser_type_specifier (cp_parser* parser, > > /* Parse tentatively so that we can back up if we don't find a > > class-specifier. */ > > cp_parser_parse_tentatively (parser); > > + vec_safe_push (cp_parser_decl_specs_attrs, decl_specs->attributes); > > This looks like any time we see 'class' in a function we'll add another > entry to the vec, that accumulates until we finish the enclosing top-level > declaration. Let's pop the entry again after the call to > cp_parser_class_specifier, and only push/pop if there are actually any > attributes.
Ah, yeah, push/pop works here too, thanks. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10? -- >8 -- Coming back to <https://gcc.gnu.org/pipermail/gcc-patches/2019-August/527699.html>: This is a crash that points to a GC problem. Consider this test: __attribute__ ((unused)) struct S { S() { } } s; We're parsing a simple-declaration. While parsing the decl specs, we parse the attribute, which means creating a TREE_LIST using ggc_alloc_*. A function body is a complete-class context so when parsing the member-specification of this class-specifier, we parse the bodies of the functions we'd queued in cp_parser_late_parsing_for_member. This then leads to this call chain: cp_parser_function_definition_after_declarator -> expand_or_defer_fn -> expand_or_defer_fn_1 -> maybe_clone_body -> expand_or_defer_fn -> cgraph_node::finalize_function -> ggc_collect. In this test, the ggc_collect call collects the TREE_LIST we had allocated, and a crash duly ensues. I couldn't do what Richard suggested, that is, attach the attribute list to struct S, because we don't pass decl_specs from cp_parser_type_specifier down to cp_parser_class_specifier. Therefore I've attempted to do "push the decl_specifiers onto a vec that is a GC root", except I couldn't really push the decl_specifiers, because first I'd have to mark cp_decl_specifier_seq with GTY(()) and even that wouldn't be enough for me to be able to create static GTY(()) vec<cp_decl_specifier_seq *, va_gc> But here we only care about cp_decl_specifier_seq::attributes, so the patch is just this. I've also extended the test so now we test a nested class too. gcc/cp/ChangeLog: PR c++/91416 * parser.c: Create a GC root for attributes in a decl specifier. (cp_parser_type_specifier): Push/pop ->attributes onto/from it. gcc/testsuite/ChangeLog: PR c++/91416 * g++.dg/other/gc7.C: New test. --- gcc/cp/parser.c | 9 +++++++++ gcc/testsuite/g++.dg/other/gc7.C | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 gcc/testsuite/g++.dg/other/gc7.C diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 808e5b61b1e..c915d6415de 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -18346,6 +18346,11 @@ cp_parser_explicit_specialization (cp_parser* parser) --parser->num_template_parameter_lists; } +/* Preserve the attributes across a garbage collect (by making it a GC + root), which can occur when parsing a member function. */ + +static GTY(()) vec<tree, va_gc> *cp_parser_decl_specs_attrs; + /* Parse a type-specifier. type-specifier: @@ -18438,8 +18443,12 @@ cp_parser_type_specifier (cp_parser* parser, /* Parse tentatively so that we can back up if we don't find a class-specifier. */ cp_parser_parse_tentatively (parser); + if (decl_specs->attributes) + vec_safe_push (cp_parser_decl_specs_attrs, decl_specs->attributes); /* Look for the class-specifier. */ type_spec = cp_parser_class_specifier (parser); + if (decl_specs->attributes) + cp_parser_decl_specs_attrs->pop (); invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, type_spec); /* If that worked, we're done. */ if (cp_parser_parse_definitely (parser)) diff --git a/gcc/testsuite/g++.dg/other/gc7.C b/gcc/testsuite/g++.dg/other/gc7.C new file mode 100644 index 00000000000..ab436bac72f --- /dev/null +++ b/gcc/testsuite/g++.dg/other/gc7.C @@ -0,0 +1,16 @@ +// PR c++/91416 - GC during late parsing collects live data. +// { dg-do compile } +// { dg-options "--param ggc-min-heapsize=0 --param ggc-min-expand=0" } + +__attribute__ ((unused)) struct S { + S() { } +} s; + +__attribute__ ((unused)) struct X { + void fn () + { + __attribute__ ((unused)) struct N { + N() { } + } n; + } +} x; base-commit: fc27115d6107f219e6f3dc610c99210005fe9dc5 -- 2.30.2