forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Clang][Sema] Improve support for explicit specializations of constra…
…ined member functions & member function templates (llvm#88963) Consider the following snippet from the discussion of CWG2847 on the core reflector: ``` template<typename T> concept C = sizeof(T) <= sizeof(long); template<typename T> struct A { template<typename U> void f(U) requires C<U>; // #1, declares a function template void g() requires C<T>; // #2, declares a function template<> void f(char); // #3, an explicit specialization of a function template that declares a function }; template<> template<typename U> void A<short>::f(U) requires C<U>; // llvm#4, an explicit specialization of a function template that declares a function template template<> template<> void A<int>::f(int); // llvm#5, an explicit specialization of a function template that declares a function template<> void A<long>::g(); // llvm#6, an explicit specialization of a function that declares a function ``` A number of problems exist: - Clang rejects `llvm#4` because the trailing _requires-clause_ has `U` substituted with the wrong template parameter depth when `Sema::AreConstraintExpressionsEqual` is called to determine whether it matches the trailing _requires-clause_ of the implicitly instantiated function template. - Clang rejects `llvm#5` because the function template specialization instantiated from `A<int>::f` has a trailing _requires-clause_, but `llvm#5` does not (nor can it have one as it isn't a templated function). - Clang rejects `llvm#6` for the same reasons it rejects `llvm#5`. This patch resolves these issues by making the following changes: - To fix `llvm#4`, `Sema::AreConstraintExpressionsEqual` is passed `FunctionTemplateDecl`s when comparing the trailing _requires-clauses_ of `llvm#4` and the function template instantiated from `#1`. - To fix `llvm#5` and `llvm#6`, the trailing _requires-clauses_ are not compared for explicit specializations that declare functions. In addition to these changes, `CheckMemberSpecialization` now considers constraint satisfaction/constraint partial ordering when determining which member function is specialized by an explicit specialization of a member function for an implicit instantiation of a class template (we previously would select the first function that has the same type as the explicit specialization). With constraints taken under consideration, we match EDG's behavior for these declarations.
- Loading branch information
1 parent
83f3b1c
commit 34ae226
Showing
10 changed files
with
248 additions
and
68 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
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
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,60 @@ | ||
// RUN: %clang_cc1 -std=c++20 -verify %s | ||
|
||
template<int I> | ||
concept C = I >= 4; | ||
|
||
template<int I> | ||
concept D = I < 8; | ||
|
||
template<int I> | ||
struct A { | ||
constexpr static int f() { return 0; } | ||
constexpr static int f() requires C<I> && D<I> { return 1; } | ||
constexpr static int f() requires C<I> { return 2; } | ||
|
||
constexpr static int g() requires C<I> { return 0; } // #candidate-0 | ||
constexpr static int g() requires D<I> { return 1; } // #candidate-1 | ||
|
||
constexpr static int h() requires C<I> { return 0; } // expected-note {{member declaration nearly matches}} | ||
}; | ||
|
||
template<> | ||
constexpr int A<2>::f() { return 3; } | ||
|
||
template<> | ||
constexpr int A<4>::f() { return 4; } | ||
|
||
template<> | ||
constexpr int A<8>::f() { return 5; } | ||
|
||
static_assert(A<3>::f() == 0); | ||
static_assert(A<5>::f() == 1); | ||
static_assert(A<9>::f() == 2); | ||
static_assert(A<2>::f() == 3); | ||
static_assert(A<4>::f() == 4); | ||
static_assert(A<8>::f() == 5); | ||
|
||
template<> | ||
constexpr int A<0>::g() { return 2; } | ||
|
||
template<> | ||
constexpr int A<8>::g() { return 3; } | ||
|
||
template<> | ||
constexpr int A<6>::g() { return 4; } // expected-error {{ambiguous member function specialization 'A<6>::g' of 'A::g'}} | ||
// expected-note@#candidate-0 {{member function specialization matches 'g'}} | ||
// expected-note@#candidate-1 {{member function specialization matches 'g'}} | ||
|
||
static_assert(A<9>::g() == 0); | ||
static_assert(A<1>::g() == 1); | ||
static_assert(A<0>::g() == 2); | ||
static_assert(A<8>::g() == 3); | ||
|
||
template<> | ||
constexpr int A<4>::h() { return 1; } | ||
|
||
template<> | ||
constexpr int A<0>::h() { return 2; } // expected-error {{out-of-line definition of 'h' does not match any declaration in 'A<0>'}} | ||
|
||
static_assert(A<5>::h() == 0); | ||
static_assert(A<4>::h() == 1); |
Oops, something went wrong.