[clang][Sema] Fix a CTAD regression after 42239d2e9 (#86914)

The most recent declaration of a template as a friend can introduce a
different template parameter depth compared to what we anticipate from a
CTAD guide.

Fixes https://github.com/llvm/llvm-project/issues/86769
This commit is contained in:
Younan Zhang 2024-03-29 23:28:54 +08:00 committed by Tom Stellard
parent e0f0c463b5
commit 76c721994a
4 changed files with 52 additions and 2 deletions

View File

@ -1106,6 +1106,10 @@ Bug Fixes to C++ Support
- Fix a crash when an unresolved overload set is encountered on the RHS of a ``.*`` operator.
(`#53815 <https://github.com/llvm/llvm-project/issues/53815>`_)
- Fixed a regression in CTAD that a friend declaration that befriends itself may cause
incorrect constraint substitution.
(`#86769 <https://github.com/llvm/llvm-project/issues/86769>`_)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed an import failure of recursive friend class template.

View File

@ -1830,7 +1830,27 @@ static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) {
// Make sure we get the template parameter list from the most
// recent declaration, since that is the only one that is guaranteed to
// have all the default template argument information.
return cast<TemplateDecl>(TD->getMostRecentDecl())->getTemplateParameters();
Decl *D = TD->getMostRecentDecl();
// C++11 [temp.param]p12:
// A default template argument shall not be specified in a friend class
// template declaration.
//
// Skip past friend *declarations* because they are not supposed to contain
// default template arguments. Moreover, these declarations may introduce
// template parameters living in different template depths than the
// corresponding template parameters in TD, causing unmatched constraint
// substitution.
//
// FIXME: Diagnose such cases within a class template:
// template <class T>
// struct S {
// template <class = void> friend struct C;
// };
// template struct S<int>;
while (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None &&
D->getPreviousDecl())
D = D->getPreviousDecl();
return cast<TemplateDecl>(D)->getTemplateParameters();
}
DeclResult Sema::CheckClassTemplate(

View File

@ -478,3 +478,29 @@ template <Concept> class Foo {
};
} // namespace FriendOfFriend
namespace GH86769 {
template <typename T>
concept X = true;
template <X T> struct Y {
Y(T) {}
template <X U> friend struct Y;
template <X U> friend struct Y;
template <X U> friend struct Y;
};
template <class T>
struct Z {
// FIXME: This is ill-formed per C++11 [temp.param]p12:
// A default template argument shall not be specified in a friend class
// template declaration.
template <X U = void> friend struct Y;
};
template struct Y<int>;
template struct Z<int>;
Y y(1);
}

View File

@ -53,4 +53,4 @@ X x;
template<class T, class B> struct Y { Y(T); };
template<class T, class B=void> struct Y ;
Y y(1);
};
}