-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Clang][Sema] Defer instantiation of exception specification until af…
…ter partial ordering when determining primary template (#82417) Consider the following: ``` struct A { static constexpr bool x = true; }; template<typename T, typename U> void f(T, U) noexcept(T::y); // #1, error: no member named 'y' in 'A' template<typename T, typename U> void f(T, U*) noexcept(T::x); // #2 template<> void f(A, int*) noexcept; // explicit specialization of #2 ``` We currently instantiate the exception specification of all candidate function template specializations when deducting template arguments for an explicit specialization, which results in a error despite `#1` not being selected by partial ordering as the most specialized template. According to [except.spec] p13: > An exception specification is considered to be needed when: > - [...] > - the exception specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function); Assuming that "comparing declarations" means "determining whether the declarations correspond and declare the same entity" (per [basic.scope.scope] p4 and [basic.link] p11.1, respectively), the exception specification does _not_ need to be instantiated until _after_ partial ordering, at which point we determine whether the implicitly instantiated specialization and the explicit specialization declare the same entity (the determination of whether two functions/function templates correspond does not consider the exception specifications). This patch defers the instantiation of the exception specification until a single function template specialization is selected via partial ordering, matching the behavior of GCC, EDG, and MSVC: see https://godbolt.org/z/Ebb6GTcWE.
- Loading branch information
1 parent
83feb84
commit 9cfb138
Showing
5 changed files
with
117 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s | ||
|
||
struct A { | ||
static constexpr bool x = true; | ||
}; | ||
|
||
namespace N0 { | ||
|
||
template<typename T, typename U> | ||
void f(T, U) noexcept(T::y); // #1 | ||
|
||
template<typename T, typename U> // #2 | ||
void f(T, U*) noexcept(T::x); | ||
|
||
// Deduction should succeed for both candidates, and #2 should be selected as the primary template. | ||
// Only the exception specification of #2 should be instantiated. | ||
template<> | ||
void f(A, int*) noexcept; | ||
|
||
} | ||
|
||
namespace N1 { | ||
|
||
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 | ||
// expected-error@-1 {{no member named 'y' in 'A'}} | ||
|
||
// Deduction should succeed for both candidates, and #2 should be selected as the primary template. | ||
// Only the exception specification of #2 should be instantiated. | ||
template<> | ||
void f(A, int*) noexcept; // expected-error {{exception specification in declaration does not match previous declaration}} | ||
// expected-note@-1 {{in instantiation of exception specification for 'f<A, int>' requested here}} | ||
// expected-note@-2 {{previous declaration is here}} | ||
} | ||
|
||
namespace N2 { | ||
|
||
template<typename T, typename U> | ||
void f(T, U) noexcept(T::x); | ||
|
||
template<typename T, typename U> | ||
void f(T, U*) noexcept(T::x); | ||
|
||
template<typename T, typename U> | ||
void f(T, U**) noexcept(T::y); // expected-error {{no member named 'y' in 'A'}} | ||
|
||
template<typename T, typename U> | ||
void f(T, U***) noexcept(T::x); | ||
|
||
template<> | ||
void f(A, int*) noexcept; // expected-note {{previous declaration is here}} | ||
|
||
template<> | ||
void f(A, int*); // expected-error {{'f<A, int>' is missing exception specification 'noexcept'}} | ||
|
||
template<> | ||
void f(A, int**) noexcept; // expected-error {{exception specification in declaration does not match previous declaration}} | ||
// expected-note@-1 {{in instantiation of exception specification for 'f<A, int>' requested here}} | ||
// expected-note@-2 {{previous declaration is here}} | ||
|
||
// FIXME: Exception specification is currently set to EST_None if instantiation fails. | ||
template<> | ||
void f(A, int**); | ||
|
||
template<> | ||
void f(A, int***) noexcept; // expected-note {{previous declaration is here}} | ||
|
||
template<> | ||
void f(A, int***); // expected-error {{'f<A, int>' is missing exception specification 'noexcept'}} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters