c++: ICE with delayed noexcept and attribute used [PR97966]
authorMarek Polacek <polacek@redhat.com>
Tue, 12 Jan 2021 21:16:44 +0000 (16:16 -0500)
committerMarek Polacek <polacek@redhat.com>
Fri, 22 Jan 2021 00:22:01 +0000 (19:22 -0500)
Another ICE with delayed noexcept parsing, but a bit gnarlier.

A function definition marked with __attribute__((used)) ought to be
emitted even when it is not referenced in the TU.  For a member function
template marked with __attribute__((used)) this means that it will
be instantiated: in instantiate_class_template_1 we have

11971               /* Instantiate members marked with attribute used.  */
11972               if (r != error_mark_node && DECL_PRESERVE_P (r))
11973                 mark_used (r);

It is not so surprising that this doesn't work well with delayed
noexcept parsing: when we're processing the function template we delay
the parsing, so the member "foo" is found, but then when we're
instantiating it, "foo" hasn't yet been seen, which creates a
discrepancy and a crash ensues.  "foo" hasn't yet been seen because
instantiate_class_template_1 just loops over the class members and
instantiates right away.

To make it work, this patch uses a vector to keep track of members
marked with attribute used and uses it to instantiate such members
only after we're done with the class; in particular, after we have
called finish_member_declaration for each member.  And we ought to
be verifying that we did emit such members, so I've added a bunch
of dg-finals.

gcc/cp/ChangeLog:

PR c++/97966
* pt.c (instantiate_class_template_1): Instantiate members
marked with attribute used only after we're done instantiating
the class.

gcc/testsuite/ChangeLog:

PR c++/97966
* g++.dg/cpp0x/noexcept63.C: New test.

gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/noexcept63.C [new file with mode: 0644]

index 373f82796047226910eea33d00967eababbfc331..63a0a1104404109d486bfe515bdbc41cf71a4514 100644 (file)
@@ -11895,6 +11895,9 @@ instantiate_class_template_1 (tree type)
      relative to the scope of the class.  */
   pop_to_parent_deferring_access_checks ();
 
+  /* A vector to hold members marked with attribute used. */
+  auto_vec<tree> used;
+
   /* Now members are processed in the order of declaration.  */
   for (member = CLASSTYPE_DECL_LIST (pattern);
        member; member = TREE_CHAIN (member))
@@ -11968,7 +11971,7 @@ instantiate_class_template_1 (tree type)
              finish_member_declaration (r);
              /* Instantiate members marked with attribute used.  */
              if (r != error_mark_node && DECL_PRESERVE_P (r))
-               mark_used (r);
+               used.safe_push (r);
              if (TREE_CODE (r) == FUNCTION_DECL
                  && DECL_OMP_DECLARE_REDUCTION_P (r))
                cp_check_omp_declare_reduction (r);
@@ -12034,7 +12037,7 @@ instantiate_class_template_1 (tree type)
                             /*flags=*/0);
                          /* Instantiate members marked with attribute used. */
                          if (r != error_mark_node && DECL_PRESERVE_P (r))
-                           mark_used (r);
+                           used.safe_push (r);
                        }
                      else if (TREE_CODE (r) == FIELD_DECL)
                        {
@@ -12225,6 +12228,11 @@ instantiate_class_template_1 (tree type)
   if (TYPE_CONTAINS_VPTR_P (type) && CLASSTYPE_KEY_METHOD (type))
     vec_safe_push (keyed_classes, type);
 
+  /* Now that we've gone through all the members, instantiate those
+     marked with attribute used.  */
+  for (tree x : used)
+    mark_used (x);
+
   return type;
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept63.C b/gcc/testsuite/g++.dg/cpp0x/noexcept63.C
new file mode 100644 (file)
index 0000000..cf048f5
--- /dev/null
@@ -0,0 +1,63 @@
+// PR c++/97966
+// { dg-do compile { target c++11 } }
+
+template <int>
+struct S1 {
+  __attribute__((used)) S1() noexcept(noexcept(this->foo())) { }
+  void foo();
+};
+
+template <int>
+struct S2 {
+  __attribute__((used)) void bar() noexcept(noexcept(this->foo())) { }
+  void foo();
+};
+
+template <int>
+struct S3 {
+  void __attribute__((used)) bar() noexcept(noexcept(this->foo())) { }
+  void foo();
+};
+
+template <int>
+struct S4 {
+  [[gnu::used]] void bar() noexcept(noexcept(this->foo())) { }
+  void foo();
+};
+
+template <int>
+struct S5 {
+  void bar() noexcept(noexcept(this->foo())) __attribute__((used)) { }
+  void foo();
+};
+
+template <int>
+struct S6 {
+  template <int>
+  struct N {
+    [[gnu::used]] void bar() noexcept(noexcept(this->foo())) { }
+    void foo();
+  };
+};
+
+void
+g ()
+{
+  S1<1> s1;
+  S2<1> s2;
+  S3<1> s3;
+  S4<1> s4;
+  S5<1> s5;
+  S6<1>::N<1> n;
+}
+
+// Make sure that we did emit the functions marked with attribute used
+// even though they're not referenced in this TU.  (Well, the S1()
+// constructor is.)
+// { dg-final { scan-assembler "_ZN2S1ILi1EEC1Ev" } }
+// { dg-final { scan-assembler "_ZN2S1ILi1EEC2Ev" } }
+// { dg-final { scan-assembler "_ZN2S2ILi1EE3barEv" } }
+// { dg-final { scan-assembler "_ZN2S3ILi1EE3barEv" } }
+// { dg-final { scan-assembler "_ZN2S4ILi1EE3barEv" } }
+// { dg-final { scan-assembler "_ZN2S5ILi1EE3barEv" } }
+// { dg-final { scan-assembler "_ZN2S6ILi1EE1NILi1EE3barEv" } }