Re: [PATCH] c++: resolvedness of resolve_nondeduced_context result [PR126406]
Jason Merrill <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
On 7/30/26 10:31 AM, Patrick Palka wrote:
> On Thu, 30 Jul 2026, Jason Merrill wrote:
>
>> 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.
>
> I see, so resolve_nondeduced_context should call mark_used after all?
> And should it also call mark_used on non-template-id overloads?
>
>> 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.
>
> The problem is that convert_to_void didn't propagate error_mark_node
> result from mark_used, instead it just returned void_node. So we could
> have more simply fixed that PR by just propagating error_mark_node
> but I was under the impression that resolve_nondeduced_function shouldn't
> call mark_used at all which incidentally fixes the PR at a higher level
> (resolve_nondeduced_function can no longer return error_mark_node).
>
>>> 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.
>
> But I thought the mentioned [basic.def.odr]/4.1 and /8 suggests
> resolve_nondeduced_function could safely call mark_single_function?
Yes, but the comment to mark_single_function suggests otherwise, and the
standard_conversion case seems relevant.
Certainly the state before r16-5967 was inconsistent between template
and non-template cases, and perhaps unconditionally calling
mark_single_function would help to clarify the cases we need to handle
differently.
I think maybe we used to mark single non-template functions immediately
in finish_id_expression, but stopped to avoid duplicate diagnostics?
>>> 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.
>
> Yeah, I'm not immediately sure whether tf_conv users expected it resolve
> auto and instaniate noexcept etc.
tf_conv is trying to make sense of a single candidate, which it can't do
in general without resolving auto.
It shouldn't need to instantiate noexcept unless to match function
pointer types.
>> 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?
>
> It causes us to reject the newly added cond2a with:
>
> cond2a.C:17:29: error: use of ‘decltype ((b ? f<int> : <throw-expression>)) g() [with T = int; decltype ((b ? f<int> : <throw-expression>)) = void (&)()]’ before deduction of ‘auto’
>
> And for
>
> bool b;
>
> template < class T > void f ()
> {
> __builtin_printf("Hello World!\n");
> }
>
> template < class T > void g ()
> {
> auto p = !b ? f<int> : throw 0;
> p();
> }
>
> int main() {
> g<int>();
> }
>
> it seems we eventually call mark_single_function on f<int> from
> cp_build_addr_expr_1, so no missed instantiation in that case at least.
OK, good. We might add a comment about that after the resolve_.
I guess that also affects the 'addr' case in resolve_nondeduced_context.
>> The function comment for resolve_nondeduced_context should mention that most
>> uses also want mark_single_function.
>
> They should _eventually_ call mark_single_function, not necessarily
> immediately after, right?
Right.
> For sake of backporting we should we then just revert r16-5967 and
> instead check error_mark_node result from mark_single_function
> in convert_to_void?
Yes, that sounds good for 16. But it looks like we already do?
Jason