Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CWG2896 [temp.deduct] Deducing template arguments from function declarations with noexcept-specifiers #537

Open
sdkrystian opened this issue May 15, 2024 · 6 comments

Comments

@sdkrystian
Copy link

Full name of submitter: Krystian Stasiowski

Reference (section label): [temp.deduct]

Link to reflector thread (if any): N/A

Issue description:

According to [temp.deduct.decl] p1:

In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations, explicit specializations, and certain friend declarations. This is also done to determine whether a deallocation function template specialization matches a placement operator new. In all these cases, P is the type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in [expr.new]. The deduction is done as described in [temp.deduct.type].

Consider the following:

template<bool B>
void f() noexcept(B);

template<>
void f() noexcept;

Despite the resolution of CWG2355, the explicit specialization of f is rejected by Clang, GCC, EDG, and MSVC because a template argument for B cannot be deduced from the noexcept-specifier. This approach is arguably correct, as it avoids instantiating the exception specification for all candidate templates (which could render the program ill-formed as the noexcept-specifier excluded from the deduction substitution loci). Moreover, deduction from the noexcept-specifier may not be immediately possible because it hasn't been parsed yet (e.g. for a class scope explicit specialization). The wording should be changed to reflect existing implementation practice.

Excluding the noexcept-specifier from deduction when deducing template arguments from a function declaration also necessitates its exclusion from deduction during partial ordering in such contexts, as template parameters only used in the noexcept-specifer would otherwise cause template argument deduction to always fail.

Suggested resolution:

Change [temp.deduct.decl] p1 as follows:

[...] In all these cases, P is the function type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in [expr.new]. Any noexcept-specifier is ignored when determining the types of P and A. The deduction is done as described in [temp.deduct.type].

Change [temp.deduct.partial] p3 as follows:

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • In the context of a function call, the types used are those function parameter types for which the function call has arguments.
  • In the context of a call to a conversion function, the return types of the conversion function templates are used.
  • In other contexts the function template's function type outside of the noexcept-specifier is used.
@sdkrystian
Copy link
Author

The noexcept-specifier should also be ignored when deducing template arguments taking the address of a function template, but I'm not sure whether it should be part of this issue.

sdkrystian added a commit to llvm/llvm-project that referenced this issue May 15, 2024
…ication as used during partial ordering (#91534)

We do not deduce template arguments from the exception specification
when determining the primary template of a function template
specialization or when taking the address of a function template.
Therefore, this patch changes `isAtLeastAsSpecializedAs` such that we do
not mark template parameters in the exception specification as 'used'
during partial ordering (per [temp.deduct.partial]
p12) to prevent the following from being ambiguous:

```
template<typename T, typename U>
void f(U) noexcept(noexcept(T())); // #1

template<typename T>
void f(T*) noexcept; // #2

template<>
void f<int>(int*) noexcept; // currently ambiguous, selects #2 with this patch applied 
```

Although there is no corresponding wording in the standard (see core issue filed here
cplusplus/CWG#537), this seems
to be the intended behavior given the definition of _deduction
substitution loci_ in [temp.deduct.general] p7 (and EDG does the same thing).
@jensmaurer
Copy link
Member

CWG2896

@jensmaurer jensmaurer changed the title [temp.deduct] Deducing template arguments from function declarations with noexcept-specifiers CWG2896 [temp.deduct] Deducing template arguments from function declarations with noexcept-specifiers May 31, 2024
@jensmaurer
Copy link
Member

It is not clear to me why the address-of-function-template case also needs to ignore the exception specification.

Also, for the two carve-outs here, do they apply only to a noexcept-specifier on the top level of a function declaration, or also to noexcept-specifiers on function parameters of (e.g.) function pointer type? "Any noexcept-specifier is ignored" sounds very much like the latter, but it seems the former is intended.

@sdkrystian
Copy link
Author

Minor grammar nit:

[...] we also need to avoid consider the exception specification [...]

"consider" should be "considering".

@sdkrystian
Copy link
Author

sdkrystian commented May 31, 2024

It is not clear to me why the address-of-function-template case also needs to ignore the exception specification.

The phrasing of [except.spec] p13.1 seems to suggest that the exception specification is instantiated only for the (unique) function selected by overload resolution. The address-of-function-template case requires deduction be performed for the candidate templates, which can result in errors outside the immediate context, e.g.:

struct A 
{ 
    static constexpr bool x = true;
};

template<typename T, typename U>
void f(T, U*) noexcept(T::x); // #1

template<typename T, typename U>
void f(T, U) noexcept(T::y); // #2

void(&g)(A, int*) noexcept = f;

Excluding the noexcept-specifier from deduction and partial ordering in this case results in only the exception specification of #1 being instantiated.

Also, for the two carve-outs here, do they apply only to a noexcept-specifier on the top level of a function declaration, or also to noexcept-specifiers on function parameters of (e.g.) function pointer type? "Any noexcept-specifier is ignored" sounds very much like the latter, but it seems the former is intended.

Yes, they only apply to a noexcept-specifier on the top level of a function declaration.

@jensmaurer
Copy link
Member

Fixed "considering" and clarified top-level noexcept-specifier.

Amended for address-of-function-template case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants