On 1/20/21 7:49 PM, Marek Polacek wrote:
On Tue, Jan 19, 2021 at 03:37:25PM -0500, Jason Merrill wrote:
On 1/12/21 9:13 PM, Marek Polacek wrote:
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 a 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.

That seems like the bug; we shouldn't instantiate any members until we're
done instantiating the class.

I reckon we can make it work, then:

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
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                             | 12 ++++-
  gcc/testsuite/g++.dg/cpp0x/noexcept63.C | 63 +++++++++++++++++++++++++
  2 files changed, 73 insertions(+), 2 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/noexcept63.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 373f8279604..81c78322a46 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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)
                        {
@@ -12185,6 +12188,11 @@ instantiate_class_template_1 (tree type)
        }
      }
+ /* Now that we've gone through all the members, instantiate those
+     marked with attribute used.  */
+  for (tree &x : used)

This doesn't need to be a reference. And I think we want this to happen even later, after finish_struct_1.

+    mark_used (x);
+
    if (fn_context)
      {
        /* Restore these before substituting into the lambda capture
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept63.C 
b/gcc/testsuite/g++.dg/cpp0x/noexcept63.C
new file mode 100644
index 00000000000..cf048f56c2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept63.C
@@ -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" } }

base-commit: b93d0e36c0a86c3d15310fe7383321ca63aeb04d


Reply via email to