c++: GC during late parsing collects live data [PR91416]

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.
This commit is contained in:
Marek Polacek 2021-04-02 17:11:32 -04:00
parent 89bc1d4e7c
commit a809d8a737
2 changed files with 25 additions and 0 deletions

View File

@ -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))

View File

@ -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;