On 7/29/26 3:40 PM, Patrick Palka wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk/16?
-- >8 --
In r16-5967-gbae0ed69e1862a we removed the mark_used logic from
resolve_nondeduced_context under the rationale that it should be the
callers' responsiblity to mark it. Resolving a template-id overload
shouldn't constitute an ODR-used on its own,
Hmm, https://eel.is/c++draft/basic#def.odr-4.1 (plus p8) seems to say it
does, as it's the selected member of an overload set in a
potentially-evaluated context.
For r16-5967, I don't see why the patch made a difference to the
testcase; in convert_to_void resolve_nondeduced_function is followed by
mark_single_function, so we should return error_mark_node regardless of
where we first call mark_used.
and callers would have to
call mark_used anyway to uniformly handle all overloads, including
non-template-id ones.
Yes, so r16-5967 is consistent with the comment to mark_single_function.
Removing this logic however now means that resolve_nondeduced_context
could return a specialization whose type is not yet fully resolved
(i.e. has an uninstantiated noexcept or undeduced return type), and
callers that immediately inspect TREE_TYPE of the result (such as
standard_conversion and build_conditional_expr) now misbehave.
I think the important case is standard_conversion; at that point we
likely haven't finished overload resolution, so the selection is still
tentative and so odr-use would be premature. Though that's why we have
tf_conv. But then, it looks like mark_used wrongly misses resolving
auto if tf_conv.
In build_conditional_expr it looks like we no longer have anything that
will mark_used the resolved operand, i.e. we're missing a call to
mark_single_function. Does this lead to missed instantiation if the
return type isn't deduced?
The function comment for resolve_nondeduced_context should mention that
most uses also want mark_single_function.
We could adjust callers to call mark_used before inspecting TREE_TYPE,
but I think it's reasonable to expect that the (successful) result of
resolve_nondeduced_context always has a resolved function type. To
that end this patch restores the mark_used call, but ensures it's
always done in an unevaluated context and quietly so that its only
effect is to resolve the function type. (Note we do this same technique
in the Reflection implementation, see resolve_type_of_reflected_decl).
PR c++/126406
PR c++/119343
gcc/cp/ChangeLog:
* pt.cc (resolve_nondeduced_context): Restore mark_used call
but always do it quietly and in an unevaluated context.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/cond2a.C: New test.
* g++.dg/cpp1y/auto-fn67.C: New test.
* g++.dg/cpp1z/noexcept-type29.C: New test.
---
gcc/cp/pt.cc | 5 +++++
gcc/testsuite/g++.dg/cpp0x/cond2a.C | 17 +++++++++++++++++
gcc/testsuite/g++.dg/cpp1y/auto-fn67.C | 11 +++++++++++
gcc/testsuite/g++.dg/cpp1z/noexcept-type29.C | 11 +++++++++++
4 files changed, 44 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/cond2a.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/auto-fn67.C
create mode 100644 gcc/testsuite/g++.dg/cpp1z/noexcept-type29.C
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 6a081b838e06..3605c2ea8b93 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -25653,6 +25653,11 @@ resolve_nondeduced_context (tree orig_expr,
tsubst_flags_t complain)
}
if (good == 1)
{
+ /* Ensure the selected function's type is fully resolved. */
+ ++cp_unevaluated_operand;
+ mark_used (goodfn, tf_none);
+ --cp_unevaluated_operand;
+
expr = goodfn;
if (baselink)
expr = build_baselink (BASELINK_BINFO (baselink),
diff --git a/gcc/testsuite/g++.dg/cpp0x/cond2a.C
b/gcc/testsuite/g++.dg/cpp0x/cond2a.C
new file mode 100644
index 000000000000..a42e198291b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/cond2a.C
@@ -0,0 +1,17 @@
+// PR c++/126406
+// { dg-do compile { target c++14 } }
+// A version of cond2.C where f has a deduced return type
+// and g is instantiated.
+
+bool b;
+
+template < class T > auto f ()
+{
+}
+
+template < class T > auto g () -> decltype (b ? f < int > : throw 0)
+{
+ return b ? f<int> : throw 0;
+}
+
+using type = decltype(g<int>());
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn67.C
b/gcc/testsuite/g++.dg/cpp1y/auto-fn67.C
new file mode 100644
index 000000000000..20ff86459c3d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn67.C
@@ -0,0 +1,11 @@
+// PR c++/126406
+// { dg-do compile { target c++14 } }
+
+template<class T> auto g(T) { }
+static_assert(g<int>);
+
+template<class T>
+struct B {
+ static auto g(T) { }
+};
+static_assert(B<int>::g);
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type29.C
b/gcc/testsuite/g++.dg/cpp1z/noexcept-type29.C
new file mode 100644
index 000000000000..c620b180fc83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type29.C
@@ -0,0 +1,11 @@
+// PR c++/126406
+// { dg-do compile { target c++11 } }
+
+template<class T> void f(T) noexcept(noexcept(T())) { }
+static_assert(f<int>);
+
+template<class T>
+struct A {
+ static void f(T) noexcept(noexcept(T())) { }
+};
+static_assert(A<int>::f);