The testcase from this PR fails to link when -fchecking=2 with the error:

  /usr/bin/ld: /tmp/ccpHiXEY.o: in function `bar<int>::bar()':
  ...: undefined reference to `foo<int>::foo()'

ultimately because we end up instantiating the NSDMI for bar<int>::alloc_
from the template context func1<T> for which in_template_function is
true and thus mark_used is inhibited from scheduling other templates for
instantiation.  So we end up never instantiating foo<int>'s ctor.

Although maybe_instantiate_nsdmi_init does call push_to_top_level, which
would have gotten us out of the template context, it doesn't happen in
this case because currently_open_class (ctx) is true, thanks to an
earlier call to push_scope from synthesized_method_walk.

We could perhaps arrange to call push_to_top_level unconditionally in
maybe_instantiate_nsdmi_init or even from from synthesized_method_walk,
but that seems rather heavyweight for this situation.  Ideally we just
want a way to allow mark_used to work here despite being in a template
context.

To that end, this patch first generalizes the in_template_function test
in mark_used to instead test current_template_parms, which has two
benefits: it works for all template contexts, not just function template
contexts, and it can be cheaply disabled by simply clearing
current_template_parms.  This patch then makes us disable this test from
maybe_instantiate_nsdmi_init.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?  This doesn't seem worth backporting since the bug seems to
manifest only with -fchecking=2.

        PR c++/109506

gcc/cp/ChangeLog:

        * decl2.cc (mark_used): Check current_template_parms instead
        of in_template_function.
        * init.cc (maybe_instantiate_nsdmi_init): Clear
        current_template_parms before instantiating.

gcc/testsuite/ChangeLog:

        * g++.dg/warn/Waddress-of-packed-member2.C: No longer expect
        a "used but never defined" warning due to the use from an
        uninstantiated template.
        * g++.dg/template/non-dependent25.C: New test.
---
 gcc/cp/decl2.cc                               |  6 ++++-
 gcc/cp/init.cc                                |  3 +++
 gcc/testsuite/g++.dg/cpp0x/nsdmi-template26.C | 23 +++++++++++++++++++
 .../g++.dg/warn/Waddress-of-packed-member2.C  |  2 +-
 4 files changed, 32 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/nsdmi-template26.C

diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 9594be4092c..b9d37d76bf6 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -5781,7 +5781,11 @@ mark_used (tree decl, tsubst_flags_t complain /* = 
tf_warning_or_error */)
          && DECL_OMP_DECLARE_REDUCTION_P (decl)))
     maybe_instantiate_decl (decl);
 
-  if (processing_template_decl || in_template_function ())
+  /* We don't want to instantiate templates based on uses from other
+     uninstantiated templates.  Since processing_template_decl is cleared
+     during instantiate_non_dependent_expr, we check current_template_parms
+     as well.  */
+  if (processing_template_decl || current_template_parms)
     return true;
 
   /* Check this too in case we're within instantiate_non_dependent_expr.  */
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index 1dd24e30d7c..ef32ef2a8c2 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -610,6 +610,9 @@ maybe_instantiate_nsdmi_init (tree member, tsubst_flags_t 
complain)
              push_deferring_access_checks (dk_no_deferred);
              pushed = true;
            }
+         /* Make sure current_template_parms is cleared so that mark_used
+            is uninhibited.  */
+         auto ctpo = make_temp_override (current_template_parms, NULL_TREE);
 
          /* If we didn't push_to_top_level, still step out of constructor
             scope so build_base_path doesn't try to use its __in_chrg.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-template26.C 
b/gcc/testsuite/g++.dg/cpp0x/nsdmi-template26.C
new file mode 100644
index 00000000000..d9e17ea6724
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-template26.C
@@ -0,0 +1,23 @@
+// PR c++/109506
+// { dg-do link { target c++11 } }
+// { dg-additional-options "-fchecking=2" }
+
+template<class T>
+struct foo {
+  foo() { };
+};
+
+template<class T>
+class bar {
+  foo<int> alloc_{};
+};
+
+template<class T>
+bar<int> func1() {
+  return bar<int>{};
+}
+
+int main() {
+  func1<int>();
+}
+
diff --git a/gcc/testsuite/g++.dg/warn/Waddress-of-packed-member2.C 
b/gcc/testsuite/g++.dg/warn/Waddress-of-packed-member2.C
index e9bf7cac04c..d619b28cfe1 100644
--- a/gcc/testsuite/g++.dg/warn/Waddress-of-packed-member2.C
+++ b/gcc/testsuite/g++.dg/warn/Waddress-of-packed-member2.C
@@ -1,7 +1,7 @@
 // PR c++/89973
 // { dg-do compile { target c++14 } }
 
-constexpr int a(); // { dg-warning "used but never defined" }
+constexpr int a();
 
 template <typename>
 constexpr void *b = a(); // { dg-error "invalid conversion" }
-- 
2.40.1.423.g2807bd2c10

Reply via email to