On 1/21/21 10:39 AM, Marek Polacek wrote:
On Thu, Jan 21, 2021 at 01:55:24AM -0500, Jason Merrill wrote:
+ /* 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.
Fair enough, here's an updated version.
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..1f3850d1048 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)
{
@@ -12203,6 +12206,11 @@ instantiate_class_template_1 (tree type)
finish_struct_1 (type);
TYPE_BEING_DEFINED (type) = 0;
+ /* Now that we've gone through all the members, instantiate those
+ marked with attribute used. */
+ for (tree x : used)
+ mark_used (x);
Even farther down, I think, since access checks are part of the class
instantiation, and I don't think doing it before all the popping gains
us anything (though I'd be interested in knowing if this is wrong).
Let's move it just before the return. OK with that change.
/* We don't instantiate default arguments for member functions. 14.7.1:
The implicit instantiation of a class template specialization causes
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: 43705f3fa343e08b2fb030460fc5e2a969954398