aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2021-04-02 17:11:32 -0400
committerMarek Polacek <polacek@redhat.com>2021-04-03 18:20:56 -0400
commita809d8a737da1ccebcd93065fc57fc0f4d94894a (patch)
tree4effe88e7b3bbba078f3ec228682944b5e9f6a03 /gcc
parent89bc1d4e7cdd0b2d012050134ad1d464ec357f0b (diff)
downloadgcc-a809d8a737da1ccebcd93065fc57fc0f4d94894a.zip
gcc-a809d8a737da1ccebcd93065fc57fc0f4d94894a.tar.gz
gcc-a809d8a737da1ccebcd93065fc57fc0f4d94894a.tar.bz2
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/parser.c9
-rw-r--r--gcc/testsuite/g++.dg/other/gc7.C16
2 files changed, 25 insertions, 0 deletions
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 808e5b6..c915d64 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 0000000..ab436ba
--- /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;