|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +nav-class: dark |
| 4 | +categories: krystian |
| 5 | +title: Krystian's Q4 2024 Update |
| 6 | +author-id: krystian |
| 7 | +--- |
| 8 | + |
| 9 | +# Clang |
| 10 | + |
| 11 | +This quarter, I continued working on the *fourth* iteration of my [refactoring of multi-level template argument list collection](https://github.com/llvm/llvm-project/pull/111852) ([#112381](https://github.com/llvm/llvm-project/pull/112381), [#114258](https://github.com/llvm/llvm-project/pull/114258) and [#114569](https://github.com/llvm/llvm-project/pull/114569) related), fixed support for the `@relates` command, made improvements to C++ conformance, amongst other things. |
| 12 | + |
| 13 | +## Multi-level template argument list collection (again) |
| 14 | + |
| 15 | +[My initial patch](https://github.com/llvm/llvm-project/pull/106585) that refactored multi-level template argument list collection proved to have some nasty bugs relating to instantiation order. The following is a reduction of a reported regression when compiling QT: |
| 16 | + |
| 17 | +```cpp |
| 18 | +template<int N> |
| 19 | +struct A |
| 20 | +{ |
| 21 | + template<typename T> |
| 22 | + static constexpr bool f(); |
| 23 | +}; |
| 24 | + |
| 25 | +template<> |
| 26 | +template<typename T> |
| 27 | +constexpr bool A<0>::f() |
| 28 | +{ |
| 29 | + return A<1>::f<T>(); // note: undefined function 'f<int>' cannot be used in a constant expression |
| 30 | +} |
| 31 | + |
| 32 | +template<> |
| 33 | +template<typename T> |
| 34 | +constexpr bool A<1>::f() |
| 35 | +{ |
| 36 | + return true; |
| 37 | +} |
| 38 | + |
| 39 | +static_assert(A<0>::f<int>()); // error: static assertion expression is not an integral constant expression |
| 40 | +``` |
| 41 | +
|
| 42 | +I initially thought Clang was correct to complain here, since the member specialization `A<1>::f` does not precede its first use (lexically), ergo IFNDR per [[temp.expl.spec] p7](http://eel.is/c++draft/temp.expl.spec#7): |
| 43 | +> If a template, a member template or a member of a class template is explicitly specialized, a declaration of that specialization shall be reachable from every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. |
| 44 | +
|
| 45 | +However, the declaration of the member specialization is reachable from a point in the *instantiation context* of the definition of `A<0>::f<int>` (that being, immediately prior to the _static_assert-declaration_), so this example is indeed valid. |
| 46 | +
|
| 47 | +## Explicit specialization of members of partial specializations |
| 48 | +
|
| 49 | +On the C++ conformance side of things, I landed a [patch](https://github.com/llvm/llvm-project/pull/113464) implementing [[temp.spec.partial.member] p2](http://eel.is/c++draft/temp.spec.partial.member#2): |
| 50 | +> If the primary member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the partial specializations of the member template are ignored for this specialization of the enclosing class template. |
| 51 | +If a partial specialization of the member template is explicitly specialized for a given (implicit) specialization of the enclosing class template, the primary member template and its other partial specializations are still considered for this specialization of the enclosing class template. |
| 52 | +
|
| 53 | +Its meaning can be illustrated via the following: |
| 54 | +```cpp |
| 55 | +template<typename T> |
| 56 | +struct A |
| 57 | +{ |
| 58 | + template<typename U> |
| 59 | + struct B |
| 60 | + { |
| 61 | + static constexpr int x = 0; // #1 |
| 62 | + }; |
| 63 | +
|
| 64 | + template<typename U> |
| 65 | + struct B<U*> |
| 66 | + { |
| 67 | + static constexpr int x = 1; // #2 |
| 68 | + }; |
| 69 | +}; |
| 70 | +
|
| 71 | +template<> |
| 72 | +template<typename U> |
| 73 | +struct A<long>::B |
| 74 | +{ |
| 75 | + static constexpr int x = 2; // #3 |
| 76 | +}; |
| 77 | +
|
| 78 | +static_assert(A<short>::B<int>::y == 0); // uses #1 |
| 79 | +static_assert(A<short>::B<int*>::y == 1); // uses #2 |
| 80 | +
|
| 81 | +static_assert(A<long>::B<int>::y == 2); // uses #3 |
| 82 | +static_assert(A<long>::B<int*>::y == 2); // uses #3 |
| 83 | +``` |
| 84 | + |
| 85 | +Since the primary member template `A<long>::B` is explicitly specialized for a given (implicit) specialization of its enclosing class template, the partial specialization `B<U*>` will be ignored when instantiating a specialization of `B`. |
0 commit comments