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