[clang-tools-extra] 0b67243 - [Clang] Support friend declarations with a dependent nested-name-specifier (#208345)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 16 00:46:38 PDT 2026
Author: Oleksandr Tarasiuk
Date: 2026-08-16T10:46:32+03:00
New Revision: 0b672437c396b700fdb94bd64b176b33f084053b
URL: https://github.com/llvm/llvm-project/commit/0b672437c396b700fdb94bd64b176b33f084053b
DIFF: https://github.com/llvm/llvm-project/commit/0b672437c396b700fdb94bd64b176b33f084053b.diff
LOG: [Clang] Support friend declarations with a dependent nested-name-specifier (#208345)
Fixes https://github.com/llvm/llvm-project/issues/104057
---
This patch adds support for friend declarations with a dependent NNS
Added:
clang/test/AST/friend-template-decl.cpp
clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
clang/test/Index/load-dependent-friend-template.cpp
clang/test/SemaTemplate/dependent-friend-constraints.cpp
Modified:
clang-tools-extra/clang-doc/Serialize.cpp
clang/docs/ReleaseNotes.md
clang/include/clang/AST/ASTNodeTraverser.h
clang/include/clang/AST/DeclFriend.h
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/AST/JSONNodeDumper.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/AST/TextNodeDumper.h
clang/include/clang/Basic/DeclNodes.td
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/Template.h
clang/include/clang/Sema/TemplateDeduction.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/ASTStructuralEquivalence.cpp
clang/lib/AST/DeclFriend.cpp
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/AST/JSONNodeDumper.cpp
clang/lib/AST/ODRHash.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Index/IndexDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaAccess.cpp
clang/lib/Sema/SemaCodeComplete.cpp
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExceptionSpec.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateDeductionGuide.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
clang/test/CXX/drs/cwg18xx.cpp
clang/test/CXX/drs/cwg19xx.cpp
clang/test/CXX/drs/cwg28xx.cpp
clang/test/CXX/drs/cwg6xx.cpp
clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
clang/test/Modules/odr_hash.cpp
clang/test/Parser/cxx2c-variadic-friends.cpp
clang/test/SemaCXX/cxx2c-variadic-friends.cpp
clang/test/SemaCXX/many-template-parameter-lists.cpp
clang/test/SemaTemplate/GH71595.cpp
clang/test/SemaTemplate/concepts-friends.cpp
clang/test/SemaTemplate/ctad.cpp
clang/test/SemaTemplate/friend-template.cpp
clang/test/SemaTemplate/nested-template.cpp
clang/tools/libclang/CIndex.cpp
clang/tools/libclang/CursorVisitor.h
clang/unittests/AST/DeclTest.cpp
clang/unittests/AST/StructuralEquivalenceTest.cpp
clang/www/cxx_dr_status.html
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 50118e0472075..5fa23416949c4 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -1029,9 +1029,6 @@ void Serializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
llvm::SmallVector<FriendInfo, 4> LocalFriends;
for (const FriendDecl *FD : D->friends()) {
- if (FD->isUnsupportedFriend())
- continue;
-
FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
const auto *ActualDecl = FD->getFriendDecl();
if (!ActualDecl) {
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 70ea2e35090c6..59ebcc4444b28 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -123,6 +123,7 @@ features cannot lower the translation-unit ABI level;
## What's New in Clang {{env.config.release}}?
### C++ Language Changes
+- Clang now supports friend declarations with a dependent nested name specifier. (#GH104057)
#### C++2d Feature Support
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index a8a73c5b72d33..e00c3dd537b0d 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -791,6 +791,14 @@ class ASTNodeTraverser
}
}
+ void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ for (const TemplateParameterList *TPL : D->getTemplateParameterLists())
+ dumpTemplateParameters(TPL);
+ if (D->getFriendKind() !=
+ FriendTemplateDecl::FriendTemplateEntityKind::Template)
+ VisitFriendDecl(D);
+ }
+
void VisitObjCMethodDecl(const ObjCMethodDecl *D) {
if (D->isThisDeclarationADefinition())
dumpDeclContext(D);
diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 1f8c210263677..6d2659337a9c8 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,18 +15,12 @@
#define LLVM_CLANG_AST_DECLFRIEND_H
#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/ExternalASTSource.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/LLVM.h"
-#include "clang/Basic/SourceLocation.h"
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
-#include "llvm/Support/TrailingObjects.h"
#include <cassert>
#include <iterator>
@@ -49,9 +43,7 @@ class ASTContext;
/// @endcode
///
/// The semantic context of a friend decl is its declaring class.
-class FriendDecl final
- : public Decl,
- private llvm::TrailingObjects<FriendDecl, TemplateParameterList *> {
+class FriendDecl : public Decl {
LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION();
public:
@@ -61,46 +53,28 @@ class FriendDecl final
friend class CXXRecordDecl;
friend class CXXRecordDecl::friend_iterator;
+ // Location of the '...', if present.
+ SourceLocation EllipsisLoc;
+
+ SourceLocation FriendLoc;
+
+protected:
// The declaration that's a friend of this class.
FriendUnion Friend;
- // A pointer to the next friend in the sequence.
LazyDeclPtr NextFriend;
- // Location of the 'friend' specifier.
- SourceLocation FriendLoc;
-
- // Location of the '...', if present.
- SourceLocation EllipsisLoc;
-
- /// True if this 'friend' declaration is unsupported. Eventually we
- /// will support every possible friend declaration, but for now we
- /// silently ignore some and set this flag to authorize all access.
- LLVM_PREFERRED_TYPE(bool)
- unsigned UnsupportedFriend : 1;
-
- // The number of "outer" template parameter lists in non-templatic
- // (currently unsupported) friend type declarations, such as
- // template <class T> friend class A<T>::B;
- unsigned NumTPLists : 31;
-
- FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
- SourceLocation FriendL, SourceLocation EllipsisLoc,
- ArrayRef<TemplateParameterList *> FriendTypeTPLists)
- : Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
- EllipsisLoc(EllipsisLoc), UnsupportedFriend(false),
- NumTPLists(FriendTypeTPLists.size()) {
- llvm::copy(FriendTypeTPLists, getTrailingObjects());
- }
+ FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend,
+ SourceLocation FL, SourceLocation EllipsisLoc = {})
+ : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FL), Friend(Friend),
+ NextFriend() {}
- FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
- : Decl(Decl::Friend, Empty), UnsupportedFriend(false),
- NumTPLists(NumFriendTypeTPLists) {}
+ FriendDecl(Kind K, EmptyShell Empty) : Decl(K, Empty) {}
FriendDecl *getNextFriend() {
- if (!NextFriend.isOffset())
- return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
- return getNextFriendSlowCase();
+ if (NextFriend.isOffset())
+ return getNextFriendSlowCase();
+ return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
}
FriendDecl *getNextFriendSlowCase();
@@ -109,14 +83,11 @@ class FriendDecl final
friend class ASTDeclReader;
friend class ASTDeclWriter;
friend class ASTNodeImporter;
- friend TrailingObjects;
- static FriendDecl *
- Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_,
- SourceLocation FriendL, SourceLocation EllipsisLoc = {},
- ArrayRef<TemplateParameterList *> FriendTypeTPLists = {});
- static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
- unsigned FriendTypeNumTPLists);
+ static FriendDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L,
+ FriendUnion Friend, SourceLocation FriendL,
+ SourceLocation EllipsisLoc = {});
+ static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
/// If this friend declaration names an (untemplated but possibly
/// dependent) type, return the type; otherwise return null. This
@@ -126,72 +97,26 @@ class FriendDecl final
return Friend.dyn_cast<TypeSourceInfo*>();
}
- unsigned getFriendTypeNumTemplateParameterLists() const {
- return NumTPLists;
- }
-
- TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
- return getTrailingObjects(NumTPLists)[N];
- }
-
/// If this friend declaration doesn't name a type, return the inner
/// declaration.
- NamedDecl *getFriendDecl() const {
+ virtual NamedDecl *getFriendDecl() const {
return Friend.dyn_cast<NamedDecl *>();
}
- /// Retrieves the location of the 'friend' keyword.
- SourceLocation getFriendLoc() const {
- return FriendLoc;
- }
-
/// Retrieves the location of the '...', if present.
SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
- /// Retrieves the source range for the friend declaration.
- SourceRange getSourceRange() const override LLVM_READONLY {
- if (TypeSourceInfo *TInfo = getFriendType()) {
- SourceLocation StartL = (NumTPLists == 0)
- ? getFriendLoc()
- : getTrailingObjects()[0]->getTemplateLoc();
- SourceLocation EndL = isPackExpansion() ? getEllipsisLoc()
- : TInfo->getTypeLoc().getEndLoc();
- return SourceRange(StartL, EndL);
- }
-
- if (isPackExpansion())
- return SourceRange(getFriendLoc(), getEllipsisLoc());
-
- if (NamedDecl *ND = getFriendDecl()) {
- if (const auto *FD = dyn_cast<FunctionDecl>(ND))
- return FD->getSourceRange();
- if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
- return FTD->getSourceRange();
- if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
- return CTD->getSourceRange();
- if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) {
- if (DD->getOuterLocStart() != DD->getInnerLocStart())
- return DD->getSourceRange();
- }
- return SourceRange(getFriendLoc(), ND->getEndLoc());
- }
-
- return SourceRange(getFriendLoc(), getLocation());
- }
+ SourceLocation getFriendLoc() const { return FriendLoc; }
- /// Determines if this friend kind is unsupported.
- bool isUnsupportedFriend() const {
- return UnsupportedFriend;
- }
- void setUnsupportedFriend(bool Unsupported) {
- UnsupportedFriend = Unsupported;
- }
+ SourceRange getSourceRange() const override LLVM_READONLY;
bool isPackExpansion() const { return EllipsisLoc.isValid(); }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
- static bool classofKind(Kind K) { return K == Decl::Friend; }
+ static bool classofKind(Kind K) {
+ return K >= firstFriend && K <= lastFriend;
+ }
};
/// An iterator over the friend declarations of a class.
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index ed1a517247943..f5b26b0aab9ba 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -19,6 +19,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Redeclarable.h"
#include "clang/AST/TemplateBase.h"
@@ -2457,76 +2458,78 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
/// \code
/// template \<typename T> class A {
/// friend class MyVector<T>; // not a friend template
-/// template \<typename U> friend class B; // not a friend template
+/// template \<typename U> friend class B; // friend class template
/// template \<typename U> friend class Foo<T>::Nested; // friend template
/// };
/// \endcode
-///
-/// \note This class is not currently in use. All of the above
-/// will yield a FriendDecl, not a FriendTemplateDecl.
-class FriendTemplateDecl : public Decl {
- virtual void anchor();
-
-public:
- using FriendUnion = llvm::PointerUnion<NamedDecl *,TypeSourceInfo *>;
+class FriendTemplateDecl final
+ : public FriendDecl,
+ private llvm::TrailingObjects<FriendTemplateDecl,
+ TemplateParameterList *> {
+ void anchor() override;
private:
- // The number of template parameters; always non-zero.
- unsigned NumParams = 0;
+ unsigned NumTPLists = 0;
+ TemplateName Template;
- // The parameter list.
- TemplateParameterList **Params = nullptr;
-
- // The declaration that's a friend of this class.
- FriendUnion Friend;
-
- // Location of the 'friend' specifier.
- SourceLocation FriendLoc;
-
- FriendTemplateDecl(DeclContext *DC, SourceLocation Loc,
- TemplateParameterList **Params, unsigned NumParams,
- FriendUnion Friend, SourceLocation FriendLoc)
- : Decl(Decl::FriendTemplate, DC, Loc), NumParams(NumParams),
- Params(Params), Friend(Friend), FriendLoc(FriendLoc) {}
+ FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, FriendUnion Friend,
+ SourceLocation FriendLoc, SourceLocation EllipsisLoc,
+ ArrayRef<TemplateParameterList *> FriendTPLists,
+ TemplateName Template = {})
+ : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
+ EllipsisLoc),
+ NumTPLists(FriendTPLists.size()), Template(Template) {
+ assert(!FriendTPLists.empty());
+ llvm::copy(FriendTPLists, getTrailingObjects());
+ }
- FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
+ FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTPLists)
+ : FriendDecl(Decl::FriendTemplate, Empty), NumTPLists(NumFriendTPLists) {
+ assert(NumFriendTPLists != 0);
+ }
public:
friend class ASTDeclReader;
+ friend class ASTDeclWriter;
+ friend TrailingObjects;
+
+ enum class FriendTemplateEntityKind { Type, Template, Decl };
+
+ static FriendTemplateDecl *
+ Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
+ FriendUnion Friend, SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> FriendTPLists,
+ SourceLocation EllipsisLoc = {}, TemplateName Template = {});
static FriendTemplateDecl *
Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
- MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
- SourceLocation FriendLoc);
+ TemplateName Template, SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> FriendTPLists,
+ SourceLocation EllipsisLoc = {});
- static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
+ static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned NumFriendTPLists);
- /// If this friend declaration names a templated type (or
- /// a dependent member type of a templated type), return that
- /// type; otherwise return null.
- TypeSourceInfo *getFriendType() const {
- return Friend.dyn_cast<TypeSourceInfo*>();
- }
+ SourceRange getSourceRange() const override LLVM_READONLY;
- /// If this friend declaration names a templated function (or
- /// a member function of a templated type), return that type;
- /// otherwise return null.
- NamedDecl *getFriendDecl() const {
- return Friend.dyn_cast<NamedDecl*>();
- }
+ TemplateName getFriendTemplateName() const { return Template; }
- /// Retrieves the location of the 'friend' keyword.
- SourceLocation getFriendLoc() const {
- return FriendLoc;
+ FriendTemplateEntityKind getFriendKind() const {
+ if (getFriendType())
+ return FriendTemplateEntityKind::Type;
+ if (Template.isNull())
+ return FriendTemplateEntityKind::Decl;
+ return FriendTemplateEntityKind::Template;
}
- TemplateParameterList *getTemplateParameterList(unsigned i) const {
- assert(i <= NumParams);
- return Params[i];
+ NamedDecl *getFriendDecl() const override {
+ if (NamedDecl *ND = Friend.dyn_cast<NamedDecl *>())
+ return ND;
+ return Template.getAsTemplateDecl();
}
- unsigned getNumTemplateParameters() const {
- return NumParams;
+ ArrayRef<TemplateParameterList *> getTemplateParameterLists() const {
+ return ArrayRef(getTrailingObjects(), NumTPLists);
}
// Implement isa/cast/dyncast/etc.
diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h
index 4e8d1649bbf8b..679ce4e4815ae 100644
--- a/clang/include/clang/AST/JSONNodeDumper.h
+++ b/clang/include/clang/AST/JSONNodeDumper.h
@@ -268,6 +268,7 @@ class JSONNodeDumper
void VisitLinkageSpecDecl(const LinkageSpecDecl *LSD);
void VisitAccessSpecDecl(const AccessSpecDecl *ASD);
void VisitFriendDecl(const FriendDecl *FD);
+ void VisitFriendTemplateDecl(const FriendTemplateDecl *FD);
void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D);
void VisitObjCIvarDecl(const ObjCIvarDecl *D);
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 12204dcfb5938..d435cda19fa49 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1739,17 +1739,15 @@ DEF_TRAVERSE_DECL(FriendDecl, {
})
DEF_TRAVERSE_DECL(FriendTemplateDecl, {
+ const TemplateName Template = D->getFriendTemplateName();
if (D->getFriendType())
TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+ else if (!Template.isNull())
+ TRY_TO(TraverseTemplateName(Template));
else
TRY_TO(TraverseDecl(D->getFriendDecl()));
- for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
- TemplateParameterList *TPL = D->getTemplateParameterList(I);
- for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
- ITPL != ETPL; ++ITPL) {
- TRY_TO(TraverseDecl(*ITPL));
- }
- }
+ for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+ TRY_TO(TraverseTemplateParameterListHelper(TPL));
})
DEF_TRAVERSE_DECL(LinkageSpecDecl, {})
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 41ddd88a8326c..1eaafd49c2f06 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -399,6 +399,7 @@ class TextNodeDumper
void VisitLinkageSpecDecl(const LinkageSpecDecl *D);
void VisitAccessSpecDecl(const AccessSpecDecl *D);
void VisitFriendDecl(const FriendDecl *D);
+ void VisitFriendTemplateDecl(const FriendTemplateDecl *D);
void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D);
void VisitObjCIvarDecl(const ObjCIvarDecl *D);
void VisitObjCMethodDecl(const ObjCMethodDecl *D);
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 8d1dc4936cc72..114c6ae5282ef 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -99,7 +99,7 @@ def FileScopeAsm : DeclNode<Decl>;
def TopLevelStmt : DeclNode<Decl>, DeclContext;
def AccessSpec : DeclNode<Decl>;
def Friend : DeclNode<Decl>;
-def FriendTemplate : DeclNode<Decl>;
+def FriendTemplate : DeclNode<Friend>;
def StaticAssert : DeclNode<Decl>;
def ExplicitInstantiation : DeclNode<Decl>;
def CXXExpansionStmt : DeclNode<Decl>, DeclContext;
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index d44f69a815784..a6cc17980e2c2 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1206,7 +1206,6 @@ def Attributes : DiagGroup<"attributes", [UnknownAttributes,
def UnknownSanitizers : DiagGroup<"unknown-sanitizers">;
def UnnamedTypeTemplateArgs : DiagGroup<"unnamed-type-template-args",
[CXX98CompatUnnamedTypeTemplateArgs]>;
-def UnsupportedFriend : DiagGroup<"unsupported-friend">;
def UnusedArgument : DiagGroup<"unused-argument">;
def UnusedCommandLineArgument : DiagGroup<"unused-command-line-argument">;
def IgnoredOptimizationArgument : DiagGroup<"ignored-optimization-argument">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b314c17ad27bd..7c1e61120b0f2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1934,16 +1934,6 @@ def err_friend_not_first_in_declaration : Error<
"'friend' must appear first in a non-function declaration">;
def err_using_decl_friend : Error<
"cannot befriend target of using declaration">;
-def warn_template_qualified_friend_unsupported
- : Warning<
- "dependent nested name specifier %0 for friend class declaration is "
- "not supported; turning off access control for %1">,
- InGroup<UnsupportedFriend>;
-def warn_template_qualified_friend_ignored
- : Warning<"dependent nested name specifier %0 for friend template "
- "declaration is "
- "not supported; ignoring this friend declaration">,
- InGroup<UnsupportedFriend>;
def ext_friend_tag_redecl_outside_namespace : ExtWarn<
"unqualified friend declaration referring to type outside of the nearest "
"enclosing namespace is a Microsoft extension; add a nested name specifier">,
@@ -1953,6 +1943,16 @@ def err_friend_template_decl_multiple_specifiers: Error<
"a friend declaration that befriends a template must contain exactly one type-specifier">;
def friend_template_decl_malformed_pack_expansion : Error<
"friend declaration expands pack %0 that is declared it its own template parameter list">;
+def err_pack_indexing_in_friend : Error<
+ "a pack indexing specifier cannot be used in a nested name specifier of a "
+ "friend declaration">;
+def err_dependent_friend_not_member_of_template_spec : Error<
+ "%0 does not name a class template">;
+def err_dependent_friend_not_member : Error<
+ "friend declaration does not name a member of a class template specialization">;
+def err_dependent_friend_undeduced_params : Error<
+ "%select{template parameter|template parameters}0 of friend declaration "
+ "cannot be deduced from %1">;
def err_invalid_base_in_interface : Error<
"interface type cannot inherit from "
@@ -5325,11 +5325,16 @@ def note_ovl_candidate_deduced_mismatch : Note<
"adjusted type of %select{|element of }4argument}1,2%3">;
def note_ovl_candidate_non_deduced_mismatch : Note<
"candidate template ignored: could not match %
diff {$ against $|types}0,1">;
+def note_friend_template_non_deduced_mismatch : Note<
+ "candidate friend template ignored: could not match "
+ "%
diff {$ against $|types}0,1">;
// This note is needed because the above note would sometimes print two
//
diff erent types with the same name. Remove this note when the above note
// can handle that case properly.
def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
"candidate template ignored: could not match %q0 against %q1">;
+def note_friend_template_non_deduced_mismatch_qualified : Note<
+ "candidate friend template ignored: could not match %q0 against %q1">;
// Note that we don't treat templates
diff erently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d931e70cb2342..3cb0dcbbba83a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6402,7 +6402,15 @@ class Sema final : public SemaBase {
SourceLocation NameLoc,
SourceLocation EllipsisLoc,
const ParsedAttributesView &Attr,
- MultiTemplateParamsArg TempParamLists);
+ MultiTemplateParamsArg TempParamLists,
+ TemplateIdAnnotation *TemplateId);
+
+ bool CheckDependentFriend(SourceLocation Loc, NestedNameSpecifierLoc NNSLoc,
+ ArrayRef<TemplateParameterList *> TPLs,
+ bool IsInstantiation);
+
+ bool DiagnosePackIndexingInFriendNNS(SourceLocation Loc,
+ NestedNameSpecifierLoc NNSLoc);
MSPropertyDecl *HandleMSProperty(Scope *S, RecordDecl *TagD,
SourceLocation DeclStart, Declarator &D,
@@ -12735,6 +12743,18 @@ class Sema final : public SemaBase {
return false;
});
+ /// Perform [temp.friend] p5 template argument deduction for a dependent
+ /// friend declaration and a candidate class template specialization.
+ bool DeduceTemplateArguments(FriendTemplateDecl *FTD,
+ ClassTemplateDecl *PatternCTD,
+ ClassTemplateDecl *CandidateCTD,
+ ArrayRef<TemplateParameterList *> TPLs,
+ ArrayRef<TemplateArgument> PatternArgs,
+ ArrayRef<TemplateArgument> CandidateArgs,
+ SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC,
+ MultiLevelTemplateArgumentList &DeducedArgs);
+
/// Perform template argument deduction from a function call
/// (C++ [temp.deduct.call]).
///
@@ -12961,7 +12981,8 @@ class Sema final : public SemaBase {
llvm::SmallBitVector &Used);
void MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
- unsigned Depth, llvm::SmallBitVector &Used);
+ bool OnlyDeduced, unsigned Depth,
+ llvm::SmallBitVector &Used);
void MarkUsedTemplateParameters(ArrayRef<TemplateArgumentLoc> TemplateArgs,
unsigned Depth, llvm::SmallBitVector &Used);
@@ -13816,6 +13837,11 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity);
+ TypeSourceInfo *
+ SubstFriendType(TypeSourceInfo *TSI,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ SourceLocation Loc, DeclarationName Entity);
+
/// A form of SubstType intended specifically for instantiating the
/// type of a FunctionDecl. Its purpose is solely to force the
/// instantiation of default-argument expressions and to avoid
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index a3340d2f4a044..50e950e56c6ca 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -721,7 +721,7 @@ enum class TemplateSubstitutionKind : char {
// Helper functions for instantiating methods.
TypeSourceInfo *SubstFunctionType(FunctionDecl *D,
- SmallVectorImpl<ParmVarDecl *> &Params);
+ SmallVectorImpl<ParmVarDecl *> &Params);
bool InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *Tmpl);
bool InitMethodInstantiation(CXXMethodDecl *New, CXXMethodDecl *Tmpl);
@@ -730,6 +730,10 @@ enum class TemplateSubstitutionKind : char {
TemplateParameterList *
SubstTemplateParams(TemplateParameterList *List);
+ bool SubstTemplateParameterLists(
+ ArrayRef<TemplateParameterList *> TPLs,
+ SmallVectorImpl<TemplateParameterList *> &InstTPLs);
+
bool SubstQualifier(const DeclaratorDecl *OldDecl,
DeclaratorDecl *NewDecl);
bool SubstQualifier(const TagDecl *OldDecl,
@@ -740,6 +744,8 @@ enum class TemplateSubstitutionKind : char {
ArrayRef<TemplateArgument> Converted,
VarTemplateSpecializationDecl *PrevDecl = nullptr);
+ bool InstantiateFriendPackExpansion(FriendDecl *D);
+
Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias);
Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
ClassTemplatePartialSpecializationDecl *
diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 39c909d73f565..dd9fe46c9bf8a 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -311,6 +311,11 @@ struct DeductionFailureInfo {
}
};
+enum class TemplateSpecCandidateSetKind {
+ Normal,
+ FriendTemplate,
+};
+
/// TemplateSpecCandidate - This is a generalization of OverloadCandidate
/// which keeps track of template argument deduction failure info, when
/// handling explicit specializations (and instantiations) of templates
@@ -337,7 +342,8 @@ struct TemplateSpecCandidate {
}
/// Diagnose a template argument deduction failure.
- void NoteDeductionFailure(Sema &S, bool ForTakingAddress);
+ void NoteDeductionFailure(Sema &S, bool ForTakingAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind);
};
/// TemplateSpecCandidateSet - A set of generalized overload candidates,
@@ -353,11 +359,16 @@ class TemplateSpecCandidateSet {
// attribute on parameters.
bool ForTakingAddress;
+ TemplateSpecCandidateSetKind CandidateSetKind;
+
void destroyCandidates();
public:
- TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false)
- : Loc(Loc), ForTakingAddress(ForTakingAddress) {}
+ TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false,
+ TemplateSpecCandidateSetKind CandidateSetKind =
+ TemplateSpecCandidateSetKind::Normal)
+ : Loc(Loc), ForTakingAddress(ForTakingAddress),
+ CandidateSetKind(CandidateSetKind) {}
TemplateSpecCandidateSet(const TemplateSpecCandidateSet &) = delete;
TemplateSpecCandidateSet &
operator=(const TemplateSpecCandidateSet &) = delete;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index b582cbdadc070..974761b236411 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2123,6 +2123,14 @@ enum CtorInitializerType {
CTOR_INITIALIZER_INDIRECT_MEMBER
};
+/// Kinds of friend payloads owned by FriendTemplateDecl.
+enum FriendTemplateDeclKind {
+ FTDK_Type = 0,
+ FTDK_Decl = 1,
+ FTDK_Template = 2,
+ FTDK_Dependent = 3,
+};
+
/// Kinds of cleanup objects owned by ExprWithCleanups.
enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 01c5385ff5bb5..725ebbd6ec987 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -185,6 +185,8 @@ namespace clang {
return Importer.importInto(To, From);
}
+ Expected<FriendDecl::FriendUnion> importFriendUnion(FriendDecl *D);
+
// Use this to import pointers of specific type.
template <typename ImportT>
[[nodiscard]] Error importInto(ImportT *&To, ImportT *From) {
@@ -539,6 +541,7 @@ namespace clang {
ExpectedDecl VisitFieldDecl(FieldDecl *D);
ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D);
ExpectedDecl VisitFriendDecl(FriendDecl *D);
+ ExpectedDecl VisitFriendTemplateDecl(FriendTemplateDecl *D);
ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D);
ExpectedDecl VisitVarDecl(VarDecl *D);
ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D);
@@ -4392,19 +4395,15 @@ struct FriendCountAndPosition {
static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
FriendDecl *FD2) {
- if ((!FD1->getFriendType()) != (!FD2->getFriendType()))
+ if (FD1->getKind() != FD2->getKind())
return false;
- if (const TypeSourceInfo *TSI = FD1->getFriendType())
- return Importer.IsStructurallyEquivalent(
- TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false);
-
ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
StructuralEquivalenceContext Ctx(
Importer.getToContext().getLangOpts(), FD1->getASTContext(),
FD2->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default,
- /* StrictTypeSpelling = */ false, /* Complain = */ false);
+ /*StrictTypeSpelling=*/false, /*Complain=*/false);
return Ctx.IsEquivalent(FD1, FD2);
}
@@ -4424,10 +4423,30 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
}
assert(FriendPosition && "Friend decl not found in own parent.");
-
return {FriendCount, *FriendPosition};
}
+Expected<FriendDecl::FriendUnion>
+ASTNodeImporter::importFriendUnion(FriendDecl *D) {
+ if (NamedDecl *FriendD = D->getFriendDecl()) {
+ NamedDecl *ToFriendD;
+ if (Error Err = importInto(ToFriendD, FriendD))
+ return std::move(Err);
+
+ if (FriendD->getFriendObjectKind() != Decl::FOK_None &&
+ !FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator))
+ ToFriendD->setObjectOfFriendDecl(false);
+
+ return ToFriendD;
+ }
+
+ // The friend is a type, not a decl.
+ auto TSIOrErr = import(D->getFriendType());
+ if (TSIOrErr)
+ return *TSIOrErr;
+ return TSIOrErr.takeError();
+}
+
ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
// Import the major distinguishing characteristics of a declaration.
DeclContext *DC, *LexicalDC;
@@ -4454,32 +4473,10 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
// Not found. Create it.
// The declarations will be put into order later by ImportDeclContext.
- FriendDecl::FriendUnion ToFU;
- if (NamedDecl *FriendD = D->getFriendDecl()) {
- NamedDecl *ToFriendD;
- if (Error Err = importInto(ToFriendD, FriendD))
- return std::move(Err);
-
- if (FriendD->getFriendObjectKind() != Decl::FOK_None &&
- !(FriendD->isInIdentifierNamespace(Decl::IDNS_NonMemberOperator)))
- ToFriendD->setObjectOfFriendDecl(false);
-
- ToFU = ToFriendD;
- } else { // The friend is a type, not a decl.
- if (auto TSIOrErr = import(D->getFriendType()))
- ToFU = *TSIOrErr;
- else
- return TSIOrErr.takeError();
- }
-
- SmallVector<TemplateParameterList *, 1> ToTPLists(D->NumTPLists);
- auto **FromTPLists = D->getTrailingObjects();
- for (unsigned I = 0; I < D->NumTPLists; I++) {
- if (auto ListOrErr = import(FromTPLists[I]))
- ToTPLists[I] = *ListOrErr;
- else
- return ListOrErr.takeError();
- }
+ auto ToFUOrErr = importFriendUnion(D);
+ if (!ToFUOrErr)
+ return ToFUOrErr.takeError();
+ FriendDecl::FriendUnion ToFU = *ToFUOrErr;
auto LocationOrErr = import(D->getLocation());
if (!LocationOrErr)
@@ -4494,7 +4491,7 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
FriendDecl *FrD;
if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC,
*LocationOrErr, ToFU, *FriendLocOrErr,
- *EllipsisLocOrErr, ToTPLists))
+ *EllipsisLocOrErr))
return FrD;
FrD->setAccess(D->getAccess());
@@ -4503,6 +4500,74 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
return FrD;
}
+ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ DeclContext *DC, *LexicalDC;
+ if (Error Err = ImportDeclContext(D, DC, LexicalDC))
+ return std::move(Err);
+
+ const auto *RD = cast<CXXRecordDecl>(DC);
+ SmallVector<FriendTemplateDecl *, 2> ImportedEquivalentFriends;
+ for (FriendDecl *ImportedFriend : RD->friends()) {
+ auto *ImportedFriendTemplate = dyn_cast<FriendTemplateDecl>(ImportedFriend);
+ if (ImportedFriendTemplate &&
+ IsEquivalentFriend(Importer, D, ImportedFriendTemplate))
+ ImportedEquivalentFriends.push_back(ImportedFriendTemplate);
+ }
+
+ FriendCountAndPosition CountAndPosition =
+ getFriendCountAndPosition(Importer, D);
+ assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount &&
+ "Class with non-matching friends is imported, ODR check wrong?");
+
+ if (ImportedEquivalentFriends.size() == CountAndPosition.TotalCount)
+ return Importer.MapImported(
+ D, ImportedEquivalentFriends[CountAndPosition.IndexOfDecl]);
+
+ FriendTemplateDecl::FriendUnion ToFU;
+ if (D->getFriendKind() !=
+ FriendTemplateDecl::FriendTemplateEntityKind::Template) {
+ auto ToFUOrErr = importFriendUnion(D);
+ if (!ToFUOrErr)
+ return ToFUOrErr.takeError();
+ ToFU = *ToFUOrErr;
+ }
+
+ TemplateName ToTemplate;
+ const TemplateName FromTemplate = D->getFriendTemplateName();
+ if (!FromTemplate.isNull()) {
+ if (Error Err = importInto(ToTemplate, FromTemplate))
+ return std::move(Err);
+ }
+
+ ArrayRef<TemplateParameterList *> FromTPLs = D->getTemplateParameterLists();
+ SmallVector<TemplateParameterList *, 1> ToTPLs(FromTPLs.size());
+ if (Error Err = ImportContainerChecked(FromTPLs, ToTPLs))
+ return std::move(Err);
+
+ auto LocationOrErr = import(D->getLocation());
+ if (!LocationOrErr)
+ return LocationOrErr.takeError();
+
+ auto FriendLocOrErr = import(D->getFriendLoc());
+ if (!FriendLocOrErr)
+ return FriendLocOrErr.takeError();
+
+ auto EllipsisLocOrErr = import(D->getEllipsisLoc());
+ if (!EllipsisLocOrErr)
+ return EllipsisLocOrErr.takeError();
+
+ FriendTemplateDecl *FTD;
+ if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+ *LocationOrErr, ToFU, *FriendLocOrErr, ToTPLs,
+ *EllipsisLocOrErr, ToTemplate))
+ return FTD;
+
+ FTD->setAccess(D->getAccess());
+ FTD->setLexicalDeclContext(LexicalDC);
+ LexicalDC->addDeclInternal(FTD);
+ return FTD;
+}
+
ExpectedDecl ASTNodeImporter::VisitObjCIvarDecl(ObjCIvarDecl *D) {
// Import the major distinguishing characteristics of an ivar.
DeclContext *DC, *LexicalDC;
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index d8bbfbe5dac72..ebf4507e6e67d 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2297,7 +2297,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
- return true;
+ return IsStructurallyEquivalent(Context, Params1->getRequiresClause(),
+ Params2->getRequiresClause());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
@@ -2434,10 +2435,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FriendDecl *D1, FriendDecl *D2) {
+ if (D1->isPackExpansion() != D2->isPackExpansion())
+ return false;
+
if ((D1->getFriendType() && D2->getFriendDecl()) ||
- (D1->getFriendDecl() && D2->getFriendType())) {
- return false;
- }
+ (D1->getFriendDecl() && D2->getFriendType()))
+ return false;
if (D1->getFriendType() && D2->getFriendType())
return IsStructurallyEquivalent(Context,
D1->getFriendType()->getType(),
@@ -2448,6 +2451,47 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ FriendTemplateDecl *FTD1,
+ FriendTemplateDecl *FTD2) {
+ if (FTD1->isPackExpansion() != FTD2->isPackExpansion())
+ return false;
+
+ ArrayRef<TemplateParameterList *> TPL1 = FTD1->getTemplateParameterLists();
+ ArrayRef<TemplateParameterList *> TPL2 = FTD2->getTemplateParameterLists();
+ if (!llvm::equal(
+ TPL1, TPL2,
+ [&Context](TemplateParameterList *LHS, TemplateParameterList *RHS) {
+ return IsStructurallyEquivalent(Context, LHS, RHS);
+ }))
+ return false;
+
+ auto FK1 = FTD1->getFriendKind();
+ auto FK2 = FTD2->getFriendKind();
+ if (FK1 != FK2)
+ return false;
+
+ switch (FK1) {
+ case FriendTemplateDecl::FriendTemplateEntityKind::Type: {
+ const TemplateName TN1 = FTD1->getFriendTemplateName();
+ const TemplateName TN2 = FTD2->getFriendTemplateName();
+ if (TN1.isNull() != TN2.isNull())
+ return false;
+ if (!IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
+ FTD2->getFriendType()->getType()))
+ return false;
+ return TN1.isNull() || IsStructurallyEquivalent(Context, TN1, TN2);
+ }
+ case FriendTemplateDecl::FriendTemplateEntityKind::Template:
+ return IsStructurallyEquivalent(Context, FTD1->getFriendTemplateName(),
+ FTD2->getFriendTemplateName());
+ case FriendTemplateDecl::FriendTemplateEntityKind::Decl:
+ return IsStructurallyEquivalent(Context, static_cast<FriendDecl *>(FTD1),
+ static_cast<FriendDecl *>(FTD2));
+ }
+ llvm_unreachable("unknown friend template kind");
+}
+
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TypedefNameDecl *D1, TypedefNameDecl *D2) {
if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 6bfc2eb62b284..d730b4f00fba2 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,28 +13,18 @@
#include "clang/AST/DeclFriend.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
-#include "clang/Basic/LLVM.h"
+#include "clang/AST/ExternalASTSource.h"
#include <cassert>
-#include <cstddef>
using namespace clang;
void FriendDecl::anchor() {}
-FriendDecl *FriendDecl::getNextFriendSlowCase() {
- return cast_or_null<FriendDecl>(
- NextFriend.get(getASTContext().getExternalSource()));
-}
-
-FriendDecl *
-FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
- FriendUnion Friend, SourceLocation FriendL,
- SourceLocation EllipsisLoc,
- ArrayRef<TemplateParameterList *> FriendTypeTPLists) {
+FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
+ FriendUnion Friend, SourceLocation FriendL,
+ SourceLocation EllipsisLoc) {
#ifndef NDEBUG
if (const auto *D = dyn_cast<NamedDecl *>(Friend)) {
assert(isa<FunctionDecl>(D) ||
@@ -46,25 +36,22 @@ FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L,
// to the original declaration when instantiating members.
assert(D->getFriendObjectKind() ||
(cast<CXXRecordDecl>(DC)->getTemplateSpecializationKind()));
- // These template parameters are for friend types only.
- assert(FriendTypeTPLists.empty());
}
#endif
- std::size_t Extra =
- FriendDecl::additionalSizeToAlloc<TemplateParameterList *>(
- FriendTypeTPLists.size());
- auto *FD = new (C, DC, Extra)
- FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists);
+ auto *FD =
+ new (C, DC) FriendDecl(Decl::Friend, DC, L, Friend, FriendL, EllipsisLoc);
cast<CXXRecordDecl>(DC)->pushFriendDecl(FD);
return FD;
}
-FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
- unsigned FriendTypeNumTPLists) {
- std::size_t Extra =
- additionalSizeToAlloc<TemplateParameterList *>(FriendTypeNumTPLists);
- return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists);
+FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
+ return new (C, ID) FriendDecl(Decl::Friend, EmptyShell());
+}
+
+FriendDecl *FriendDecl::getNextFriendSlowCase() {
+ return cast_or_null<FriendDecl>(
+ NextFriend.get(getASTContext().getExternalSource()));
}
FriendDecl *CXXRecordDecl::getFirstFriend() const {
@@ -72,3 +59,29 @@ FriendDecl *CXXRecordDecl::getFirstFriend() const {
Decl *First = data().FirstFriend.get(Source);
return First ? cast<FriendDecl>(First) : nullptr;
}
+
+SourceRange FriendDecl::getSourceRange() const {
+ if (TypeSourceInfo *TInfo = getFriendType()) {
+ SourceLocation EndL =
+ isPackExpansion() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc();
+ return SourceRange(getFriendLoc(), EndL);
+ }
+
+ if (isPackExpansion())
+ return SourceRange(getFriendLoc(), getEllipsisLoc());
+
+ if (NamedDecl *ND = getFriendDecl()) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+ return FD->getSourceRange();
+ if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+ return FTD->getSourceRange();
+ if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
+ return CTD->getSourceRange();
+ if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) {
+ if (DD->getOuterLocStart() != DD->getInnerLocStart())
+ return DD->getSourceRange();
+ }
+ return SourceRange(getFriendLoc(), ND->getEndLoc());
+ }
+ return SourceRange(getFriendLoc(), getLocation());
+}
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 4be3e977b815e..1312ffd080342 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -70,6 +70,7 @@ namespace {
void VisitEmptyDecl(EmptyDecl *D);
void VisitFunctionDecl(FunctionDecl *D);
void VisitFriendDecl(FriendDecl *D);
+ void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitFieldDecl(FieldDecl *D);
void VisitVarDecl(VarDecl *D);
void VisitLabelDecl(LabelDecl *D);
@@ -889,24 +890,17 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
if (TypeSourceInfo *TSI = D->getFriendType()) {
- unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
- for (unsigned i = 0; i < NumTPLists; ++i)
- printTemplateParameters(D->getFriendTypeTemplateParameterList(i));
Out << "friend ";
Out << TSI->getType().getAsString(Policy);
- }
- else if (FunctionDecl *FD =
- dyn_cast<FunctionDecl>(D->getFriendDecl())) {
+ } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D->getFriendDecl())) {
Out << "friend ";
VisitFunctionDecl(FD);
- }
- else if (FunctionTemplateDecl *FTD =
- dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
+ } else if (FunctionTemplateDecl *FTD =
+ dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) {
Out << "friend ";
VisitFunctionTemplateDecl(FTD);
- }
- else if (ClassTemplateDecl *CTD =
- dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
+ } else if (ClassTemplateDecl *CTD =
+ dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) {
Out << "friend ";
VisitRedeclarableTemplateDecl(CTD);
}
@@ -915,6 +909,27 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
Out << "...";
}
+void DeclPrinter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+ printTemplateParameters(TPL);
+
+ TemplateName TN = D->getFriendTemplateName();
+ if (D->getFriendType() || TN.isNull()) {
+ VisitFriendDecl(D);
+ } else {
+ Out << "friend ";
+ if (auto *CTD =
+ dyn_cast_if_present<ClassTemplateDecl>(TN.getAsTemplateDecl()))
+ Out << CTD->getTemplatedDecl()->getKindName() << ' ';
+ TN.print(Out, Policy,
+ Policy.SuppressScope ? TemplateName::Qualified::None
+ : TemplateName::Qualified::AsWritten);
+
+ if (D->isPackExpansion())
+ Out << "...";
+ }
+}
+
void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
prettyPrintPragmas(D);
// FIXME: add printing of pragma attributes if required.
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 3ff6e3a3221fb..6f7ce21227338 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1236,21 +1236,52 @@ void FriendTemplateDecl::anchor() {}
FriendTemplateDecl *
FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
- SourceLocation L,
- MutableArrayRef<TemplateParameterList *> Params,
- FriendUnion Friend, SourceLocation FLoc) {
- TemplateParameterList **TPL = nullptr;
- if (!Params.empty()) {
- TPL = new (Context) TemplateParameterList *[Params.size()];
- llvm::copy(Params, TPL);
- }
- return new (Context, DC)
- FriendTemplateDecl(DC, L, TPL, Params.size(), Friend, FLoc);
+ SourceLocation Loc, FriendUnion Friend,
+ SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> FriendTPLists,
+ SourceLocation EllipsisLoc, TemplateName Template) {
+ std::size_t Extra =
+ FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+ FriendTPLists.size());
+ auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+ DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTPLists, Template);
+ cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+ return FTD;
}
-FriendTemplateDecl *FriendTemplateDecl::CreateDeserialized(ASTContext &C,
- GlobalDeclID ID) {
- return new (C, ID) FriendTemplateDecl(EmptyShell());
+FriendTemplateDecl *
+FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
+ SourceLocation Loc, TemplateName Template,
+ SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> FriendTPLists,
+ SourceLocation EllipsisLoc) {
+ auto *Friend = Template.getAsTemplateDecl();
+ assert(Friend && "friend template name must be resolved");
+ std::size_t Extra =
+ FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+ FriendTPLists.size());
+ auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+ DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTPLists, Template);
+ cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+ return FTD;
+}
+
+FriendTemplateDecl *
+FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned NumFriendTPLists) {
+ std::size_t Extra =
+ FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+ NumFriendTPLists);
+ return new (C, ID, Extra) FriendTemplateDecl(EmptyShell(), NumFriendTPLists);
+}
+
+SourceRange FriendTemplateDecl::getSourceRange() const {
+ SourceLocation Begin = getTemplateParameterLists().front()->getTemplateLoc();
+ SourceLocation End =
+ !Template.isNull() && !getFriendType()
+ ? (isPackExpansion() ? getEllipsisLoc() : getLocation())
+ : FriendDecl::getSourceRange().getEnd();
+ return SourceRange(Begin, End);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index af54f15410789..b9c2272b97a4b 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1151,6 +1151,20 @@ void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) {
attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
}
+void JSONNodeDumper::VisitFriendTemplateDecl(const FriendTemplateDecl *FD) {
+ if (FD->getFriendKind() !=
+ FriendTemplateDecl::FriendTemplateEntityKind::Template) {
+ VisitFriendDecl(FD);
+ return;
+ }
+
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ FD->getFriendTemplateName().print(OS, PrintPolicy);
+ JOS.attribute("templateName", Str);
+ attributeOnlyIfTrue("isPackExpansion", FD->isPackExpansion());
+}
+
void JSONNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
VisitNamedDecl(D);
JOS.attribute("type", createQualType(D->getType()));
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..297d6ba5b7686 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -164,7 +164,9 @@ void ODRHash::AddTemplateName(TemplateName Name) {
case TemplateName::AssumedTemplate:
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
+ break;
case TemplateName::UsingTemplate:
+ AddDecl(Name.getAsUsingShadowDecl()->getTargetDecl());
break;
case TemplateName::DeducedTemplate:
llvm_unreachable("Unexpected DeducedTemplate");
@@ -220,6 +222,11 @@ void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {
for (auto *ND : TPL->asArray()) {
AddSubDecl(ND);
}
+
+ const Expr *RequiresClause = TPL->getRequiresClause();
+ AddBoolean(RequiresClause);
+ if (RequiresClause)
+ AddStmt(RequiresClause);
}
void ODRHash::clear() {
@@ -473,6 +480,26 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
Hash.AddBoolean(D->isPackExpansion());
}
+ void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ for (const TemplateParameterList *TPL : D->getTemplateParameterLists())
+ Hash.AddTemplateParameterList(TPL);
+
+ bool IsTemplateFriend =
+ D->getFriendKind() ==
+ FriendTemplateDecl::FriendTemplateEntityKind::Template;
+ Hash.AddBoolean(!IsTemplateFriend);
+ if (!IsTemplateFriend) {
+ VisitFriendDecl(D);
+ if (D->getFriendKind() ==
+ FriendTemplateDecl::FriendTemplateEntityKind::Type &&
+ !D->getFriendTemplateName().isNull())
+ Hash.AddTemplateName(D->getFriendTemplateName());
+ } else {
+ Hash.AddTemplateName(D->getFriendTemplateName());
+ Hash.AddBoolean(D->isPackExpansion());
+ }
+ }
+
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
// Only care about default arguments as part of the definition.
const bool hasDefaultArgument =
@@ -557,6 +584,7 @@ bool ODRHash::isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent) {
case Decl::EnumConstant: // Only found in EnumDecl's.
case Decl::Field:
case Decl::Friend:
+ case Decl::FriendTemplate:
case Decl::FunctionTemplate:
case Decl::StaticAssert:
case Decl::TypeAlias:
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 9bcc0280bbe40..d07767aab2068 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -3051,6 +3051,18 @@ void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
OS << "...";
}
+void TextNodeDumper::VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ if (D->getFriendKind() !=
+ FriendTemplateDecl::FriendTemplateEntityKind::Template) {
+ VisitFriendDecl(D);
+ return;
+ }
+
+ dumpBareTemplateName(D->getFriendTemplateName());
+ if (D->isPackExpansion())
+ OS << "...";
+}
+
void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
dumpName(D);
dumpType(D->getType());
diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp
index df875e0b40079..8b32a48483a67 100644
--- a/clang/lib/Index/IndexDecl.cpp
+++ b/clang/lib/Index/IndexDecl.cpp
@@ -763,6 +763,15 @@ class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> {
return true;
}
+ bool VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ const NamedDecl *ND = cast<NamedDecl>(D->getDeclContext());
+ if (!D->getFriendType() && D->getFriendTemplateName().isNull())
+ ND = D->getFriendDecl();
+ for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+ indexTemplateParameters(TPL, ND);
+ return VisitFriendDecl(D);
+ }
+
bool VisitImportDecl(const ImportDecl *D) {
return IndexCtx.importedModule(D);
}
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index a3617c3db49c4..0bdea354c2406 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1986,7 +1986,10 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
bool Owned = false;
SkipBodyInfo SkipBody;
- if (TemplateId) {
+ if (TemplateId &&
+ (TUK != TagUseKind::Friend ||
+ TemplateInfo.Kind != ParsedTemplateKind::Template ||
+ TemplateId->isInvalid() || !TemplateId->Template.get().isDependent())) {
// Explicit specialization, class template partial specialization,
// or explicit instantiation.
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
@@ -2006,10 +2009,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, TemplateArgsPtr,
TemplateId->RAngleLoc, attrs);
- // Friend template-ids are treated as references unless
- // they have template headers, in which case they're ill-formed
- // (FIXME: "template <class T> friend class A<T>::B<int>;").
- // We diagnose this error in ActOnClassTemplateSpecialization.
} else if (TUK == TagUseKind::Reference ||
(TUK == TagUseKind::Friend &&
TemplateInfo.Kind == ParsedTemplateKind::NonTemplate)) {
@@ -2106,11 +2105,17 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
SkipUntil(tok::semi, StopBeforeMatch);
}
+ if (TemplateId) {
+ Name = nullptr;
+ NameLoc = TemplateId->TemplateNameLoc;
+ }
+
TagOrTempResult = Actions.ActOnTemplatedFriendTag(
getCurScope(), DS.getFriendSpecLoc(), TagType, StartLoc, SS, Name,
NameLoc, EllipsisLoc, attrs,
MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr,
- TemplateParams ? TemplateParams->size() : 0));
+ TemplateParams ? TemplateParams->size() : 0),
+ TemplateId);
} else {
if (TUK != TagUseKind::Declaration && TUK != TagUseKind::Definition)
ProhibitCXX11Attributes(attrs, diag::err_attributes_not_allowed,
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 0f291900d5612..f1e328ccba426 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1178,8 +1178,9 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
E = RD->friend_end();
I != E && Complete; ++I) {
+ FriendDecl *Friend = *I;
// Check if friend classes and methods are complete.
- if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
+ if (TypeSourceInfo *TSI = Friend->getFriendType()) {
// Friend classes are available as the TypeSourceInfo of the FriendDecl.
if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
@@ -1188,7 +1189,7 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
} else {
// Friend functions are available through the NamedDecl of FriendDecl.
if (const FunctionDecl *FD =
- dyn_cast<FunctionDecl>((*I)->getFriendDecl()))
+ dyn_cast<FunctionDecl>(Friend->getFriendDecl()))
Complete = FD->isDefined();
else
// This is a template friend, give up.
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 9b4f59613d33b..cc2af8feb3794 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -21,6 +21,8 @@
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/ScopeExit.h"
using namespace clang;
@@ -272,7 +274,163 @@ struct AccessTarget : public AccessedEntity {
mutable const CXXRecordDecl *InstanceContext;
const CXXRecordDecl *DeclaringClass;
};
+} // namespace
+static CanQual<FunctionProtoType> GetCanonicalFunctionProto(ASTContext &Context,
+ QualType Ty) {
+ return Context.getCanonicalType(Ty)->getAs<FunctionProtoType>();
+}
+
+static CanQual<FunctionProtoType>
+GetCanonicalFunctionProto(ASTContext &Context, const FunctionDecl *FD) {
+ return GetCanonicalFunctionProto(Context, FD->getType());
+}
+
+static const TemplateSpecializationType *
+GetQualifierClassTemplateSpecializationType(ASTContext &Context,
+ NestedNameSpecifier NNS) {
+ if (!NNS || NNS.getKind() != NestedNameSpecifier::Kind::Type)
+ return nullptr;
+
+ QualType Ty(NNS.getAsType(), 0);
+ if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
+ Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(Context);
+
+ const auto *TST = Ty->getAsNonAliasTemplateSpecializationType();
+ if (TST && isa_and_nonnull<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
+ return TST;
+
+ return nullptr;
+}
+
+static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
+ if (auto *FTD = FD->getPrimaryTemplate())
+ return FTD->getCanonicalDecl();
+
+ if (auto *FTD = FD->getDescribedFunctionTemplate())
+ return FTD->getCanonicalDecl();
+
+ if (FunctionDecl *Pattern =
+ FD->getTemplateInstantiationPattern(/*ForDefinition=*/false)) {
+ if (auto *FTD = Pattern->getDescribedFunctionTemplate())
+ return FTD->getCanonicalDecl();
+ if (auto *FTD = Pattern->getPrimaryTemplate())
+ return FTD->getCanonicalDecl();
+ }
+
+ return nullptr;
+}
+
+static ClassTemplateDecl *GetClassTemplatePattern(ClassTemplateDecl *CTD) {
+ while (ClassTemplateDecl *Pattern = CTD->getInstantiatedFromMemberTemplate())
+ CTD = Pattern;
+ return CTD;
+}
+
+static ClassTemplateDecl *GetClassTemplateDecl(CXXRecordDecl *RD) {
+ if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ return Spec->getSpecializedTemplate();
+ return RD->getDescribedClassTemplate();
+}
+
+static TemplateParameterList *
+SubstTemplateParameterList(Sema &S, TemplateParameterList *TPL, DeclContext *DC,
+ const MultiLevelTemplateArgumentList &Args) {
+ TemplateParameterList *InstTPL =
+ S.SubstTemplateParams(TPL, DC, Args,
+ /*EvaluateConstraints=*/false);
+ if (!InstTPL || !TPL->getRequiresClause())
+ return InstTPL;
+
+ ExprResult InstRequiresClause =
+ S.SubstConstraintExprWithoutSatisfaction(TPL->getRequiresClause(), Args);
+ if (!InstRequiresClause.isUsable())
+ return nullptr;
+
+ return TemplateParameterList::Create(
+ S.Context, InstTPL->getTemplateLoc(), InstTPL->getLAngleLoc(),
+ InstTPL->asArray(), InstTPL->getRAngleLoc(), InstRequiresClause.get());
+}
+
+static AccessResult
+DeduceTemplateArguments(Sema &S, FriendTemplateDecl *FTD, DeclContext *DC,
+ const TemplateSpecializationType *TST,
+ ArrayRef<TemplateParameterList *> TPLs,
+ TemplateSpecCandidateSet *FailedTSC,
+ MultiLevelTemplateArgumentList &DeducedArgs) {
+ const auto *CandidateRD = dyn_cast<CXXRecordDecl>(DC);
+ if (!CandidateRD)
+ return AR_inaccessible;
+
+ ClassTemplateDecl *CandidateCTD = CandidateRD->getDescribedClassTemplate();
+ ArrayRef<TemplateArgument> CandidateArgs;
+ if (CandidateCTD) {
+ CandidateArgs = CandidateCTD->getInjectedTemplateArgs(S.Context);
+ } else {
+ const auto *CandidateSpec =
+ dyn_cast<ClassTemplateSpecializationDecl>(CandidateRD);
+ if (!CandidateSpec)
+ return AR_inaccessible;
+ CandidateCTD = CandidateSpec->getSpecializedTemplate();
+ CandidateArgs = CandidateSpec->getTemplateArgs().asArray();
+ }
+
+ auto *PatternCTD = dyn_cast_if_present<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl());
+ if (!PatternCTD || !declaresSameEntity(GetClassTemplatePattern(CandidateCTD),
+ GetClassTemplatePattern(PatternCTD)))
+ return AR_inaccessible;
+
+ if (S.DeduceTemplateArguments(FTD, PatternCTD, CandidateCTD, TPLs,
+ TST->template_arguments(), CandidateArgs,
+ FTD->getLocation(), FailedTSC, DeducedArgs))
+ return AR_accessible;
+
+ return CandidateRD->isDependentContext() ? AR_dependent : AR_inaccessible;
+}
+
+class FriendTemplateMatchContext {
+ Sema &S;
+ FriendTemplateDecl *FTD;
+ Sema::InstantiatingTemplate Inst;
+ TemplateDeductionInfo Info;
+ MultiLevelTemplateArgumentList DeducedArgs;
+ Sema::SFINAETrap Trap;
+ LocalInstantiationScope InstantiationScope;
+ AccessResult Result = AR_inaccessible;
+
+public:
+ FriendTemplateMatchContext(Sema &S, FriendTemplateDecl *FTD)
+ : S(S), FTD(FTD), Inst(S, FTD->getLocation(), FTD),
+ Info(FTD->getLocation()), Trap(S, Info), InstantiationScope(S) {}
+
+ AccessResult deduce(DeclContext *DC, const TemplateSpecializationType *TST,
+ ArrayRef<TemplateParameterList *> TPLs,
+ TemplateSpecCandidateSet *FailedTSC) {
+ if (Inst.isInvalid())
+ return Result = AR_inaccessible;
+ return Result = DeduceTemplateArguments(S, FTD, DC, TST, TPLs, FailedTSC,
+ DeducedArgs);
+ }
+
+ AccessResult getAccessResult() const { return Result; }
+ MultiLevelTemplateArgumentList &getDeducedArgs() { return DeducedArgs; }
+
+ bool hasDeducedArgs() const { return Result == AR_accessible; }
+ bool hasErrorOccurred() const { return Trap.hasErrorOccurred(); }
+};
+
+static bool HasSameFunctionType(Sema &S, QualType FriendType,
+ QualType ContextType, SourceLocation Loc) {
+ if (!S.Context.hasSameFunctionTypeIgnoringExceptionSpec(FriendType,
+ ContextType))
+ return false;
+
+ const auto *FriendFPT = FriendType->castAs<FunctionProtoType>();
+ const auto *ContextFPT = ContextType->castAs<FunctionProtoType>();
+ return !S.CheckEquivalentExceptionSpec(S.PDiag(), S.PDiag(), FriendFPT, Loc,
+ ContextFPT, Loc);
}
/// Checks whether one class might instantiate to the other.
@@ -284,8 +442,12 @@ static bool MightInstantiateTo(const CXXRecordDecl *From,
const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext();
const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext();
- if (FromDC == ToDC) return true;
- if (FromDC->isFileContext() || ToDC->isFileContext()) return false;
+
+ if (FromDC == ToDC)
+ return true;
+
+ if (FromDC->isFileContext() || ToDC->isFileContext())
+ return false;
// Be conservative.
return true;
@@ -343,9 +505,7 @@ static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
return OnFailure;
}
-
-static bool MightInstantiateTo(Sema &S, DeclContext *Context,
- DeclContext *Friend) {
+static bool MightInstantiateTo(DeclContext *Context, DeclContext *Friend) {
if (Friend == Context)
return true;
@@ -364,7 +524,7 @@ static bool MightInstantiateTo(Sema &S, DeclContext *Context,
// Asks whether the type in 'context' can ever instantiate to the type
// in 'friend'.
-static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
+static bool MightInstantiateTo(CanQualType Context, CanQualType Friend) {
if (Friend == Context)
return true;
@@ -375,49 +535,66 @@ static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend)
return true;
}
-static bool MightInstantiateTo(Sema &S,
- FunctionDecl *Context,
- FunctionDecl *Friend) {
- if (Context->getDeclName() != Friend->getDeclName())
+static bool MightInstantiateTo(CanQual<FunctionProtoType> Context,
+ CanQual<FunctionProtoType> Friend) {
+ if (Friend.getQualifiers() != Context.getQualifiers())
+ return false;
+
+ if (Friend->getNumParams() != Context->getNumParams())
return false;
- if (!MightInstantiateTo(S,
- Context->getDeclContext(),
- Friend->getDeclContext()))
+ if (!MightInstantiateTo(Context->getReturnType(), Friend->getReturnType()))
return false;
- CanQual<FunctionProtoType> FriendTy
- = S.Context.getCanonicalType(Friend->getType())
- ->getAs<FunctionProtoType>();
- CanQual<FunctionProtoType> ContextTy
- = S.Context.getCanonicalType(Context->getType())
- ->getAs<FunctionProtoType>();
+ for (unsigned I = 0, E = Friend->getNumParams(); I != E; ++I)
+ if (!MightInstantiateTo(Context->getParamType(I), Friend->getParamType(I)))
+ return false;
+
+ return true;
+}
+
+static bool MightInstantiateTo(ASTContext &Ctx, DeclarationName Context,
+ DeclarationName Friend) {
+ if (Context == Friend)
+ return true;
- // There isn't any way that I know of to add qualifiers
- // during instantiation.
- if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
+ if (Context.getNameKind() != Friend.getNameKind())
return false;
- if (FriendTy->getNumParams() != ContextTy->getNumParams())
+ switch (Context.getNameKind()) {
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ return MightInstantiateTo(Ctx.getCanonicalType(Context.getCXXNameType()),
+ Ctx.getCanonicalType(Friend.getCXXNameType()));
+
+ default:
return false;
+ }
+}
- if (!MightInstantiateTo(S, ContextTy->getReturnType(),
- FriendTy->getReturnType()))
+static bool MightInstantiateTo(ASTContext &Ctx, FunctionDecl *Context,
+ FunctionDecl *Friend) {
+ if (!MightInstantiateTo(Ctx, Context->getDeclName(), Friend->getDeclName()))
return false;
- for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
- if (!MightInstantiateTo(S, ContextTy->getParamType(I),
- FriendTy->getParamType(I)))
- return false;
+ DeclContext *ContextDC = Context->getDeclContext();
+ DeclContext *FriendDC = Friend->getDeclContext();
- return true;
+ if (!FriendDC->isDependentContext() &&
+ !MightInstantiateTo(ContextDC, FriendDC))
+ return false;
+
+ CanQual<FunctionProtoType> FriendTy = GetCanonicalFunctionProto(Ctx, Friend);
+ CanQual<FunctionProtoType> ContextTy =
+ GetCanonicalFunctionProto(Ctx, Context);
+
+ return MightInstantiateTo(ContextTy, FriendTy);
}
-static bool MightInstantiateTo(Sema &S,
- FunctionTemplateDecl *Context,
+static bool MightInstantiateTo(ASTContext &Ctx, FunctionTemplateDecl *Context,
FunctionTemplateDecl *Friend) {
- return MightInstantiateTo(S,
- Context->getTemplatedDecl(),
+ return MightInstantiateTo(Ctx, Context->getTemplatedDecl(),
Friend->getTemplatedDecl());
}
@@ -478,7 +655,7 @@ static AccessResult MatchesFriend(Sema &S,
}
// It's a match.
- if (Friend == CTD->getCanonicalDecl())
+ if (declaresSameEntity(Friend, CTD))
return AR_accessible;
// If the context isn't dependent, it can't be a dependent match.
@@ -492,8 +669,7 @@ static AccessResult MatchesFriend(Sema &S,
// If the class's context can't instantiate to the friend's
// context, it can't be a dependent match.
- if (!MightInstantiateTo(S, CTD->getDeclContext(),
- Friend->getDeclContext()))
+ if (!MightInstantiateTo(CTD->getDeclContext(), Friend->getDeclContext()))
continue;
// Otherwise, it's a dependent match.
@@ -515,7 +691,7 @@ static AccessResult MatchesFriend(Sema &S,
if (Friend == *I)
return AR_accessible;
- if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
+ if (EC.isDependent() && MightInstantiateTo(S.Context, *I, Friend))
OnFailure = AR_dependent;
}
@@ -534,21 +710,390 @@ static AccessResult MatchesFriend(Sema &S,
for (SmallVectorImpl<FunctionDecl*>::const_iterator
I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
- FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate();
- if (!FTD)
- FTD = (*I)->getDescribedFunctionTemplate();
+ FunctionTemplateDecl *FTD = TryGetFunctionTemplateDecl(*I);
if (!FTD)
continue;
- FTD = FTD->getCanonicalDecl();
-
if (Friend == FTD)
return AR_accessible;
- if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
+ if (EC.isDependent() && MightInstantiateTo(S.Context, FTD, Friend))
+ OnFailure = AR_dependent;
+ }
+
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ NamedDecl *ND) {
+ ND = cast<NamedDecl>(ND->getCanonicalDecl());
+ if (ClassTemplateDecl *CTD = dyn_cast<ClassTemplateDecl>(ND))
+ return MatchesFriend(S, EC, CTD);
+
+ if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+ return MatchesFriend(S, EC, FTD);
+
+ if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND))
+ return MatchesFriend(S, EC, RD);
+
+ assert(isa<FunctionDecl>(ND) && "unknown friend decl kind");
+ return MatchesFriend(S, EC, cast<FunctionDecl>(ND));
+}
+
+static AccessResult MatchesFriend(Sema &S, FriendTemplateDecl *FTD,
+ DeclarationName FriendName,
+ TagTypeKind FriendTagKind,
+ ClassTemplateDecl *ContextCTD,
+ const TemplateSpecializationType *FriendTST,
+ ArrayRef<TemplateParameterList *> TPLs,
+ TemplateParameterList *MemberTPL,
+ TemplateSpecCandidateSet *FailedTSC) {
+ if (FriendName != ContextCTD->getDeclName())
+ return AR_inaccessible;
+
+ if ((FriendTagKind == TagTypeKind::Union) !=
+ ContextCTD->getTemplatedDecl()->isUnion())
+ return AR_inaccessible;
+
+ DeclContext *ContextDC = ContextCTD->getDeclContext();
+ AccessResult OnFailure =
+ ContextDC->isDependentContext() ? AR_dependent : AR_inaccessible;
+
+ FriendTemplateMatchContext FTMC(S, FTD);
+ AccessResult Result = FTMC.deduce(ContextDC, FriendTST, TPLs, FailedTSC);
+ if (!FTMC.hasDeducedArgs())
+ return Result;
+
+ TemplateParameterList *InstTPL = SubstTemplateParameterList(
+ S, MemberTPL, ContextDC, FTMC.getDeducedArgs());
+ if (!InstTPL || FTMC.hasErrorOccurred())
+ return OnFailure;
+
+ Sema::TemplateCompareNewDeclInfo FriendInfo(
+ ContextDC, FTD->getLexicalDeclContext(), FTD->getLocation());
+ if (S.TemplateParameterListsAreEqual(
+ FriendInfo, InstTPL, ContextCTD, ContextCTD->getTemplateParameters(),
+ /*Complain=*/false, Sema::TPL_TemplateMatch))
+ return AR_accessible;
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ ClassTemplateDecl *FriendCTD,
+ NestedNameSpecifier Qualifier,
+ TemplateSpecCandidateSet *FailedTSC) {
+ const auto *FriendTST =
+ GetQualifierClassTemplateSpecializationType(S.Context, Qualifier);
+ if (!FriendTST)
+ return MatchesFriend(S, EC, FriendCTD);
+
+ ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+ AccessResult OnFailure = AR_inaccessible;
+ for (CXXRecordDecl *ContextRD : EC.Records) {
+ ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD);
+ if (!ContextCTD)
+ continue;
+
+ AccessResult Result =
+ MatchesFriend(S, FTD, FriendCTD->getDeclName(),
+ FriendCTD->getTemplatedDecl()->getTagKind(), ContextCTD,
+ FriendTST, TPLs.drop_back(), TPLs.back(), FailedTSC);
+ if (Result == AR_accessible)
+ return AR_accessible;
+ if (Result == AR_dependent)
+ OnFailure = AR_dependent;
+ }
+
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ TemplateName FriendTemplate,
+ ClassTemplateDecl *FriendCTD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ NestedNameSpecifier Qualifier = FriendTemplate.getQualifier();
+ if (FriendTemplate.getAsUsingShadowDecl())
+ Qualifier = FriendCTD->getTemplatedDecl()->getQualifier();
+ return MatchesFriend(S, EC, FTD, FriendCTD, Qualifier, FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ ClassTemplateDecl *FriendCTD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ return MatchesFriend(S, EC, FTD, FriendCTD,
+ FriendCTD->getTemplatedDecl()->getQualifier(),
+ FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, FriendTemplateDecl *FTD,
+ FunctionDecl *FriendFD,
+ FunctionDecl *ContextFD,
+ const TemplateSpecializationType *FriendTST,
+ ArrayRef<TemplateParameterList *> TPLs,
+ TemplateSpecCandidateSet *FailedTSC) {
+ if (!MightInstantiateTo(S.Context, ContextFD->getDeclName(),
+ FriendFD->getDeclName()))
+ return AR_inaccessible;
+
+ FunctionTemplateDecl *FriendTemplate =
+ FriendFD->getDescribedFunctionTemplate();
+ FunctionTemplateDecl *ContextTemplate = TryGetFunctionTemplateDecl(ContextFD);
+
+ if (FriendTemplate && !ContextTemplate)
+ return AR_inaccessible;
+
+ DeclContext *ContextDC = ContextFD->getDeclContext();
+ AccessResult OnFailure =
+ ContextDC->isDependentContext() ? AR_dependent : AR_inaccessible;
+
+ FriendTemplateMatchContext FTMC(S, FTD);
+ AccessResult Result = FTMC.deduce(ContextDC, FriendTST, TPLs, FailedTSC);
+ if (!FTMC.hasDeducedArgs())
+ return Result;
+
+ Sema::TemplateCompareNewDeclInfo FriendInfo(
+ ContextDC, FTD->getLexicalDeclContext(), FTD->getLocation());
+ if (FriendTemplate) {
+ TemplateParameterList *InstTPL =
+ SubstTemplateParameterList(S, FriendTemplate->getTemplateParameters(),
+ ContextDC, FTMC.getDeducedArgs());
+ if (!InstTPL || !S.TemplateParameterListsAreEqual(
+ FriendInfo, InstTPL, ContextTemplate,
+ ContextTemplate->getTemplateParameters(),
+ /*Complain=*/false, Sema::TPL_TemplateMatch))
+ return OnFailure;
+
+ ContextFD = ContextTemplate->getTemplatedDecl();
+ }
+
+ Sema::ContextRAII SavedContext(S, FTD->getDeclContext());
+ QualType InstFriendType =
+ S.SubstType(FriendFD->getType(), FTMC.getDeducedArgs(),
+ FriendFD->getLocation(), FriendFD->getDeclName());
+ SavedContext.pop();
+ if (InstFriendType.isNull() || FTMC.hasErrorOccurred())
+ return OnFailure;
+
+ if (ContextTemplate && !FriendTemplate) {
+ AccessResult OnSpecializationFailure =
+ ContextFD->isDependentContext() ? AR_dependent : OnFailure;
+ const ASTTemplateArgumentListInfo *ArgsWritten =
+ FriendFD->getTemplateSpecializationArgsAsWritten();
+ TemplateArgumentListInfo InstArgs;
+ if (ArgsWritten) {
+ InstArgs.setLAngleLoc(ArgsWritten->getLAngleLoc());
+ InstArgs.setRAngleLoc(ArgsWritten->getRAngleLoc());
+ if (S.SubstTemplateArguments(ArgsWritten->arguments(),
+ FTMC.getDeducedArgs(), InstArgs))
+ return OnSpecializationFailure;
+ }
+
+ FunctionDecl *ContextSpecialization = nullptr;
+ TemplateDeductionInfo FunctionInfo(FTD->getLocation());
+ if (S.DeduceTemplateArguments(
+ ContextTemplate, ArgsWritten ? &InstArgs : nullptr, InstFriendType,
+ ContextSpecialization,
+ FunctionInfo) != TemplateDeductionResult::Success ||
+ !ContextSpecialization || FTMC.hasErrorOccurred() ||
+ !declaresSameEntity(ContextSpecialization, ContextFD))
+ return OnSpecializationFailure;
+
+ ContextFD = ContextSpecialization;
+ }
+
+ if (!HasSameFunctionType(S, InstFriendType, ContextFD->getType(),
+ FTD->getLocation()) ||
+ FTMC.hasErrorOccurred())
+ return OnFailure;
+
+ if (!FriendTemplate)
+ return AR_accessible;
+
+ AssociatedConstraint FriendRequiresClause =
+ FriendFD->getTrailingRequiresClause();
+ AssociatedConstraint ContextRequiresClause =
+ ContextFD->getTrailingRequiresClause();
+ if (FriendRequiresClause.isNull() != ContextRequiresClause.isNull())
+ return AR_inaccessible;
+
+ if (!FriendRequiresClause)
+ return AR_accessible;
+
+ ExprResult InstFriendRequiresClause =
+ S.SubstConstraintExprWithoutSatisfaction(
+ const_cast<Expr *>(FriendRequiresClause.ConstraintExpr),
+ FTMC.getDeducedArgs());
+
+ if (!InstFriendRequiresClause.isUsable())
+ return OnFailure;
+
+ if (!S.AreConstraintExpressionsEqual(
+ ContextFD, ContextRequiresClause.ConstraintExpr, FriendInfo,
+ InstFriendRequiresClause.get()))
+ return OnFailure;
+ return FTMC.hasErrorOccurred() ? AR_inaccessible : AR_accessible;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ FunctionDecl *FriendFD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+ S.Context, FriendFD->getQualifier());
+ if (!FriendTST)
+ return AR_inaccessible;
+
+ ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+ AccessResult OnFailure = AR_inaccessible;
+ for (FunctionDecl *ContextFD : EC.Functions) {
+ AccessResult Result =
+ MatchesFriend(S, FTD, FriendFD, ContextFD, FriendTST, TPLs, FailedTSC);
+ if (Result == AR_accessible)
+ return AR_accessible;
+
+ if (Result == AR_dependent)
OnFailure = AR_dependent;
}
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD, NamedDecl *Friend,
+ TemplateSpecCandidateSet *FailedTSC) {
+ TemplateName FriendTemplate = FTD->getFriendTemplateName();
+ if (auto *FriendCTD = dyn_cast_if_present<ClassTemplateDecl>(
+ FriendTemplate.getAsTemplateDecl()))
+ return MatchesFriend(S, EC, FTD, FriendTemplate, FriendCTD, FailedTSC);
+ if (auto *FriendCTD = dyn_cast<ClassTemplateDecl>(Friend))
+ return MatchesFriend(S, EC, FTD, FriendCTD, FailedTSC);
+ if (FunctionDecl *FriendFD = Friend->getAsFunction())
+ return MatchesFriend(S, EC, FTD, FriendFD, FailedTSC);
+ return MatchesFriend(S, EC, Friend);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ TypeSourceInfo *FriendTSI,
+ TemplateSpecCandidateSet *FailedTSC) {
+ QualType FriendType = FriendTSI->getType();
+ if (!FriendType->isDependentType())
+ return MatchesFriend(S, EC, S.Context.getCanonicalType(FriendType));
+
+ AccessResult OnFailure = AR_inaccessible;
+ if (auto FriendTSTL =
+ FriendTSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>()) {
+ const auto *FriendTST = FriendTSTL.getTypePtr();
+ const auto *FriendQTST = GetQualifierClassTemplateSpecializationType(
+ S.Context, FriendTSTL.getQualifierLoc().getNestedNameSpecifier());
+ if (!FriendQTST)
+ return OnFailure;
+
+ ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+ TemplateName FriendTemplate = FriendTST->getTemplateName();
+ DeclarationName FriendName;
+ if (TemplateDecl *TD = FriendTemplate.getAsTemplateDecl())
+ FriendName = TD->getDeclName();
+ else if (DependentTemplateName *DTN =
+ FriendTemplate.getAsDependentTemplateName())
+ FriendName = DTN->getName().getIdentifier();
+
+ TagTypeKind FriendTagKind =
+ TypeWithKeyword::getTagTypeKindForKeyword(FriendTST->getKeyword());
+
+ for (CXXRecordDecl *ContextRD : EC.Records) {
+ ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD);
+ if (!ContextCTD)
+ continue;
+
+ if (FriendName && ContextCTD->getDeclName() != FriendName)
+ continue;
+
+ if ((FriendTagKind == TagTypeKind::Union) !=
+ ContextCTD->getTemplatedDecl()->isUnion())
+ continue;
+
+ FriendTemplateMatchContext FTMC(S, FTD);
+ AccessResult Result =
+ FTMC.deduce(ContextRD->getDeclContext(), FriendQTST, TPLs, FailedTSC);
+ if (!FTMC.hasDeducedArgs()) {
+ if (Result == AR_dependent)
+ OnFailure = AR_dependent;
+ continue;
+ }
+
+ TypeSourceInfo *InstFriendTSI =
+ S.SubstFriendType(FriendTSI, FTMC.getDeducedArgs(),
+ FTD->getLocation(), DeclarationName());
+ if (InstFriendTSI && !FTMC.hasErrorOccurred() &&
+ S.Context.hasSameType(InstFriendTSI->getType(),
+ S.Context.getCanonicalTagType(ContextRD)))
+ return AR_accessible;
+
+ if (ContextRD->isDependentContext())
+ OnFailure = AR_dependent;
+ }
+
+ return OnFailure;
+ }
+
+ const auto *FriendDNT = FriendType->getAs<DependentNameType>();
+ if (!FriendDNT)
+ return OnFailure;
+
+ const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+ S.Context, FriendDNT->getQualifier());
+ if (!FriendTST)
+ return OnFailure;
+ ArrayRef<TemplateParameterList *> TPLs = FTD->getTemplateParameterLists();
+
+ TagTypeKind FriendTagKind =
+ TypeWithKeyword::getTagTypeKindForKeyword(FriendDNT->getKeyword());
+ for (CXXRecordDecl *ContextRD : EC.Records) {
+ if (ContextRD->getDeclName() != FriendDNT->getIdentifier())
+ continue;
+
+ if (ClassTemplateDecl *ContextCTD = GetClassTemplateDecl(ContextRD)) {
+ if (FTD->getFriendTemplateName().isNull()) {
+ if (FailedTSC) {
+ MultiLevelTemplateArgumentList DeducedArgs;
+ DeduceTemplateArguments(S, FTD, ContextCTD->getDeclContext(),
+ FriendTST, TPLs, FailedTSC, DeducedArgs);
+ }
+ continue;
+ }
+
+ AccessResult Result = MatchesFriend(
+ S, FTD, FriendDNT->getIdentifier(), FriendTagKind, ContextCTD,
+ FriendTST, TPLs.drop_back(), TPLs.back(), FailedTSC);
+ if (Result == AR_accessible)
+ return AR_accessible;
+ if (Result == AR_dependent)
+ OnFailure = AR_dependent;
+ continue;
+ }
+
+ if (!FTD->getFriendTemplateName().isNull())
+ continue;
+
+ if ((FriendTagKind == TagTypeKind::Union) != ContextRD->isUnion())
+ continue;
+
+ MultiLevelTemplateArgumentList DeducedArgs;
+ AccessResult Result =
+ DeduceTemplateArguments(S, FTD, ContextRD->getDeclContext(), FriendTST,
+ TPLs, FailedTSC, DeducedArgs);
+ if (Result == AR_accessible)
+ return AR_accessible;
+ if (Result == AR_dependent)
+ OnFailure = AR_dependent;
+ }
return OnFailure;
}
@@ -557,40 +1102,47 @@ static AccessResult MatchesFriend(Sema &S,
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
FriendDecl *FriendD) {
- // Whitelist accesses if there's an invalid or unsupported friend
- // declaration.
- if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
+ // Whitelist accesses if there's an invalid friend declaration.
+ if (FriendD->isInvalidDecl())
return AR_accessible;
+ if (NamedDecl *Friend = FriendD->getFriendDecl())
+ return MatchesFriend(S, EC, Friend);
+
if (TypeSourceInfo *T = FriendD->getFriendType())
return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified());
- NamedDecl *Friend
- = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
-
- // FIXME: declarations with dependent or templated scope.
-
- if (isa<ClassTemplateDecl>(Friend))
- return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+ return AR_inaccessible;
+}
- if (isa<FunctionTemplateDecl>(Friend))
- return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ if (FTD->isInvalidDecl())
+ return AR_accessible;
- if (isa<CXXRecordDecl>(Friend))
- return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+ if (TypeSourceInfo *TSI = FTD->getFriendType())
+ return MatchesFriend(S, EC, FTD, TSI, FailedTSC);
- assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
- return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
+ NamedDecl *Friend = FTD->getFriendDecl();
+ assert(Friend && "friend template must name a type or declaration");
+ return MatchesFriend(S, EC, FTD, Friend, FailedTSC);
}
-static AccessResult GetFriendKind(Sema &S,
- const EffectiveContext &EC,
- const CXXRecordDecl *Class) {
+static AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC,
+ const CXXRecordDecl *Class,
+ TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
// Okay, check friends.
- for (auto *Friend : Class->friends()) {
- switch (MatchesFriend(S, EC, Friend)) {
+ for (FriendDecl *Friend : Class->friends()) {
+ AccessResult AR;
+ if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend))
+ AR = MatchesFriend(S, EC, FTD, FailedTSC);
+ else
+ AR = MatchesFriend(S, EC, Friend);
+
+ switch (AR) {
case AR_accessible:
return AR_accessible;
@@ -614,6 +1166,7 @@ namespace {
struct ProtectedFriendContext {
Sema &S;
const EffectiveContext &EC;
+ TemplateSpecCandidateSet *FailedTSC;
const CXXRecordDecl *NamingClass;
bool CheckDependent;
bool EverDependent;
@@ -623,18 +1176,19 @@ struct ProtectedFriendContext {
ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
const CXXRecordDecl *InstanceContext,
- const CXXRecordDecl *NamingClass)
- : S(S), EC(EC), NamingClass(NamingClass),
- CheckDependent(InstanceContext->isDependentContext() ||
- NamingClass->isDependentContext()),
- EverDependent(false) {}
+ const CXXRecordDecl *NamingClass,
+ TemplateSpecCandidateSet *FailedTSC)
+ : S(S), EC(EC), FailedTSC(FailedTSC), NamingClass(NamingClass),
+ CheckDependent(InstanceContext->isDependentContext() ||
+ NamingClass->isDependentContext()),
+ EverDependent(false) {}
/// Check classes in the current path for friendship, starting at
/// the given index.
bool checkFriendshipAlongPath(unsigned I) {
assert(I < CurPath.size());
for (unsigned E = CurPath.size(); I != E; ++I) {
- switch (GetFriendKind(S, EC, CurPath[I])) {
+ switch (GetFriendKind(S, EC, CurPath[I], FailedTSC)) {
case AR_accessible: return true;
case AR_inaccessible: continue;
case AR_dependent: EverDependent = true; continue;
@@ -721,9 +1275,9 @@ struct ProtectedFriendContext {
/// because the original target might have been more accessible
/// because of crazy subclassing.
/// So we don't implement that.
-static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
- const CXXRecordDecl *InstanceContext,
- const CXXRecordDecl *NamingClass) {
+static AccessResult GetProtectedFriendKind(
+ Sema &S, const EffectiveContext &EC, const CXXRecordDecl *InstanceContext,
+ const CXXRecordDecl *NamingClass, TemplateSpecCandidateSet *FailedTSC) {
assert(InstanceContext == nullptr ||
InstanceContext->getCanonicalDecl() == InstanceContext);
assert(NamingClass->getCanonicalDecl() == NamingClass);
@@ -731,19 +1285,20 @@ static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
// If we don't have an instance context, our constraints give us
// that NamingClass <= P <= NamingClass, i.e. P == NamingClass.
// This is just the usual friendship check.
- if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
+ if (!InstanceContext)
+ return GetFriendKind(S, EC, NamingClass, FailedTSC);
- ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
+ ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass, FailedTSC);
if (PRC.findFriendship(InstanceContext)) return AR_accessible;
if (PRC.EverDependent) return AR_dependent;
return AR_inaccessible;
}
-static AccessResult HasAccess(Sema &S,
- const EffectiveContext &EC,
+static AccessResult HasAccess(Sema &S, const EffectiveContext &EC,
const CXXRecordDecl *NamingClass,
AccessSpecifier Access,
- const AccessTarget &Target) {
+ const AccessTarget &Target,
+ TemplateSpecCandidateSet *FailedTSC) {
assert(NamingClass->getCanonicalDecl() == NamingClass &&
"declaration should be canonicalized before being passed here");
@@ -863,7 +1418,8 @@ static AccessResult HasAccess(Sema &S,
if (!InstanceContext) return AR_dependent;
}
- switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
+ switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass,
+ FailedTSC)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: return OnFailure;
case AR_dependent: return AR_dependent;
@@ -871,7 +1427,7 @@ static AccessResult HasAccess(Sema &S,
llvm_unreachable("impossible friendship kind");
}
- switch (GetFriendKind(S, EC, NamingClass)) {
+ switch (GetFriendKind(S, EC, NamingClass, FailedTSC)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: return OnFailure;
case AR_dependent: return AR_dependent;
@@ -984,7 +1540,8 @@ static CXXBasePath *FindBestPath(Sema &S,
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
PathAccess = std::max(PathAccess, BaseAccess);
- switch (HasAccess(S, EC, NC, PathAccess, Target)) {
+ switch (HasAccess(S, EC, NC, PathAccess, Target,
+ /*FailedTSC=*/nullptr)) {
case AR_inaccessible: break;
case AR_accessible:
PathAccess = AS_public;
@@ -1180,7 +1737,8 @@ static void DiagnoseAccessPath(Sema &S,
accessSoFar = D->getAccess();
const CXXRecordDecl *declaringClass = entity.getDeclaringClass();
- switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) {
+ switch (HasAccess(S, EC, declaringClass, accessSoFar, entity,
+ /*FailedTSC=*/nullptr)) {
// If the declaration is accessible when named in its declaring
// class, then we must be constrained by the path.
case AR_accessible:
@@ -1223,7 +1781,8 @@ static void DiagnoseAccessPath(Sema &S,
accessSoFar = baseAccess;
}
- switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) {
+ switch (HasAccess(S, EC, derivingClass, accessSoFar, entity,
+ /*FailedTSC=*/nullptr)) {
case AR_inaccessible: break;
case AR_accessible:
accessSoFar = AS_public;
@@ -1327,9 +1886,9 @@ static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S,
/// Determines whether the accessed entity is accessible. Public members
/// have been weeded out by this point.
-static AccessResult IsAccessible(Sema &S,
- const EffectiveContext &EC,
- AccessTarget &Entity) {
+static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC,
+ AccessTarget &Entity,
+ TemplateSpecCandidateSet *FailedTSC) {
// Determine the actual naming class.
const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass();
@@ -1341,7 +1900,8 @@ static AccessResult IsAccessible(Sema &S,
// which don't require [M4] or [B4]. These are by far the most
// common forms of privileged access.
if (UnprivilegedAccess != AS_none) {
- switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
+ switch (
+ HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity, FailedTSC)) {
case AR_dependent:
// This is actually an interesting policy decision. We don't
// *have* to delay immediately here: we can do the full access
@@ -1370,7 +1930,7 @@ static AccessResult IsAccessible(Sema &S,
const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
FinalAccess = Target->getAccess();
- switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
+ switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity, FailedTSC)) {
case AR_accessible:
// Target is accessible at EC when named in its declaring class.
// We can now hill-climb and simply check whether the declaring
@@ -1422,25 +1982,30 @@ static void DelayDependentAccess(Sema &S,
Entity.getDiag());
}
-/// Checks access to an entity from the given effective context.
-static AccessResult CheckEffectiveAccess(Sema &S,
- const EffectiveContext &EC,
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
SourceLocation Loc,
- AccessTarget &Entity) {
- assert(Entity.getAccess() != AS_public && "called for public access!");
+ AccessTarget &Entity,
+ TemplateSpecCandidateSet *FailedTSC) {
+ assert((Entity.isQuiet() || FailedTSC) &&
+ "non-quiet access check requires a candidate set");
- switch (IsAccessible(S, EC, Entity)) {
+ switch (IsAccessible(S, EC, Entity, FailedTSC)) {
case AR_dependent:
DelayDependentAccess(S, EC, Loc, Entity);
return AR_dependent;
- case AR_inaccessible:
+ case AR_inaccessible: {
if (S.getLangOpts().MSVCCompat &&
IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity))
return AR_accessible;
- if (!Entity.isQuiet())
- DiagnoseBadAccess(S, Loc, EC, Entity);
+
+ if (Entity.isQuiet())
+ return AR_inaccessible;
+
+ DiagnoseBadAccess(S, Loc, EC, Entity);
+ FailedTSC->NoteCandidates(S, Loc);
return AR_inaccessible;
+ }
case AR_accessible:
return AR_accessible;
@@ -1450,6 +2015,20 @@ static AccessResult CheckEffectiveAccess(Sema &S,
llvm_unreachable("invalid access result");
}
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
+ SourceLocation Loc,
+ AccessTarget &Entity) {
+ assert(Entity.getAccess() != AS_public && "called for public access!");
+
+ if (Entity.isQuiet())
+ return CheckEffectiveAccess(S, EC, Loc, Entity, /*FailedTSC=*/nullptr);
+
+ TemplateSpecCandidateSet FailedTSC(
+ Loc, /*ForTakingAddress=*/false,
+ TemplateSpecCandidateSetKind::FriendTemplate);
+ return CheckEffectiveAccess(S, EC, Loc, Entity, &FailedTSC);
+}
+
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
AccessTarget &Entity) {
// If the access path is public, it's accessible everywhere.
@@ -1944,7 +2523,8 @@ bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass,
AccessTarget Entity(Context, AccessedEntity::Member, NamingClass,
DeclAccessPair::make(Target, AS_none), BaseType);
EffectiveContext EC(CurContext);
- return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
+ return ::IsAccessible(*this, EC, Entity, /*FailedTSC=*/nullptr) !=
+ ::AR_inaccessible;
}
if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index bd239adb0f215..3b3c9a476421d 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -4497,6 +4497,7 @@ CXCursorKind clang::getCursorKindForDecl(const Decl *D) {
case Decl::StaticAssert:
return CXCursor_StaticAssert;
case Decl::Friend:
+ case Decl::FriendTemplate:
return CXCursor_FriendDecl;
case Decl::TranslationUnit:
return CXCursor_TranslationUnit;
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index ce91099cd037c..606cfe7bdcf73 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2175,9 +2175,10 @@ void SubstituteParameterMappings::buildParameterMapping(
assert(Arg && "expected a default argument");
DefaultArgs.emplace_back(std::move(*Arg));
}
- SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*Depth=*/0,
- OccurringIndices);
- SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*Depth=*/0,
+ SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*OnlyDeduced=*/false,
+ /*Depth=*/0, OccurringIndices);
+ SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*OnlyDeduced=*/false,
+ /*Depth=*/0,
OccurringIndicesForSubsumption);
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d87710d3cf140..c68ac12a88e8b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10916,9 +10916,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
if (isFriend) {
// For friend function specializations, this is a dependent
// specialization if its semantic context is dependent, its
- // type is dependent, or if its template-id is dependent.
+ // qualifier is dependent, its type is dependent, or its template-id is
+ // dependent.
isDependentSpecialization =
- DC->isDependentContext() || NewFD->getType()->isDependentType() ||
+ DC->isDependentContext() || NewFD->getQualifier().isDependent() ||
+ NewFD->getType()->isDependentType() ||
(HasExplicitTemplateArgs &&
TemplateSpecializationType::
anyInstantiationDependentTemplateArguments(
@@ -12583,7 +12585,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
// struct B { struct Y { ~Y(); }; using X = Y; };
// template struct A<B>;
if (NewFD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None ||
- !Destructor->getFunctionObjectParameterType()->isDependentType()) {
+ (!Destructor->getFunctionObjectParameterType()->isDependentType() &&
+ !Destructor->getDeclName().isDependentName())) {
CanQualType ClassType =
Context.getCanonicalTagType(Destructor->getParent());
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5c425a176ff41..8d5ee07c5ad49 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18127,31 +18127,153 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
return Decl;
}
+static QualType IgnorePackIndexing(QualType T) {
+ if (const auto *PIT = dyn_cast<PackIndexingType>(T))
+ return PIT->getPattern();
+ return T;
+}
+
+static const TemplateSpecializationType *
+GetClassTemplateSpecializationType(ASTContext &Context, QualType T) {
+ T = IgnorePackIndexing(T);
+ if (const auto *ICNT = dyn_cast<InjectedClassNameType>(T))
+ T = ICNT->getDecl()->getCanonicalTemplateSpecializationType(Context);
+
+ const auto *TST = dyn_cast<TemplateSpecializationType>(T);
+ if (!TST)
+ return nullptr;
+
+ TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();
+ if (!TD || isa<ClassTemplateDecl>(TD))
+ return TST;
+ return nullptr;
+}
+
+bool Sema::DiagnosePackIndexingInFriendNNS(SourceLocation Loc,
+ NestedNameSpecifierLoc NNSLoc) {
+ for (TypeLoc TL = NNSLoc.getAsTypeLoc(); TL;
+ TL = TL.getPrefix().getAsTypeLoc()) {
+ if (TL.getTypeLocClass() != TypeLoc::PackIndexing)
+ continue;
+
+ Diag(Loc, diag::err_pack_indexing_in_friend) << TL.getSourceRange();
+ return true;
+ }
+ return false;
+}
+
+static void DiagnoseDependentFriendNotMember(Sema &S, SourceLocation Loc,
+ NestedNameSpecifier NNS) {
+ QualType T(NNS.getAsType(), 0);
+ if (const auto *TST =
+ dyn_cast<TemplateSpecializationType>(IgnorePackIndexing(T))) {
+ if (isa_and_nonnull<TypeAliasTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl())) {
+ S.Diag(Loc, diag::err_dependent_friend_not_member_of_template_spec)
+ << NNS;
+ return;
+ }
+ }
+
+ if (NNS.getAsRecordDecl()) {
+ S.Diag(Loc, diag::err_dependent_friend_not_member_of_template_spec) << NNS;
+ } else {
+ S.Diag(Loc, diag::err_dependent_friend_not_member);
+ }
+}
+
+bool Sema::CheckDependentFriend(SourceLocation Loc,
+ NestedNameSpecifierLoc NNSLoc,
+ ArrayRef<TemplateParameterList *> TPLs,
+ bool IsInstantiation) {
+ NestedNameSpecifier NNS = NNSLoc.getNestedNameSpecifier();
+ if (!NNS.isDependent() && !IsInstantiation)
+ return false;
+
+ assert(NNS.getKind() == NestedNameSpecifier::Kind::Type &&
+ "nested-name-specifier of dependent friend must be a type");
+
+ QualType T(NNS.getAsType(), 0);
+ if (DiagnosePackIndexingInFriendNNS(Loc, NNSLoc))
+ return true;
+
+ const TemplateSpecializationType *TST =
+ GetClassTemplateSpecializationType(Context, T);
+ if (!TST) {
+ DiagnoseDependentFriendNotMember(*this, Loc, NNS);
+ return true;
+ }
+
+ if (TPLs.empty())
+ return false;
+
+ SmallVector<NamedDecl *, 4> UndeducedParameters;
+ for (TemplateParameterList *Params : TPLs) {
+ llvm::SmallBitVector UsedParameters(Params->size());
+ MarkUsedTemplateParameters(TST->template_arguments(),
+ /*OnlyDeduced=*/true, Params->getDepth(),
+ UsedParameters);
+
+ for (unsigned I = 0, N = UsedParameters.size(); I != N; ++I)
+ if (!UsedParameters[I])
+ UndeducedParameters.push_back(Params->getParam(I));
+ }
+
+ if (UndeducedParameters.empty())
+ return false;
+
+ Diag(Loc, diag::err_dependent_friend_undeduced_params)
+ << (UndeducedParameters.size() > 1) << QualType(TST, 0);
+
+ for (NamedDecl *Param : UndeducedParameters) {
+ if (Param->getDeclName())
+ Diag(Param->getLocation(), diag::note_non_deducible_parameter)
+ << Param->getDeclName();
+ else
+ Diag(Param->getLocation(), diag::note_non_deducible_parameter)
+ << "(anonymous)";
+ }
+
+ return true;
+}
+
DeclResult Sema::ActOnTemplatedFriendTag(
Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
SourceLocation EllipsisLoc, const ParsedAttributesView &Attr,
- MultiTemplateParamsArg TempParamLists) {
+ MultiTemplateParamsArg TempParamLists, TemplateIdAnnotation *TemplateId) {
TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
bool IsMemberSpecialization = false;
bool Invalid = false;
- if (TemplateParameterList *TemplateParams =
- MatchTemplateParametersToScopeSpecifier(
- TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true,
- IsMemberSpecialization, Invalid)) {
+ TemplateParameterList *TemplateParams =
+ MatchTemplateParametersToScopeSpecifier(TagLoc, NameLoc, SS, TemplateId,
+ TempParamLists, /*friend*/ true,
+ IsMemberSpecialization, Invalid);
+ if (TemplateId) {
+ if (Invalid)
+ return true;
+
+ if (TemplateParams) {
+ Diag(NameLoc, diag::err_not_class_template_specialization) << 0;
+ return true;
+ }
+ }
+
+ if (TemplateParams) {
if (TemplateParams->size() > 0) {
- // This is a declaration of a class template.
if (Invalid)
return true;
- return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
- Name, NameLoc, Attr, TemplateParams, AS_public,
- /*ModulePrivateLoc=*/SourceLocation(),
- FriendLoc, TempParamLists.size() - 1,
- TempParamLists.data(), IsMemberSpecialization)
- .get();
+ if (SS.isEmpty() || !SS.getScopeRep().isDependent()) {
+ DeclResult Result = CheckClassTemplate(
+ S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr,
+ TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(),
+ FriendLoc, TempParamLists.size() - 1, TempParamLists.data(),
+ IsMemberSpecialization);
+ return Result.get();
+ }
} else {
// The "template<>" header is extraneous.
Diag(TemplateParams->getTemplateLoc(), diag::err_template_tag_noparams)
@@ -18159,9 +18281,10 @@ DeclResult Sema::ActOnTemplatedFriendTag(
}
}
- if (Invalid) return true;
+ if (Invalid)
+ return true;
- bool isAllExplicitSpecializations =
+ bool IsAllExplicitSpecializations =
llvm::all_of(TempParamLists, [](const TemplateParameterList *List) {
return List->size() == 0;
});
@@ -18172,7 +18295,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
// about the template header and build an appropriate non-templated
// friend. TODO: for source fidelity, remember the headers.
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
- if (isAllExplicitSpecializations) {
+ if (!TemplateId && IsAllExplicitSpecializations) {
if (SS.isEmpty()) {
bool Owned = false;
bool IsDependent = false;
@@ -18189,16 +18312,15 @@ DeclResult Sema::ActOnTemplatedFriendTag(
}
TypeSourceInfo *TSI = nullptr;
- ElaboratedTypeKeyword Keyword
- = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
+ ElaboratedTypeKeyword Keyword =
+ TypeWithKeyword::getKeywordForTagTypeKind(Kind);
QualType T = CheckTypenameType(Keyword, TagLoc, QualifierLoc, *Name,
NameLoc, &TSI, /*DeducedTSTContext=*/true);
if (T.isNull())
return true;
- FriendDecl *Friend =
- FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
- EllipsisLoc, TempParamLists);
+ FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI,
+ FriendLoc, EllipsisLoc);
Friend->setAccess(AS_public);
CurContext->addDecl(Friend);
return Friend;
@@ -18206,43 +18328,86 @@ DeclResult Sema::ActOnTemplatedFriendTag(
assert(SS.isNotEmpty() && "valid templated tag with no SS and no direct?");
- // CWG 2917: if it (= the friend-type-specifier) is a pack expansion
- // (13.7.4 [temp.variadic]), any packs expanded by that pack expansion
- // shall not have been introduced by the template-declaration.
+ ArrayRef<TemplateParameterList *> TPLs = TempParamLists;
+ if (TemplateParams)
+ TPLs = TPLs.drop_back();
+ if (CheckDependentFriend(TagLoc, QualifierLoc, TPLs,
+ /*IsInstantiation=*/false))
+ return true;
+
+ TypeSourceInfo *TSI = nullptr;
+ if (TemplateId) {
+ ASTTemplateArgsPtr ParsedArgs(TemplateId->getTemplateArgs(),
+ TemplateId->NumArgs);
+ TypeResult ParsedType = ActOnTagTemplateIdType(
+ TagUseKind::Friend, static_cast<TypeSpecifierType>(TagSpec), TagLoc, SS,
+ TemplateId->TemplateKWLoc, TemplateId->Template, NameLoc,
+ TemplateId->LAngleLoc, ParsedArgs, TemplateId->RAngleLoc);
+ if (ParsedType.isInvalid())
+ return true;
+
+ GetTypeFromParser(ParsedType.get(), &TSI);
+ } else {
+ ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
+ QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
+ TSI = Context.CreateTypeSourceInfo(T);
+
+ DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
+ TL.setElaboratedKeywordLoc(TagLoc);
+ TL.setQualifierLoc(QualifierLoc);
+ TL.setNameLoc(NameLoc);
+ }
+
SmallVector<UnexpandedParameterPack, 1> Unexpanded;
- collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
- unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
- for (UnexpandedParameterPack &U : Unexpanded) {
- if (std::optional<std::pair<unsigned, unsigned>> DI = getDepthAndIndex(U);
- DI && DI->first >= FriendDeclDepth) {
- auto *ND = dyn_cast<NamedDecl *>(U.first);
- if (!ND)
- ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
- Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
- << ND->getDeclName() << SourceRange(SS.getBeginLoc(), EllipsisLoc);
+ collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
+ if (EllipsisLoc.isInvalid()) {
+ if (DiagnoseUnexpandedParameterPack(TagLoc, TSI, UPPC_FriendDeclaration))
return true;
+ } else if (Unexpanded.empty()) {
+ Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
+ << TSI->getTypeLoc().getSourceRange();
+ return true;
+ } else {
+ // CWG 2917: a pack expanded by a friend-type-specifier cannot have been
+ // introduced by the template-declaration containing that specifier.
+ if (!TempParamLists.empty()) {
+ unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
+ for (UnexpandedParameterPack &U : Unexpanded) {
+ if (std::optional<std::pair<unsigned, unsigned>> DI =
+ getDepthAndIndex(U);
+ DI && DI->first >= FriendDeclDepth) {
+ auto *ND = dyn_cast<NamedDecl *>(U.first);
+ if (!ND)
+ ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
+ Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
+ << ND->getDeclName()
+ << SourceRange(TSI->getTypeLoc().getBeginLoc(), EllipsisLoc);
+ return true;
+ }
+ }
}
}
- // Handle the case of a templated-scope friend class. e.g.
- // template <class T> class A<T>::B;
- // FIXME: we don't support these right now.
- Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
- << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext);
- ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
- QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
- TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
- DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
- TL.setElaboratedKeywordLoc(TagLoc);
- TL.setQualifierLoc(SS.getWithLocInContext(Context));
- TL.setNameLoc(NameLoc);
+ FriendDecl *Friend;
+ if (TempParamLists.empty())
+ Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+ EllipsisLoc);
+ else {
+ if (CheckTemplateDeclScope(S, TempParamLists.back()))
+ return true;
+
+ TemplateName FriendTemplate;
+ if (TemplateParams)
+ FriendTemplate = Context.getDependentTemplateName(
+ {SS.getScopeRep(), Name, /*HasTemplateKeyword=*/false});
+ Friend =
+ FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+ TempParamLists, EllipsisLoc, FriendTemplate);
+ }
- FriendDecl *Friend =
- FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
- EllipsisLoc, TempParamLists);
Friend->setAccess(AS_public);
- Friend->setUnsupportedFriend(true);
CurContext->addDecl(Friend);
+
return Friend;
}
@@ -18343,11 +18508,14 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
// friend a member of an arbitrary specialization of your template).
Decl *D;
- if (!TempParams.empty())
+ if (!TempParams.empty()) {
+ if (CheckTemplateDeclScope(S, TempParams.back()))
+ return nullptr;
+
// TODO: Support variadic friend template decls?
- D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI,
- FriendLoc);
- else
+ D = FriendTemplateDecl::Create(Context, CurContext, Loc, TSI, FriendLoc,
+ TempParams, EllipsisLoc);
+ } else
D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
TSI, FriendLoc, EllipsisLoc);
@@ -18410,6 +18578,10 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
assert(NameInfo.getName());
+ if (SS.isValid() && DiagnosePackIndexingInFriendNNS(
+ NameInfo.getLoc(), SS.getWithLocInContext(Context)))
+ return nullptr;
+
// Check for unexpanded parameter packs.
if (DiagnoseUnexpandedParameterPack(Loc, TInfo, UPPC_FriendDeclaration) ||
DiagnoseUnexpandedParameterPack(NameInfo, UPPC_FriendDeclaration) ||
@@ -18424,7 +18596,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
Kind == NestedNameSpecifier::Kind::Namespace;
if (IsNamespaceOrGlobal) {
Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def)
- << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange());
+ << SS.getScopeRep();
SS.clear();
}
}
@@ -18535,11 +18707,8 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
diag::warn_cxx98_compat_friend_is_member :
diag::err_friend_is_member);
- // - There's a scope specifier that does not match any template
- // parameter lists, in which case we use some arbitrary context,
- // create a method or method template, and wait for instantiation.
- // - There's a scope specifier that does match some template
- // parameter lists, which we don't handle right now.
+ // - There's a dependent scope specifier, in which case we use an
+ // arbitrary context and wait for instantiation.
} else {
DC = CurContext;
assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
@@ -18613,81 +18782,88 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
PushOnScopeChains(ND, EnclosingScope, /*AddToContext=*/ false);
}
- FriendDecl *FrD = FriendDecl::Create(Context, CurContext,
- D.getIdentifierLoc(), ND,
- DS.getFriendSpecLoc());
- FrD->setAccess(AS_public);
- CurContext->addDecl(FrD);
+ warnOnReservedIdentifier(ND);
if (ND->isInvalidDecl()) {
- FrD->setInvalidDecl();
- } else {
- if (DC->isRecord()) CheckFriendAccess(ND);
+ FriendDecl *Friend = FriendDecl::Create(
+ Context, CurContext, D.getIdentifierLoc(), ND, DS.getFriendSpecLoc());
+ Friend->setAccess(AS_public);
+ if (!isa<FunctionTemplateDecl>(ND))
+ Friend->setInvalidDecl();
+ CurContext->addDecl(Friend);
+ return ND;
+ }
- FunctionDecl *FD;
- if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
- FD = FTD->getTemplatedDecl();
- else
- FD = cast<FunctionDecl>(ND);
-
- // C++ [class.friend]p6:
- // A function may be defined in a friend declaration of a class if and
- // only if the class is a non-local class, and the function name is
- // unqualified.
- if (D.isFunctionDefinition()) {
- // Qualified friend function definition.
- if (SS.isNotEmpty()) {
- // FIXME: We should only do this if the scope specifier names the
- // innermost enclosing namespace; otherwise the fixit changes the
- // meaning of the code.
- SemaDiagnosticBuilder DB =
- Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def);
-
- DB << SS.getScopeRep();
- if (DC->isFileContext())
- DB << FixItHint::CreateRemoval(SS.getRange());
-
- // Friend function defined in a local class.
- } else if (FunctionContainingLocalClass) {
- Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
-
- // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have
- // a template-id, the function name is not unqualified because these is
- // no name. While the wording requires some reading in-between the
- // lines, GCC, MSVC, and EDG all consider a friend function
- // specialization definitions to be de facto explicit specialization
- // and diagnose them as such.
- } else if (isTemplateId) {
- Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
- }
- }
+ FunctionDecl *FD = ND->getAsFunction();
+ assert(FD && "Expected a function declaration!");
- // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
- // default argument expression, that declaration shall be a definition
- // and shall be the only declaration of the function or function
- // template in the translation unit.
- if (functionDeclHasDefaultArgument(FD)) {
- // We can't look at FD->getPreviousDecl() because it may not have been set
- // if we're in a dependent context. If the function is known to be a
- // redeclaration, we will have narrowed Previous down to the right decl.
- if (D.isRedeclaration()) {
- Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_redeclared);
- Diag(Previous.getRepresentativeDecl()->getLocation(),
- diag::note_previous_declaration);
- } else if (!D.isFunctionDefinition())
- Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
- }
+ ArrayRef<TemplateParameterList *> TPLs = FD->getTemplateParameterLists();
+ if (!TPLs.empty() && SS.isValid() && CheckTemplateDeclScope(S, TPLs.back()))
+ return nullptr;
- // Mark templated-scope function declarations as unsupported.
- if (!FD->getTemplateParameterLists().empty() && SS.isValid()) {
- Diag(FD->getLocation(), diag::warn_template_qualified_friend_unsupported)
- << SS.getScopeRep() << SS.getRange()
- << cast<CXXRecordDecl>(CurContext);
- FrD->setUnsupportedFriend(true);
- }
- }
+ FriendDecl *Friend;
+ if (!TPLs.empty() && SS.isValid())
+ Friend =
+ FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
+ ND, DS.getFriendSpecLoc(), TPLs);
+ else
+ Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
+ DS.getFriendSpecLoc());
- warnOnReservedIdentifier(ND);
+ Friend->setAccess(AS_public);
+ CurContext->addDecl(Friend);
+
+ if (DC->isRecord())
+ CheckFriendAccess(ND);
+
+ if (!TemplateParams.empty() && SS.isValid() &&
+ CheckDependentFriend(NameInfo.getLoc(), SS.getWithLocInContext(Context),
+ FD->getTemplateParameterLists(),
+ /*IsInstantiation=*/false))
+ return ND;
+
+ // C++ [class.friend]p6:
+ // A function may be defined in a friend declaration of a class if and
+ // only if the class is a non-local class, and the function name is
+ // unqualified.
+ if (D.isFunctionDefinition()) {
+ // Qualified friend function definition.
+ if (SS.isNotEmpty()) {
+ SemaDiagnosticBuilder DB =
+ Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def);
+
+ DB << SS.getScopeRep();
+
+ // Friend function defined in a local class.
+ } else if (FunctionContainingLocalClass) {
+ Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
+
+ // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have
+ // a template-id, the function name is not unqualified because these is
+ // no name. While the wording requires some reading in-between the
+ // lines, GCC, MSVC, and EDG all consider a friend function
+ // specialization definitions to be de facto explicit specialization
+ // and diagnose them as such.
+ } else if (isTemplateId) {
+ Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
+ }
+ }
+
+ // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
+ // default argument expression, that declaration shall be a definition
+ // and shall be the only declaration of the function or function
+ // template in the translation unit.
+ if (functionDeclHasDefaultArgument(FD)) {
+ // We can't look at FD->getPreviousDecl() because it may not have been set
+ // if we're in a dependent context. If the function is known to be a
+ // redeclaration, we will have narrowed Previous down to the right decl.
+ if (D.isRedeclaration()) {
+ Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_redeclared);
+ Diag(Previous.getRepresentativeDecl()->getLocation(),
+ diag::note_previous_declaration);
+ } else if (!D.isFunctionDefinition())
+ Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
+ }
return ND;
}
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index d41137ddc85d5..daa4bb5234b7b 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -655,7 +655,8 @@ static bool CheckEquivalentExceptionSpecImpl(
return true;
}
- S.Diag(NewLoc, DiagID);
+ if (DiagID.getDiagID() != 0)
+ S.Diag(NewLoc, DiagID);
if (NoteID.getDiagID() != 0 && OldLoc.isValid())
S.Diag(OldLoc, NoteID);
return true;
@@ -667,7 +668,7 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
SourceLocation OldLoc,
const FunctionProtoType *New,
SourceLocation NewLoc) {
- if (!getLangOpts().CXXExceptions)
+ if (!getLangOpts().CXXExceptions && !getLangOpts().CPlusPlus17)
return false;
return CheckEquivalentExceptionSpecImpl(*this, DiagID, NoteID, Old, OldLoc,
New, NewLoc);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 97744b6e8836b..81bc6c68f377d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12420,8 +12420,9 @@ static TemplateDecl *getDescribedTemplate(Decl *Templated) {
/// Diagnose a failed template-argument deduction.
static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
DeductionFailureInfo &DeductionFailure,
- unsigned NumArgs,
- bool TakingCandidateAddress) {
+ unsigned NumArgs, bool TakingCandidateAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind =
+ TemplateSpecCandidateSetKind::Normal) {
TemplateParameter Param = DeductionFailure.getTemplateParameter();
NamedDecl *ParamD;
(ParamD = Param.dyn_cast<TemplateTypeParmDecl*>()) ||
@@ -12668,7 +12669,10 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
// name for types, not decls.
// Ideally, this should folded into the diagnostic printer.
S.Diag(Templated->getLocation(),
- diag::note_ovl_candidate_non_deduced_mismatch_qualified)
+ CandidateSetKind ==
+ TemplateSpecCandidateSetKind::FriendTemplate
+ ? diag::note_friend_template_non_deduced_mismatch_qualified
+ : diag::note_ovl_candidate_non_deduced_mismatch_qualified)
<< FirstTN.getAsTemplateDecl() << SecondTN.getAsTemplateDecl();
return;
}
@@ -12684,7 +12688,9 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
// diagnostic that mentions 'auto' and lambda in addition to
// (or instead of?) the canonical template type parameters.
S.Diag(Templated->getLocation(),
- diag::note_ovl_candidate_non_deduced_mismatch)
+ CandidateSetKind == TemplateSpecCandidateSetKind::FriendTemplate
+ ? diag::note_friend_template_non_deduced_mismatch
+ : diag::note_ovl_candidate_non_deduced_mismatch)
<< FirstTA << SecondTA;
return;
}
@@ -13635,10 +13641,12 @@ struct CompareTemplateSpecCandidatesForDisplay {
/// Diagnose a template argument deduction failure.
/// We are treating these failures as overload failures due to bad
/// deductions.
-void TemplateSpecCandidate::NoteDeductionFailure(Sema &S,
- bool ForTakingAddress) {
+void TemplateSpecCandidate::NoteDeductionFailure(
+ Sema &S, bool ForTakingAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind) {
DiagnoseBadDeduction(S, FoundDecl, Specialization, // pattern
- DeductionFailure, /*NumArgs=*/0, ForTakingAddress);
+ DeductionFailure, /*NumArgs=*/0, ForTakingAddress,
+ CandidateSetKind);
}
void TemplateSpecCandidateSet::destroyCandidates() {
@@ -13690,7 +13698,7 @@ void TemplateSpecCandidateSet::NoteCandidates(Sema &S, SourceLocation Loc) {
assert(Cand->Specialization &&
"Non-matching built-in candidates are not added to Cands.");
- Cand->NoteDeductionFailure(S, ForTakingAddress);
+ Cand->NoteDeductionFailure(S, ForTakingAddress, CandidateSetKind);
}
if (I != E)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 643392833759d..dff681454ae5a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1977,14 +1977,9 @@ DeclResult Sema::CheckClassTemplate(
if (SS.isNotEmpty() && !SS.isInvalid()) {
SemanticContext = computeDeclContext(SS, true);
if (!SemanticContext) {
- // FIXME: Horrible, horrible hack! We can't currently represent this
- // in the AST, and historically we have just ignored such friend
- // class templates, so don't complain here.
- Diag(NameLoc, TUK == TagUseKind::Friend
- ? diag::warn_template_qualified_friend_ignored
- : diag::err_template_qualified_declarator_no_match)
+ Diag(NameLoc, diag::err_template_qualified_declarator_no_match)
<< SS.getScopeRep() << SS.getRange();
- return TUK != TagUseKind::Friend;
+ return true;
}
if (RequireCompleteDeclContext(SS, SemanticContext))
@@ -3053,6 +3048,8 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier(
if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
ExpectedTemplateParams = Template->getTemplateParameters();
NeedNonemptyTemplateHeader = true;
+ } else if (Name.getAsDependentTemplateName()) {
+ NeedNonemptyTemplateHeader = true;
} else if (Name.getAsDeducedTemplateName()) {
// FIXME: We actually could/should check the template arguments here
// against the corresponding template parameter list.
@@ -9581,6 +9578,15 @@ bool Sema::CheckDependentFunctionTemplateSpecialization(
bool IsFriend = FD->getFriendObjectKind() != Decl::FOK_None;
if (Previous.empty()) {
+ NestedNameSpecifier FriendQualifier = FD->getQualifier();
+ if (IsFriend && FriendQualifier.isDependent() &&
+ FriendQualifier.getKind() == NestedNameSpecifier::Kind::Type &&
+ FriendQualifier.getAsType()->getAs<TemplateSpecializationType>()) {
+ FD->setDependentTemplateSpecialization(
+ Context, Previous.asUnresolvedSet(), ExplicitTemplateArgs);
+ return false;
+ }
+
Diag(FD->getLocation(), diag::err_dependent_function_template_spec_no_match)
<< IsFriend;
for (auto &P : DiscardedCandidates)
@@ -11203,6 +11209,10 @@ TypeResult Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (!NNS)
return true;
+ if (TUK == TagUseKind::Friend &&
+ DiagnosePackIndexingInFriendNNS(NameLoc, SS.getWithLocInContext(Context)))
+ return true;
+
TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec);
if (TUK == TagUseKind::Declaration || TUK == TagUseKind::Definition) {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 3c45806c47a6e..c57a090a8553e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3225,42 +3225,12 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
return TemplateDeductionResult::Success;
}
-/// Complete template argument deduction.
-static TemplateDeductionResult FinishTemplateArgumentDeduction(
- Sema &S, NamedDecl *Entity, TemplateParameterList *EntityTPL,
- TemplateDecl *Template, bool PartialOrdering,
- ArrayRef<TemplateArgumentLoc> Ps, ArrayRef<TemplateArgument> As,
- SmallVectorImpl<DeducedTemplateArgument> &Deduced,
- TemplateDeductionInfo &Info, bool CopyDeducedArgs) {
- Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Entity));
-
- // C++ [temp.deduct.type]p2:
- // [...] or if any template argument remains neither deduced nor
- // explicitly specified, template argument deduction fails.
- Sema::CheckTemplateArgumentInfo CTAI(PartialOrdering);
- if (auto Result = ConvertDeducedTemplateArguments(
- S, Entity, EntityTPL, /*IsDeduced=*/PartialOrdering, Deduced, Info,
- CTAI,
- /*CurrentInstantiationScope=*/nullptr,
- /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr);
- Result != TemplateDeductionResult::Success)
- return Result;
-
- if (CopyDeducedArgs) {
- // Form the template argument list from the deduced template arguments.
- TemplateArgumentList *SugaredDeducedArgumentList =
- TemplateArgumentList::CreateCopy(S.Context, CTAI.SugaredConverted);
- TemplateArgumentList *CanonicalDeducedArgumentList =
- TemplateArgumentList::CreateCopy(S.Context, CTAI.CanonicalConverted);
- Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList);
- }
-
+static TemplateDeductionResult CheckDeducedTemplateArgumentList(
+ Sema &S, TemplateDecl *Template, ArrayRef<TemplateArgumentLoc> Ps,
+ ArrayRef<TemplateArgument> As, const MultiLevelTemplateArgumentList &MLTAL,
+ TemplateDeductionInfo &Info) {
TemplateParameterList *TPL = Template->getTemplateParameters();
TemplateArgumentListInfo InstArgs(TPL->getLAngleLoc(), TPL->getRAngleLoc());
- MultiLevelTemplateArgumentList MLTAL(Entity, CTAI.SugaredConverted,
- /*Final=*/true);
- MLTAL.addOuterRetainedLevels(TPL->getDepth());
-
if (S.SubstTemplateArguments(Ps, MLTAL, InstArgs)) {
unsigned ArgIdx = InstArgs.size(), ParamIdx = ArgIdx;
if (ParamIdx >= TPL->size())
@@ -3336,6 +3306,46 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
}
assert(PsStack.empty());
assert(AsStack.empty());
+ return TemplateDeductionResult::Success;
+}
+
+/// Complete template argument deduction.
+static TemplateDeductionResult FinishTemplateArgumentDeduction(
+ Sema &S, NamedDecl *Entity, TemplateParameterList *EntityTPL,
+ TemplateDecl *Template, bool PartialOrdering,
+ ArrayRef<TemplateArgumentLoc> Ps, ArrayRef<TemplateArgument> As,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ TemplateDeductionInfo &Info, bool CopyDeducedArgs) {
+ Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Entity));
+
+ // C++ [temp.deduct.type]p2:
+ // [...] or if any template argument remains neither deduced nor
+ // explicitly specified, template argument deduction fails.
+ Sema::CheckTemplateArgumentInfo CTAI(PartialOrdering);
+ if (auto Result = ConvertDeducedTemplateArguments(
+ S, Entity, EntityTPL, /*IsDeduced=*/PartialOrdering, Deduced, Info,
+ CTAI,
+ /*CurrentInstantiationScope=*/nullptr,
+ /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr);
+ Result != TemplateDeductionResult::Success)
+ return Result;
+
+ if (CopyDeducedArgs) {
+ // Form the template argument list from the deduced template arguments.
+ TemplateArgumentList *SugaredDeducedArgumentList =
+ TemplateArgumentList::CreateCopy(S.Context, CTAI.SugaredConverted);
+ TemplateArgumentList *CanonicalDeducedArgumentList =
+ TemplateArgumentList::CreateCopy(S.Context, CTAI.CanonicalConverted);
+ Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList);
+ }
+
+ MultiLevelTemplateArgumentList MLTAL(Entity, CTAI.SugaredConverted,
+ /*Final=*/true);
+ MLTAL.addOuterRetainedLevels(Template->getTemplateParameters()->getDepth());
+ if (auto Result =
+ CheckDeducedTemplateArgumentList(S, Template, Ps, As, MLTAL, Info);
+ Result != TemplateDeductionResult::Success)
+ return Result;
if (!PartialOrdering) {
if (auto Result = CheckDeducedArgumentConstraints(
@@ -4112,6 +4122,147 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
return TemplateDeductionResult::Success;
}
+static void AddFriendTemplateDeductionCandidate(
+ Sema &S, TemplateDecl *TD, TemplateDeductionInfo &Info,
+ TemplateDeductionResult Result, TemplateSpecCandidateSet *FailedTSC) {
+ if (!FailedTSC)
+ return;
+
+ Decl *TemplatedDecl = TD->getTemplatedDecl();
+ for (TemplateSpecCandidate &Candidate : *FailedTSC) {
+ if (Candidate.Specialization &&
+ declaresSameEntity(Candidate.Specialization, TemplatedDecl))
+ return;
+ }
+
+ FailedTSC->addCandidate().set(
+ DeclAccessPair::make(TD, AS_public), TemplatedDecl,
+ MakeDeductionFailureInfo(S.Context, Result, Info));
+}
+
+bool Sema::DeduceTemplateArguments(
+ FriendTemplateDecl *FTD, ClassTemplateDecl *PatternCTD,
+ ClassTemplateDecl *CandidateCTD, ArrayRef<TemplateParameterList *> TPLs,
+ ArrayRef<TemplateArgument> PatternArgs,
+ ArrayRef<TemplateArgument> CandidateArgs, SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC,
+ MultiLevelTemplateArgumentList &DeducedArgs) {
+ EnterExpressionEvaluationContext Unevaluated(
+ *this, ExpressionEvaluationContext::Unevaluated);
+ ContextRAII SavedContext(*this, FTD->getDeclContext());
+ LocalInstantiationScope InstantiationScope(*this);
+ InstantiatingTemplate Inst(*this, Loc, FTD);
+ if (Inst.isInvalid()) {
+ TemplateDeductionInfo Info(Loc);
+ AddFriendTemplateDeductionCandidate(
+ *this, PatternCTD, Info, TemplateDeductionResult::InstantiationDepth,
+ FailedTSC);
+ return false;
+ }
+
+ SmallVector<TemplateArgumentList *, 2> DeducedArgLists;
+ DeducedArgLists.reserve(TPLs.size());
+ for (TemplateParameterList *Params : TPLs) {
+ TemplateDeductionInfo Info(Loc, Params->getDepth());
+ SFINAETrap Trap(*this, Info);
+ SmallVector<DeducedTemplateArgument, 4> Deduced(Params->size());
+ TemplateDeductionResult Result = DeduceTemplateArguments(
+ Params, PatternArgs, CandidateArgs, Info, Deduced,
+ /*NumberOfArgumentsMustMatch=*/false);
+
+ CheckTemplateArgumentInfo CTAI;
+ bool IsIncomplete = false;
+ if (Result == TemplateDeductionResult::Success)
+ Result = ConvertDeducedTemplateArguments(
+ *this, PatternCTD, Params, /*IsDeduced=*/false, Deduced, Info, CTAI,
+ &InstantiationScope, /*NumAlreadyConverted=*/0, &IsIncomplete);
+ if (Result == TemplateDeductionResult::Success && IsIncomplete) {
+ for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
+ if (!Deduced[I].isNull())
+ continue;
+ Info.Param = makeTemplateParameter(Params->getParam(I));
+ break;
+ }
+ Info.reset(
+ TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted),
+ TemplateArgumentList::CreateCopy(Context, CTAI.CanonicalConverted));
+ Result = TemplateDeductionResult::Incomplete;
+ }
+ if (Result == TemplateDeductionResult::Success && Trap.hasErrorOccurred())
+ Result = TemplateDeductionResult::SubstitutionFailure;
+ if (Result != TemplateDeductionResult::Success) {
+ AddFriendTemplateDeductionCandidate(*this, PatternCTD, Info, Result,
+ FailedTSC);
+ return false;
+ }
+
+ DeducedArgLists.push_back(
+ TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted));
+ }
+
+ for (TemplateArgumentList *Args : llvm::reverse(DeducedArgLists))
+ DeducedArgs.addOuterTemplateArguments(FTD, Args->asArray(),
+ /*Final=*/true);
+ if (!TPLs.empty())
+ DeducedArgs.addOuterRetainedLevels(TPLs.front()->getDepth());
+
+ if (DeducedArgs.isAnyArgInstantiationDependent() &&
+ llvm::any_of(TPLs, [](TemplateParameterList *Params) {
+ return Params->hasAssociatedConstraints();
+ }))
+ return false;
+
+ SmallVector<TemplateArgumentLoc, 8> PatternArgLocs;
+ PatternArgLocs.reserve(PatternArgs.size());
+ for (const TemplateArgument &Arg : PatternArgs)
+ PatternArgLocs.push_back(
+ getTrivialTemplateArgumentLoc(Arg, QualType(), Loc));
+
+ {
+ TemplateDeductionInfo Info(Loc);
+ SFINAETrap Trap(*this, Info);
+ TemplateDeductionResult Result = CheckDeducedTemplateArgumentList(
+ *this, CandidateCTD, PatternArgLocs, CandidateArgs, DeducedArgs, Info);
+ if (Result == TemplateDeductionResult::Success && Trap.hasErrorOccurred())
+ Result = TemplateDeductionResult::SubstitutionFailure;
+ if (Result != TemplateDeductionResult::Success) {
+ AddFriendTemplateDeductionCandidate(*this, PatternCTD, Info, Result,
+ FailedTSC);
+ return false;
+ }
+ }
+
+ for (TemplateParameterList *Params : TPLs) {
+ SmallVector<AssociatedConstraint, 3> Constraints;
+ Params->getAssociatedConstraints(Constraints);
+ if (Constraints.empty())
+ continue;
+
+ TemplateDeductionInfo Info(Loc, Params->getDepth());
+ SFINAETrap Trap(*this, Info);
+ if (CheckConstraintSatisfaction(PatternCTD, Constraints, DeducedArgs,
+ SourceRange(Loc),
+ Info.AssociatedConstraintsSatisfaction) ||
+ !Info.AssociatedConstraintsSatisfaction.IsSatisfied ||
+ Trap.hasErrorOccurred()) {
+ SmallVector<TemplateArgument, 4> CanonicalCandidateArgs;
+ CanonicalCandidateArgs.reserve(CandidateArgs.size());
+ for (const TemplateArgument &Arg : CandidateArgs)
+ CanonicalCandidateArgs.push_back(
+ Context.getCanonicalTemplateArgument(Arg));
+ Info.reset(
+ TemplateArgumentList::CreateCopy(Context, CandidateArgs),
+ TemplateArgumentList::CreateCopy(Context, CanonicalCandidateArgs));
+ AddFriendTemplateDeductionCandidate(
+ *this, PatternCTD, Info,
+ TemplateDeductionResult::ConstraintsNotSatisfied, FailedTSC);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/// Gets the type of a function for template-argument-deducton
/// purposes when it's considered as part of an overload set.
static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R,
@@ -7196,11 +7347,14 @@ Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
}
void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
- unsigned Depth,
+ bool OnlyDeduced, unsigned Depth,
llvm::SmallBitVector &Used) {
+ if (OnlyDeduced && hasPackExpansionBeforeEnd(TemplateArgs))
+ return;
+
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
- ::MarkUsedTemplateParameters(Context, TemplateArgs[I],
- /*OnlyDeduced=*/false, Depth, Used);
+ ::MarkUsedTemplateParameters(Context, TemplateArgs[I], OnlyDeduced, Depth,
+ Used);
}
void Sema::MarkUsedTemplateParameters(
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 3f37b59807f85..40c5a6181c51d 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -889,14 +889,15 @@ SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList(
ArrayRef<TemplateArgument> DeducedArgs) {
llvm::SmallBitVector ReferencedTemplateParams(TemplateParamsList->size());
- SemaRef.MarkUsedTemplateParameters(
- DeducedArgs, TemplateParamsList->getDepth(), ReferencedTemplateParams);
+ SemaRef.MarkUsedTemplateParameters(DeducedArgs, /*OnlyDeduced=*/false,
+ TemplateParamsList->getDepth(),
+ ReferencedTemplateParams);
auto MarkDefaultArgs = [&](auto *Param) {
if (!Param->hasDefaultArgument())
return;
SemaRef.MarkUsedTemplateParameters(
- Param->getDefaultArgument().getArgument(),
+ Param->getDefaultArgument().getArgument(), /*OnlyDeduced=*/false,
TemplateParamsList->getDepth(), ReferencedTemplateParams);
};
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 79d106168e9d0..a21f65f687568 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2006,68 +2006,247 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
return IndirectField;
}
-Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
- // Handle friend type expressions by simply substituting template
- // parameters into the pattern type and checking the result.
- if (TypeSourceInfo *Ty = D->getFriendType()) {
- TypeSourceInfo *InstTy;
- // If this is an unsupported friend, don't bother substituting template
- // arguments into it. The actual type referred to won't be used by any
- // parts of Clang, and may not be valid for instantiating. Just use the
- // same info for the instantiated friend.
- if (D->isUnsupportedFriend()) {
- InstTy = Ty;
- } else {
- if (D->isPackExpansion()) {
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- SemaRef.collectUnexpandedParameterPacks(Ty->getTypeLoc(), Unexpanded);
- assert(!Unexpanded.empty() && "Pack expansion without packs");
-
- bool ShouldExpand = true;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = std::nullopt;
- if (SemaRef.CheckParameterPacksForExpansion(
- D->getEllipsisLoc(), D->getSourceRange(), Unexpanded,
- TemplateArgs, /*FailOnPackProducingTemplates=*/true,
- ShouldExpand, RetainExpansion, NumExpansions))
- return nullptr;
+static std::optional<TemplateName>
+LookupFriendTemplateName(Sema &SemaRef, NestedNameSpecifierLoc QualifierLoc,
+ DeclarationName Name, SourceLocation NameLoc,
+ bool HasTemplateKeyword, bool RequireClassTemplate) {
+ if (!QualifierLoc)
+ return TemplateName();
- assert(!RetainExpansion &&
- "should never retain an expansion for a variadic friend decl");
-
- if (ShouldExpand) {
- SmallVector<FriendDecl *> Decls;
- for (unsigned I = 0; I != *NumExpansions; I++) {
- Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
- TypeSourceInfo *TSI = SemaRef.SubstType(
- Ty, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
- if (!TSI)
- return nullptr;
-
- auto FD =
- FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
- TSI, D->getFriendLoc());
-
- FD->setAccess(AS_public);
- Owner->addDecl(FD);
- Decls.push_back(FD);
- }
+ CXXScopeSpec SS;
+ SS.Adopt(QualifierLoc);
- // Just drop this node; we have no use for it anymore.
- return nullptr;
- }
- }
+ DeclContext *DC = SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
+ if (!DC) {
+ if (QualifierLoc.getNestedNameSpecifier().isDependent())
+ return TemplateName();
+ return std::nullopt;
+ }
+
+ bool IsDependentContext = DC->isDependentContext();
+ if (!IsDependentContext && SemaRef.RequireCompleteDeclContext(SS, DC))
+ return std::nullopt;
+
+ LookupResult Result(SemaRef, Name, NameLoc, Sema::LookupOrdinaryName,
+ SemaRef.forRedeclarationInCurContext());
+ if (!SemaRef.LookupQualifiedName(Result, DC)) {
+ if (RequireClassTemplate && !IsDependentContext) {
+ SemaRef.Diag(NameLoc, diag::err_no_member_template)
+ << Name << DC << QualifierLoc.getSourceRange();
+ return std::nullopt;
+ }
+ return TemplateName();
+ }
+
+ if (Result.isAmbiguous())
+ return std::nullopt;
+
+ auto *CTD = Result.getAsSingle<ClassTemplateDecl>();
+ if (!CTD) {
+ if (RequireClassTemplate && !IsDependentContext) {
+ SemaRef.Diag(NameLoc, diag::err_redefinition_
diff erent_kind) << Name;
+ SemaRef.Diag(
+ Result.getRepresentativeDecl()->getUnderlyingDecl()->getLocation(),
+ diag::note_previous_definition);
+ return std::nullopt;
+ }
+ return TemplateName();
+ }
+
+ auto *FoundUsingShadow =
+ dyn_cast<UsingShadowDecl>(Result.getRepresentativeDecl());
+
+ return SemaRef.Context.getQualifiedTemplateName(
+ QualifierLoc.getNestedNameSpecifier(), HasTemplateKeyword,
+ FoundUsingShadow ? TemplateName(FoundUsingShadow) : TemplateName(CTD));
+}
+
+TypeSourceInfo *
+Sema::SubstFriendType(TypeSourceInfo *TSI,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ SourceLocation Loc, DeclarationName Entity) {
+ TemplateSpecializationTypeLoc TSTL =
+ TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
+ NestedNameSpecifierLoc QualifierLoc =
+ TSTL ? TSTL.getQualifierLoc() : NestedNameSpecifierLoc();
+ if (!TSTL || !QualifierLoc ||
+ !QualifierLoc.getNestedNameSpecifier().isDependent())
+ return SubstType(TSI, TemplateArgs, Loc, Entity);
+
+ const auto *FriendTST = TSTL.getTypePtr();
+ auto *FriendCTD = dyn_cast_or_null<ClassTemplateDecl>(
+ FriendTST->getTemplateName().getAsTemplateDecl());
+ if (!FriendCTD)
+ return SubstType(TSI, TemplateArgs, Loc, Entity);
+
+ QualifierLoc = SubstNestedNameSpecifierLoc(QualifierLoc, TemplateArgs);
+ if (!QualifierLoc)
+ return nullptr;
+
+ std::optional<TemplateName> InstTemplate = LookupFriendTemplateName(
+ *this, QualifierLoc, FriendCTD->getDeclName(), TSTL.getTemplateNameLoc(),
+ TSTL.getTemplateKeywordLoc().isValid(),
+ /*RequireClassTemplate=*/false);
+ if (!InstTemplate)
+ return nullptr;
+ if (InstTemplate->isNull())
+ return SubstType(TSI, TemplateArgs, Loc, Entity);
+
+ SmallVector<TemplateArgumentLoc, 4> FriendArgLocs;
+ for (unsigned I = 0, N = TSTL.getNumArgs(); I != N; ++I)
+ FriendArgLocs.push_back(TSTL.getArgLoc(I));
+
+ TemplateArgumentListInfo InstArgs(TSTL.getLAngleLoc(), TSTL.getRAngleLoc());
+ if (SubstTemplateArguments(FriendArgLocs, TemplateArgs, InstArgs))
+ return nullptr;
+
+ QualType InstTy =
+ CheckTemplateIdType(FriendTST->getKeyword(), *InstTemplate,
+ TSTL.getTemplateNameLoc(), InstArgs,
+ /*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false);
+ if (InstTy.isNull())
+ return nullptr;
+
+ TypeLocBuilder TLB;
+ TLB.push<TemplateSpecializationTypeLoc>(InstTy).set(
+ TSTL.getElaboratedKeywordLoc(), QualifierLoc,
+ TSTL.getTemplateKeywordLoc(), TSTL.getTemplateNameLoc(), InstArgs);
+ return TLB.getTypeSourceInfo(Context, InstTy);
+}
+
+struct SubstitutedFriend {
+ TypeSourceInfo *TypeInfo = nullptr;
+ TemplateName TemplateName;
+
+ bool empty() const { return !TypeInfo && TemplateName.isNull(); }
+};
+
+static std::optional<SubstitutedFriend>
+SubstFriendTemplateType(Sema &SemaRef, TypeSourceInfo *TSI,
+ TemplateName FriendTemplate,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ SourceLocation Loc, DeclarationName Entity) {
+ NestedNameSpecifierLoc QualifierLoc = TSI->getTypeLoc().getPrefix();
+ NestedNameSpecifierLoc InstQualifierLoc = QualifierLoc;
+ if (QualifierLoc && QualifierLoc.getNestedNameSpecifier().isDependent()) {
+ InstQualifierLoc =
+ SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc, TemplateArgs);
+ if (!InstQualifierLoc ||
+ SemaRef.CheckDependentFriend(Loc, InstQualifierLoc, /*TPLs=*/{},
+ /*IsInstantiation=*/true))
+ return std::nullopt;
+ }
+
+ TemplateName InstFriendTemplate;
+ if (!FriendTemplate.isNull()) {
+ auto DNTL = TSI->getTypeLoc().getAs<DependentNameTypeLoc>();
+ assert(DNTL && "friend class template must have a dependent name type");
+
+ std::optional<TemplateName> InstTemplate = LookupFriendTemplateName(
+ SemaRef, InstQualifierLoc, DNTL.getTypePtr()->getIdentifier(),
+ DNTL.getNameLoc(), /*HasTemplateKeyword=*/false,
+ /*RequireClassTemplate=*/true);
+ if (!InstTemplate)
+ return std::nullopt;
+ if (!InstTemplate->isNull())
+ return SubstitutedFriend{nullptr, *InstTemplate};
+
+ auto *DTN = FriendTemplate.getAsDependentTemplateName();
+ assert(DTN && "unresolved friend template must have a dependent name");
+ InstFriendTemplate = SemaRef.Context.getDependentTemplateName(
+ {InstQualifierLoc.getNestedNameSpecifier(), DTN->getName(),
+ DTN->hasTemplateKeyword()});
+ }
+
+ TypeSourceInfo *InstType =
+ SemaRef.SubstFriendType(TSI, TemplateArgs, Loc, Entity);
+ if (!InstType)
+ return std::nullopt;
+ return SubstitutedFriend{InstType, InstFriendTemplate};
+}
+
+bool TemplateDeclInstantiator::InstantiateFriendPackExpansion(FriendDecl *D) {
+ TypeSourceInfo *TSI = D->getFriendType();
+ assert(TSI && "friend pack expansion must name a type");
+
+ const auto *FTD = dyn_cast<FriendTemplateDecl>(D);
+ ArrayRef<TemplateParameterList *> TPLs;
+ if (FTD)
+ TPLs = FTD->getTemplateParameterLists();
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ SemaRef.collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without packs");
+
+ bool ShouldExpand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions = std::nullopt;
+ if (SemaRef.CheckParameterPacksForExpansion(
+ D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
+ /*FailOnPackProducingTemplates=*/true, ShouldExpand, RetainExpansion,
+ NumExpansions))
+ return true;
+
+ assert(!RetainExpansion &&
+ "should never retain an expansion for a friend declaration");
+
+ if (!ShouldExpand)
+ return false;
+
+ for (unsigned I = 0; I != *NumExpansions; I++) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ SmallVector<TemplateParameterList *, 1> InstTPLs;
+ if (SubstTemplateParameterLists(TPLs, InstTPLs))
+ return true;
- InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
- DeclarationName());
+ std::optional<SubstitutedFriend> InstFriend;
+ if (FTD)
+ InstFriend = SubstFriendTemplateType(
+ SemaRef, TSI, FTD->getFriendTemplateName(), TemplateArgs,
+ D->getEllipsisLoc(), DeclarationName());
+ else if (TypeSourceInfo *InstType = SemaRef.SubstFriendType(
+ TSI, TemplateArgs, D->getEllipsisLoc(), DeclarationName()))
+ InstFriend = SubstitutedFriend{InstType, {}};
+ if (!InstFriend || InstFriend->empty())
+ return true;
+
+ FriendDecl *FD;
+ if (FTD) {
+ FriendDecl::FriendUnion ToFriend =
+ InstFriend->TypeInfo ? FriendDecl::FriendUnion(InstFriend->TypeInfo)
+ : FriendDecl::FriendUnion();
+ FD = FriendTemplateDecl::Create(
+ SemaRef.Context, Owner, D->getLocation(), ToFriend, D->getFriendLoc(),
+ InstTPLs, /*EllipsisLoc=*/{}, InstFriend->TemplateName);
+ } else {
+ assert(InstTPLs.empty() && "unexpected template parameter lists");
+ assert(InstFriend->TemplateName.isNull() &&
+ "non-template friend resolved to a class template");
+ FD = FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ InstFriend->TypeInfo, D->getFriendLoc());
}
+
+ FD->setAccess(AS_public);
+ Owner->addDecl(FD);
+ }
+
+ return true;
+}
+
+Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
+ if (TypeSourceInfo *Ty = D->getFriendType()) {
+ if (D->isPackExpansion() && InstantiateFriendPackExpansion(D))
+ return nullptr;
+
+ TypeSourceInfo *InstTy = SemaRef.SubstFriendType(
+ Ty, TemplateArgs, D->getLocation(), DeclarationName());
if (!InstTy)
return nullptr;
FriendDecl *FD = FriendDecl::Create(
SemaRef.Context, Owner, D->getLocation(), InstTy, D->getFriendLoc());
FD->setAccess(AS_public);
- FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}
@@ -2086,7 +2265,6 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
cast<NamedDecl>(NewND), D->getFriendLoc());
FD->setAccess(AS_public);
- FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}
@@ -2851,6 +3029,12 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
if (!QualifierLoc)
return nullptr;
}
+ if (isFriend &&
+ (FunctionTemplate || !D->getTemplateParameterLists().empty()) &&
+ D->getQualifier().isDependent() &&
+ SemaRef.CheckDependentFriend(D->getLocation(), QualifierLoc,
+ /*TPLs=*/{}, /*IsInstantiation=*/true))
+ return nullptr;
AssociatedConstraint TrailingRequiresClause = D->getTrailingRequiresClause();
@@ -3265,17 +3449,23 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (!QualifierLoc)
return nullptr;
}
+ if (isFriend &&
+ (FunctionTemplate || !D->getTemplateParameterLists().empty()) &&
+ D->getQualifier().isDependent() &&
+ SemaRef.CheckDependentFriend(D->getLocation(), QualifierLoc,
+ /*TPLs=*/{}, /*IsInstantiation=*/true))
+ return nullptr;
DeclContext *DC = Owner;
if (isFriend) {
- if (QualifierLoc) {
+ if (QualifierLoc && !QualifierLoc.getNestedNameSpecifier().isDependent()) {
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
DC = SemaRef.computeDeclContext(SS);
if (DC && SemaRef.RequireCompleteDeclContext(SS, DC))
return nullptr;
- } else {
+ } else if (!QualifierLoc) {
DC = SemaRef.FindInstantiatedContext(D->getLocation(),
D->getDeclContext(),
TemplateArgs);
@@ -3423,12 +3613,22 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
return nullptr;
}
- if (SemaRef.CheckFunctionTemplateSpecialization(
- Method, DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
- Previous))
- Method->setInvalidDecl();
-
- IsExplicitSpecialization = true;
+ if (QualifierLoc && QualifierLoc.getNestedNameSpecifier().isDependent()) {
+ if (SemaRef.CheckDependentFunctionTemplateSpecialization(
+ Method,
+ DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
+ Previous))
+ Method->setInvalidDecl();
+ } else {
+ if (Previous.empty())
+ SemaRef.LookupQualifiedName(Previous, DC);
+ if (SemaRef.CheckFunctionTemplateSpecialization(
+ Method,
+ DFTSI->TemplateArgumentsAsWritten ? &ExplicitArgs : nullptr,
+ Previous))
+ Method->setInvalidDecl();
+ IsExplicitSpecialization = true;
+ }
} else if (const ASTTemplateArgumentListInfo *ArgsWritten =
D->getTemplateSpecializationArgsAsWritten()) {
SemaRef.LookupQualifiedName(Previous, DC);
@@ -4772,14 +4972,48 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
}
Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
- // FIXME: We need to be able to instantiate FriendTemplateDecls.
- unsigned DiagID = SemaRef.getDiagnostics().getCustomDiagID(
- DiagnosticsEngine::Error,
- "cannot instantiate %0 yet");
- SemaRef.Diag(D->getLocation(), DiagID)
- << D->getDeclKindName();
+ ArrayRef<TemplateParameterList *> FriendTPLs = D->getTemplateParameterLists();
- return nullptr;
+ TypeSourceInfo *FriendTSI = D->getFriendType();
+ if (FriendTSI && D->isPackExpansion() && InstantiateFriendPackExpansion(D))
+ return nullptr;
+
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ SmallVector<TemplateParameterList *, 1> InstTPLs;
+ if (SubstTemplateParameterLists(FriendTPLs, InstTPLs))
+ return nullptr;
+
+ FriendDecl::FriendUnion ToFriend;
+ TemplateName ToTemplate;
+ if (FriendTSI) {
+ std::optional<SubstitutedFriend> Substituted = SubstFriendTemplateType(
+ SemaRef, FriendTSI, D->getFriendTemplateName(), TemplateArgs,
+ D->getLocation(), DeclarationName());
+ if (!Substituted || Substituted->empty())
+ return nullptr;
+ ToFriend = Substituted->TypeInfo;
+ ToTemplate = Substituted->TemplateName;
+ } else if (!D->getFriendTemplateName().isNull()) {
+ if (auto *InstTemplate =
+ cast_or_null<TemplateDecl>(Visit(D->getFriendDecl())))
+ ToTemplate = TemplateName(InstTemplate);
+ else
+ return nullptr;
+ } else {
+ if (auto *InstFriendDecl =
+ cast_or_null<NamedDecl>(Visit(D->getFriendDecl())))
+ ToFriend = InstFriendDecl;
+ else
+ return nullptr;
+ }
+
+ FriendTemplateDecl *InstFriend = FriendTemplateDecl::Create(
+ SemaRef.Context, Owner, D->getLocation(), ToFriend, D->getFriendLoc(),
+ InstTPLs, /*EllipsisLoc=*/{}, ToTemplate);
+
+ InstFriend->setAccess(AS_public);
+ Owner->addDecl(InstFriend);
+ return InstFriend;
}
Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) {
@@ -4924,6 +5158,33 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
return InstL;
}
+bool TemplateDeclInstantiator::SubstTemplateParameterLists(
+ ArrayRef<TemplateParameterList *> TPLs,
+ SmallVectorImpl<TemplateParameterList *> &InstTPLs) {
+ llvm::SaveAndRestore RAII(EvaluateConstraints, false);
+ for (TemplateParameterList *L : TPLs) {
+ TemplateParameterList *InstParams = SubstTemplateParams(L);
+ if (!InstParams)
+ return true;
+
+ if (Expr *RequiresClause = L->getRequiresClause()) {
+ ExprResult InstRequiresClause =
+ SemaRef.SubstConstraintExprWithoutSatisfaction(RequiresClause,
+ TemplateArgs);
+ if (!InstRequiresClause.isUsable())
+ return true;
+
+ InstParams = TemplateParameterList::Create(
+ SemaRef.Context, InstParams->getTemplateLoc(),
+ InstParams->getLAngleLoc(), InstParams->asArray(),
+ InstParams->getRAngleLoc(), InstRequiresClause.get());
+ }
+
+ InstTPLs.push_back(InstParams);
+ }
+ return false;
+}
+
TemplateParameterList *
Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -5166,9 +5427,8 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
return InstPartialSpec;
}
-TypeSourceInfo*
-TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
- SmallVectorImpl<ParmVarDecl *> &Params) {
+TypeSourceInfo *TemplateDeclInstantiator::SubstFunctionType(
+ FunctionDecl *D, SmallVectorImpl<ParmVarDecl *> &Params) {
TypeSourceInfo *OldTInfo = D->getTypeSourceInfo();
assert(OldTInfo && "substituting function without type source info");
assert(Params.empty() && "parameter vector is non-empty at start");
@@ -5237,8 +5497,10 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
continue;
}
- ParmVarDecl *Parm =
- cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam));
+ ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
+ OldParam, TemplateArgs, /*indexAdjustment=*/0,
+ /*NumExpansions=*/std::nullopt,
+ /*ExpectParameterPack=*/false, EvaluateConstraints);
if (!Parm)
return nullptr;
Params.push_back(Parm);
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index bec62e7c55606..e973b7ae71954 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2408,26 +2408,37 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
D->Friend = readDeclAs<NamedDecl>();
else
D->Friend = readTypeSourceInfo();
- for (unsigned i = 0; i != D->NumTPLists; ++i)
- D->getTrailingObjects()[i] = Record.readTemplateParameterList();
D->NextFriend = readDeclID().getRawValue();
- D->UnsupportedFriend = (Record.readInt() != 0);
D->FriendLoc = readSourceLocation();
D->EllipsisLoc = readSourceLocation();
}
void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
VisitDecl(D);
- unsigned NumParams = Record.readInt();
- D->NumParams = NumParams;
- D->Params = new (Reader.getContext()) TemplateParameterList *[NumParams];
- for (unsigned i = 0; i != NumParams; ++i)
- D->Params[i] = Record.readTemplateParameterList();
- if (Record.readInt()) // HasFriendDecl
+ for (unsigned I = 0; I != D->NumTPLists; ++I)
+ D->getTrailingObjects()[I] = Record.readTemplateParameterList();
+ auto Kind = static_cast<FriendTemplateDeclKind>(Record.readInt());
+ switch (Kind) {
+ case FTDK_Type:
+ D->Friend = readTypeSourceInfo();
+ break;
+ case FTDK_Decl:
D->Friend = readDeclAs<NamedDecl>();
- else
+ break;
+ case FTDK_Template:
+ D->Template = Record.readTemplateName();
+ assert(D->Template.getAsTemplateDecl() &&
+ "friend template name must resolve to a template declaration");
+ D->Friend = D->Template.getAsTemplateDecl();
+ break;
+ case FTDK_Dependent:
D->Friend = readTypeSourceInfo();
+ D->Template = Record.readTemplateName();
+ break;
+ }
+ D->NextFriend = readDeclID().getRawValue();
D->FriendLoc = readSourceLocation();
+ D->EllipsisLoc = readSourceLocation();
}
void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4104,10 +4115,11 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
D = AccessSpecDecl::CreateDeserialized(Context, ID);
break;
case DECL_FRIEND:
- D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
+ D = FriendDecl::CreateDeserialized(Context, ID);
break;
case DECL_FRIEND_TEMPLATE:
- D = FriendTemplateDecl::CreateDeserialized(Context, ID);
+ D = FriendTemplateDecl::CreateDeserialized(Context, ID,
+ /*NumTPLists=*/Record.readInt());
break;
case DECL_CLASS_TEMPLATE:
D = ClassTemplateDecl::CreateDeserialized(Context, ID);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index d8f5ed9afdde1..6f67acf9a6e7e 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1831,9 +1831,6 @@ void ASTDeclWriter::VisitAccessSpecDecl(AccessSpecDecl *D) {
}
void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
- // Record the number of friend type template parameter lists here
- // so as to simplify memory allocation during deserialization.
- Record.push_back(D->NumTPLists);
VisitDecl(D);
bool hasFriendDecl = isa<NamedDecl *>(D->Friend);
Record.push_back(hasFriendDecl);
@@ -1841,26 +1838,47 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
Record.AddDeclRef(D->getFriendDecl());
else
Record.AddTypeSourceInfo(D->getFriendType());
- for (unsigned i = 0; i < D->NumTPLists; ++i)
- Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
Record.AddDeclRef(D->getNextFriend());
- Record.push_back(D->UnsupportedFriend);
Record.AddSourceLocation(D->FriendLoc);
Record.AddSourceLocation(D->EllipsisLoc);
Code = serialization::DECL_FRIEND;
}
void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ // Record the number of template parameter lists here to simplify memory
+ // allocation during deserialization.
+ Record.push_back(D->NumTPLists);
VisitDecl(D);
- Record.push_back(D->getNumTemplateParameters());
- for (unsigned i = 0, e = D->getNumTemplateParameters(); i != e; ++i)
- Record.AddTemplateParameterList(D->getTemplateParameterList(i));
- Record.push_back(D->getFriendDecl() != nullptr);
- if (D->getFriendDecl())
- Record.AddDeclRef(D->getFriendDecl());
- else
+ for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+ Record.AddTemplateParameterList(TPL);
+ FriendTemplateDeclKind Kind;
+ if (D->getFriendType()) {
+ Kind = D->Template.isNull() ? FTDK_Type : FTDK_Dependent;
+ } else if (D->Template.isNull()) {
+ assert(D->getFriendDecl());
+ Kind = FTDK_Decl;
+ } else {
+ Kind = FTDK_Template;
+ }
+ Record.push_back(Kind);
+ switch (Kind) {
+ case FTDK_Type:
+ Record.AddTypeSourceInfo(D->getFriendType());
+ break;
+ case FTDK_Dependent:
Record.AddTypeSourceInfo(D->getFriendType());
- Record.AddSourceLocation(D->getFriendLoc());
+ Record.AddTemplateName(D->Template);
+ break;
+ case FTDK_Decl:
+ Record.AddDeclRef(D->getFriendDecl());
+ break;
+ case FTDK_Template:
+ Record.AddTemplateName(D->Template);
+ break;
+ }
+ Record.AddDeclRef(D->getNextFriend());
+ Record.AddSourceLocation(D->FriendLoc);
+ Record.AddSourceLocation(D->EllipsisLoc);
Code = serialization::DECL_FRIEND_TEMPLATE;
}
diff --git a/clang/test/AST/friend-template-decl.cpp b/clang/test/AST/friend-template-decl.cpp
new file mode 100644
index 0000000000000..c9816771991b9
--- /dev/null
+++ b/clang/test/AST/friend-template-decl.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=c++20 -ast-print %s | FileCheck %s --check-prefix=PRINT
+// RUN: %clang_cc1 -std=c++20 -ast-dump -ast-dump-filter C %s | FileCheck %s --check-prefix=DUMP
+// RUN: %clang_cc1 -std=c++20 -ast-dump=json -ast-dump-filter C %s | FileCheck %s --check-prefix=JSON
+// RUN: %clang_cc1 -std=c++20 -x c++-header -emit-pch -o %t %s
+// RUN: %clang_cc1 -std=c++20 -x c++ -include-pch %t -ast-dump-all -ast-dump-filter C /dev/null | FileCheck %s --check-prefix=DUMP
+
+namespace N {
+template <class T> struct A {
+ template <class U> struct B;
+};
+
+template <class V> struct C {
+ template <class T> template <class U> friend struct A<T>::B;
+};
+
+template struct C<int>;
+}
+
+// PRINT: template<> struct C<int> {
+// PRINT-NEXT: template <class T> template <class U> friend struct A<T>::B;
+
+// DUMP: ClassTemplateDecl {{.*}} C
+// DUMP: FriendTemplateDecl {{.*}} 'struct A<T>::B'
+// DUMP-NEXT: |-TemplateTypeParmDecl {{.*}} T
+// DUMP-NEXT: `-TemplateTypeParmDecl {{.*}} U
+// DUMP: ClassTemplateSpecializationDecl {{.*}} struct C definition
+// DUMP-NOT: <<<NULL>>>
+// DUMP: FriendTemplateDecl {{.*}} qualified
+// DUMP-NEXT: |-NestedNameSpecifier TypeSpec 'A<T>'
+// DUMP-NEXT: |-ClassTemplateDecl
+// DUMP-NEXT: |-TemplateTypeParmDecl {{.*}} T
+// DUMP-NEXT: `-TemplateTypeParmDecl {{.*}} U
+
+// JSON: "kind": "ClassTemplateSpecializationDecl",
+// JSON: "kind": "FriendTemplateDecl",
+// JSON-NEXT: "loc": {
+// JSON: "templateName": "A<T>::B"
diff --git a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
index f7216ea7eb7b0..6c55e81c58c18 100644
--- a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
+++ b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
@@ -36,7 +36,7 @@ class A {
public:
class foo {};
static int y;
- template <typename S> friend class B<S>::ty; // expected-warning {{dependent nested name specifier 'B<S>' for friend class declaration is not supported}}
+ template <typename S> friend class B<S>::ty;
};
template<typename T> class B { typedef int ty; };
@@ -74,7 +74,7 @@ struct {
friend
float;
- template<typename T> friend class A<T>::foo; // expected-warning {{not supported}}
+ template<typename T> friend class A<T>::foo;
} a;
void testA() { (void)sizeof(A<int>); }
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index 646b82eb40fd7..32ff4d72d30f3 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -375,7 +375,7 @@ namespace cwg1837 { // cwg1837: 3.3
#endif
} // namespace cwg1837
-namespace cwg1862 { // cwg1862: no
+namespace cwg1862 { // cwg1862: 24
template<class T>
struct A {
struct B {
@@ -416,29 +416,24 @@ struct A<float*> {
};
class C {
- int private_int;
+ int private_int; // #cwg1862-C-private_int
template<class T>
friend struct A<T>::B;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
template<class T>
friend void A<T>::f();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
- // FIXME: this is ill-formed, because A<T>​::​D does not end with a simple-template-id
template<class T>
friend void A<T>::D::g();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::D' for friend class declaration is not supported; turning off access control for 'C'}}
+ // expected-error at -1 {{'A<T>::D' does not name a class template}}
template<class T>
friend int *A<T*>::h();
- // expected-warning at -1 {{dependent nested name specifier 'A<T *>' for friend class declaration is not supported; turning off access control for 'C'}}
template<class T>
template<T U>
friend T A<T>::i();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
};
C c;
@@ -450,11 +445,16 @@ void A<int>::B::e() { (void)c.private_int; }
template<class T>
void A<T>::f() { (void)c.private_int; }
int A<int>::f() { (void)c.private_int; return 0; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
-// FIXME: both definition of 'D::g' are not friends, so they don't have access to 'private_int'
template<class T>
void A<T>::D::g() { (void)c.private_int; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
void A<int>::D::g() { (void)c.private_int; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
template<class T>
T A<T>::h() { (void)c.private_int; }
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 8162f9caa8f15..53ff58c7cbffd 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -94,26 +94,26 @@ namespace cwg1909 { // cwg1909: 3.7
};
} // namespace cwg1909
-namespace cwg1918 { // cwg1918: no
+namespace cwg1918 { // cwg1918: 24
template<typename T> struct A {
class B {
class C {};
};
};
class X {
- static int x;
- // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id
+ static int x; // #cwg1918-X-x
template <typename T>
friend class A<T>::B::C;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
+ // expected-error at -1 {{'A<T>::B' does not name a class template}}
};
template<> struct A<int> {
typedef struct Q B;
};
struct Q {
class C {
- // FIXME: 'f' is not a friend, so 'X::x' is not accessible
int f() { return X::x; }
+ // expected-error at -1 {{'x' is a private member of 'cwg1918::X'}}
+ // expected-note@#cwg1918-X-x {{implicitly declared private here}}
};
};
} // namespace cwg1918
@@ -159,7 +159,7 @@ derived d2(42, 9);
#endif
} // namespace cwg1941
-namespace cwg1945 { // cwg1945: no
+namespace cwg1945 { // cwg1945: 24
template<typename T> struct A {
class B {
class C {};
@@ -167,10 +167,9 @@ template<typename T> struct A {
};
class X {
static int x;
- // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id
template <typename T>
friend class A<T>::B::C;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
+ // expected-error at -1 {{'A<T>::B' does not name a class template}}
};
} // namespace cwg1945
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 02c8f30249683..7352da80e7b41 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -178,17 +178,21 @@ namespace cwg2858 { // cwg2858: 19
template<typename... Ts>
struct A {
- // FIXME: The nested-name-specifier in the following friend declarations are declarative,
- // but we don't treat them as such (yet).
friend void Ts...[0]::f();
+ // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
template<typename U>
friend void Ts...[0]::g();
+ // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
friend struct Ts...[0]::B;
- // FIXME: The index of the pack-index-specifier is printed as a memory address in the diagnostic.
+ // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
template<typename U>
friend struct Ts...[0]::C;
- // since-cxx26-warning at -1 {{dependent nested name specifier 'Ts...[0]' for friend template declaration is not supported; ignoring this friend declaration}}
+ // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
+
+ template<typename U>
+ friend struct Ts...[0]::template B<U>::C;
+ // since-cxx26-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
};
#endif
diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index 451554a36d70d..eb1898b56762d 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -396,7 +396,7 @@ namespace cwg637 { // cwg637: 3.0
}
} // namespace cwg637
-namespace cwg638 { // cwg638: no
+namespace cwg638 { // cwg638: 24
template<typename T> struct A {
struct B;
void f();
@@ -407,26 +407,29 @@ namespace cwg638 { // cwg638: no
};
class X {
- typedef int type;
+ typedef int type; // #cwg638-X-type
template<class T> friend struct A<T>::B;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
template<class T> friend void A<T>::f();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
template<class T> friend void A<T>::g();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
template<class T> friend void A<T>::C::h();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::C' for friend class declaration is not supported; turning off access control for 'X'}}
+ // expected-error at -1 {{'A<T>::C' does not name a class template}}
};
template<> struct A<int> {
- X::type a; // FIXME: private
+ X::type a;
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
struct B {
X::type b; // ok
};
- int f() { X::type c; } // FIXME: private
+ int f() { X::type c; }
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
void g() { X::type d; } // ok
struct D {
- void h() { X::type e; } // FIXME: private
+ void h() { X::type e; }
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
};
};
} // namespace cwg638
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
index b8092afaffef3..3ebd32f713371 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp
@@ -387,3 +387,29 @@ template <class T> class A {
friend void foo<T>() {} // expected-error {{ambiguous}} expected-error{{no candidate function template was found for dependent friend function template specialization}}
};
}
+
+namespace test19 {
+template <class V> class B;
+
+template <class T> struct A {
+ template <class U> static void f(B<T> &);
+};
+
+template <class V> class B {
+ int n; // #test19-B-n
+ friend void A<V>::template f<int>(B<V> &);
+};
+
+template <class T>
+template <class U>
+void A<T>::f(B<T> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test19::B<long>'}}
+ // expected-note@#test19-B-n {{implicitly declared private here}}
+}
+
+template class B<long>;
+template void A<long>::f<int>(B<long> &);
+template void A<long>::f<double>(B<long> &);
+// expected-note at -1 {{in instantiation of function template specialization 'test19::A<long>::f<double>' requested here}}
+}
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index a292d0de97a39..cb0b046eb4132 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -1,4 +1,8 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++14 -fcxx-exceptions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
namespace test0 {
template <class T> class A {
@@ -6,7 +10,7 @@ namespace test0 {
};
class B {
- template <class T> friend class A<T>::Member; // expected-warning {{not supported}}
+ template <class T> friend class A<T>::Member;
int n;
};
@@ -19,7 +23,7 @@ namespace test1 {
class C {
static void foo();
- template <class T> friend void A<T>::f(); // expected-warning {{not supported}}
+ template <class T> friend void A<T>::f();
};
template <class T> struct A {
@@ -35,25 +39,30 @@ namespace test1 {
};
}
-// FIXME: these should fail!
namespace test2 {
template <class T> struct A;
class C {
- static void foo();
- template <class T> friend void A<T>::g(); // expected-warning {{not supported}}
+ static void foo(); // #test2-C-foo
+ template <class T> friend void A<T>::g();
};
template <class T> struct A {
void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
template <class T> struct A<T*> {
void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
template <> struct A<char> {
void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
}
@@ -66,7 +75,7 @@ namespace test3 {
template <class U> class C {
int i;
- template <class T> friend struct A<T>::Inner; // expected-warning {{not supported}}
+ template <class T> friend struct A<T>::Inner;
};
template <class T> int A<T>::Inner::foo() {
@@ -79,24 +88,1091 @@ namespace test3 {
}
namespace test4 {
- template <class T> struct X {
+ template <class T> struct A {
template <class U> void operator+=(U);
-
+
template <class V>
template <class U>
- friend void X<V>::operator+=(U); // expected-warning {{not supported}}
+ friend void A<V>::operator+=(U);
};
- void test() {
- X<int>() += 1.0;
+ void test() {
+ A<int>() += 1.0;
}
}
namespace test5 {
template<template <class> class T> struct A {
- template<template <class> class U> friend void A<U>::foo(); // expected-warning {{not supported}}
+ template<template <class> class U> friend void A<U>::foo();
};
template <class> struct B {};
template class A<B>;
}
+
+namespace test6 {
+ template <class T> struct A {
+ struct B {
+ static int f();
+ };
+ };
+
+ struct C {
+ int n;
+ template <class T> friend struct A<T>::B;
+ };
+
+ template <class T> int A<T>::B::f() {
+ C c;
+ c.n = 0;
+ return 0;
+ }
+
+ int k = A<int>::B::f();
+}
+
+namespace test7 {
+ template <class T> struct A {
+ struct D {
+ void g();
+ };
+ };
+
+ struct C {
+ template <class T> friend void A<T>::D::g();
+ // expected-error at -1 {{'A<T>::D' does not name a class template}}
+ };
+}
+
+namespace test8 {
+ template <class T> struct A { // #test8-A
+ T h();
+ };
+
+ template <> struct A<int> {
+ int h();
+ };
+
+ template <> struct A<float *> {
+ int *h();
+ };
+
+ class C {
+ int n; // #test8-C-n
+ template <class T> friend int *A<T *>::h();
+ };
+
+ template <class T> T A<T>::h() {
+ return T();
+ }
+
+ int A<int>::h() {
+ C c;
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test8::C'}}
+ // expected-note@#test8-C-n {{implicitly declared private here}}
+ // expected-note@#test8-A {{candidate friend template ignored: could not match 'T *' against 'int'}}
+ return 0;
+ }
+
+ template <> int *A<int *>::h() {
+ C c;
+ c.n = 0;
+ return nullptr;
+ }
+
+ int *A<float *>::h() {
+ C c;
+ c.n = 0;
+ return nullptr;
+ }
+
+ int *t1 = A<int *>().h();
+ int *t2 = A<float *>().h();
+ int t3 = A<int>().h();
+}
+
+namespace test9 {
+ template <class T> struct A {
+ template <T U> T i();
+ };
+
+ template <> struct A<int> {
+ template <int U> int i();
+ };
+
+ struct C {
+ int n;
+ template <class T> template <T U> friend T A<T>::i();
+ };
+
+ template <class T> template <T U> T A<T>::i() {
+ C c;
+ c.n = 0;
+ return U;
+ }
+
+ template <int U> int A<int>::i() {
+ C c;
+ c.n = 0;
+ return U;
+ }
+
+ int x = A<int>().i<1>();
+}
+
+namespace test10 {
+ template <class T> struct A;
+ class C {
+ static void foo(); // #test10-C-foo
+ template <class T> friend void A<T>::f();
+ };
+
+ template <class T> struct A {
+ void f() { C::foo(); }
+ };
+
+ template <> struct A<int> {
+ int f() {
+ C::foo();
+ // expected-error at -1 {{'foo' is a private member of 'test10::C'}}
+ // expected-note@#test10-C-foo {{implicitly declared private here}}
+ return 0;
+ }
+ };
+}
+
+namespace test11 {
+ template <class> struct C;
+ template <class T> struct A {
+ template <class> struct B;
+ };
+ template <class T> struct D : A<T> {
+ using A<T>::B;
+ };
+
+ template <class T> struct C {
+ int n;
+ template <class U> friend struct D<T>::B;
+ };
+
+ template <> template <class U> struct A<int>::B {
+ static int f(C<int> &c) {
+ c.n = 0;
+ return 0;
+ }
+ };
+
+ int x = A<int>::B<void>::f(*new C<int>);
+}
+
+namespace test12 {
+ template <class T> struct A {
+ template <T> struct B {
+ static int f();
+ };
+ };
+
+ template <class T> struct C {
+ int n;
+ template <class U> template <U V> friend struct A<U>::B;
+ };
+
+ template <class T> template <T V> int A<T>::B<V>::f() {
+ C<T> c;
+ c.n = 0;
+ return 0;
+ }
+
+ int x = A<int>::B<0>::f();
+}
+
+namespace test13 {
+ template <typename T> struct S {
+ template <typename> friend class T::template A<int>::B;
+ };
+}
+
+namespace test14 {
+ template <class T> struct A {
+ template <bool V> struct B {
+ static int f(B<false> &x) { return x.n; }
+
+ private:
+ int n;
+ template <bool> friend struct A<T>::B;
+ };
+ };
+
+ int x = A<int>::B<true>::f(*new A<int>::B<false>);
+}
+
+namespace test15 {
+ template <class T> struct A {
+ T f();
+ };
+
+ template <> struct A<int> {
+ void f();
+ };
+
+ class C {
+ int n; // #test15-C-n
+ template <class T> friend T A<T>::f();
+ };
+
+ void A<int>::f() {
+ C c;
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test15::C'}}
+ // expected-note@#test15-C-n {{implicitly declared private here}}
+ }
+}
+
+namespace test16 {
+ template <class T> struct A {
+ template <T U> T i();
+ };
+
+ template <> struct A<int> {
+ template <int U> void i();
+ };
+
+ class C {
+ int n; // #test16-C-n
+ template <class T> template <T U> friend T A<T>::i();
+ };
+
+ template <int U> void A<int>::i() {
+ C c;
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test16::C'}}
+ // expected-note@#test16-C-n {{implicitly declared private here}}
+ }
+}
+
+namespace test17 {
+ template <class T> class A;
+
+ template <class T>
+ struct B {
+ template <bool V>
+ struct C {
+ int f(A<T> *p) { return p->x; }
+ };
+ };
+
+ template <class T>
+ class A {
+ friend struct B<T>::template C<true>;
+ int x;
+ };
+
+ template struct B<int>::C<true>;
+}
+
+namespace test18 {
+ template <class T> class A;
+
+ template <class T>
+ struct B {
+ template <bool V>
+ struct C {
+ int f(A<T> *p) { return p->x; }
+ // expected-error at -1 {{'x' is a private member of 'test18::A<int>'}}
+ };
+ };
+
+ template <class T>
+ class A {
+ friend struct B<T>::template C<false>;
+ int x;
+ // expected-note at -1 {{implicitly declared private here}}
+ };
+
+ template struct B<int>::C<true>;
+ // expected-note at -1 {{in instantiation of member function 'test18::B<int>::C<true>::f' requested here}}
+}
+
+namespace test19 {
+ template <class...> struct A {
+ struct B;
+ static void f();
+ };
+
+ struct C {
+ template <class T, class U> // #test19-U-type
+ friend struct A<T>::B;
+ // expected-error at -1 {{template parameter of friend declaration cannot be deduced from 'A<T>'}}
+ // expected-note@#test19-U-type {{non-deducible template parameter 'U'}}
+
+ template <class T, class U> // #test19-U-function
+ friend void A<T>::f();
+ // expected-error at -1 {{template parameter of friend declaration cannot be deduced from 'A<T>'}}
+ // expected-note@#test19-U-function {{non-deducible template parameter 'U'}}
+
+ template <class... Ts> // #test19-Ts
+ friend struct A<Ts..., int>::B;
+ // expected-error at -1 {{template parameter of friend declaration cannot be deduced from 'A<Ts..., int>'}}
+ // expected-note@#test19-Ts {{non-deducible template parameter 'Ts'}}
+ };
+}
+
+namespace test20 {
+ class D;
+
+ template <class T> struct A {
+ template <class, class U> struct B {
+ struct C {
+ static void f(D &);
+ };
+ static void g(D &);
+ };
+ };
+
+ class D {
+ int n;
+
+ template <class T>
+ template <class U>
+ friend struct A<T>::B<T, U>::C;
+
+ template <class T>
+ template <class U>
+ friend void A<T>::B<T, U>::g(D &);
+ };
+
+ template <class T>
+ template <class V, class U>
+ void A<T>::B<V, U>::C::f(D &x) {
+ x.n = 0;
+ }
+
+ template <class T>
+ template <class V, class U>
+ void A<T>::B<V, U>::g(D &x) {
+ x.n = 0;
+ }
+
+ template struct A<int>::B<int, double>;
+}
+
+namespace test21 {
+ class C;
+
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+
+ class C {
+ int n; // #test21-C-n
+ template <class T>
+ template <class U>
+ friend struct A<T>::B;
+ };
+
+ template <> struct A<int> {
+ template <int U> struct B {
+ static void f(C &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test21::C'}}
+ // expected-note@#test21-C-n {{implicitly declared private here}}
+ }
+ };
+ };
+
+ template struct A<int>::B<0>;
+}
+
+namespace test22 {
+ class C;
+
+ template <class T> struct A {
+ struct B;
+ };
+
+ class C {
+ int n; // #test22-C-n
+ template <class T> friend struct A<T>::B;
+ };
+
+ template <> struct A<int> {
+ union B {
+ static void f(C &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test22::C'}}
+ // expected-note@#test22-C-n {{implicitly declared private here}}
+ }
+ };
+ };
+}
+
+namespace test23 {
+ class B;
+
+ template <class T> struct A {
+ template <class U> static void f(B &);
+ };
+
+ class B {
+ int n; // #test23-B-n
+ template <class T>
+ template <class U>
+ friend void A<T>::f(B &);
+ };
+
+ template <> struct A<int> {
+ template <int U> static void f(B &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test23::B'}}
+ // expected-note@#test23-B-n {{implicitly declared private here}}
+ }
+ };
+
+ template void A<int>::f<0>(B &);
+}
+
+namespace test24 {
+ class B;
+
+ template <class T> struct A {
+ static void f(B &);
+ };
+
+ class B {
+ int n; // #test24-B-n
+ template <class T> friend void A<T>::f(B &);
+ };
+
+ template <> struct A<int> {
+ static void f(B &x, ...) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test24::B'}}
+ // expected-note@#test24-B-n {{implicitly declared private here}}
+ }
+ };
+}
+
+namespace test25 {
+ class C;
+
+ template <class... Ts> struct A {
+ struct B {
+ static void f(C &);
+ };
+ };
+
+ class C {
+ int n;
+ template <class... Ts> friend struct A<Ts...>::B;
+ };
+
+ template <class... Ts> void A<Ts...>::B::f(C &x) {
+ x.n = 0;
+ }
+
+ template void A<int, double>::B::f(C &);
+}
+
+namespace test26 {
+ class B;
+
+ template <class T> struct A {
+ template <class U> static void f(U);
+ };
+
+ template <> struct A<int> {
+ template <class U> static void f(U);
+ };
+
+ class B {
+ int n;
+ template <class T>
+ template <class U>
+ friend void A<T>::f(U);
+ };
+
+ template <class T>
+ template <class U>
+ void A<T>::f(U) {
+ B b;
+ b.n = 0;
+ }
+
+ template <class U>
+ void A<int>::f(U) {
+ B b;
+ b.n = 0;
+ }
+
+ template void A<long>::f<double>(double);
+ template void A<int>::f<double>(double);
+}
+
+namespace test27 {
+ template <class T> struct A {
+ struct B {
+ template <class U> static void g(U);
+ template <class U> struct C;
+ };
+ };
+
+ template <class V> struct D {
+ template <class U> friend void A<V>::B::g(U);
+ // expected-error at -1 {{'A<V>::B' does not name a class template}}
+
+ template <class U> friend class A<V>::B::C;
+ // expected-error at -1 {{'A<V>::B' does not name a class template}}
+ };
+}
+
+namespace test28 {
+ class B;
+
+ template <class T> struct A {
+ ~A();
+ };
+
+ template <> struct A<int> {
+ ~A();
+ };
+
+ class B {
+ int n;
+ template <class T> friend A<T>::~A();
+ };
+
+ template <class T> A<T>::~A() {
+ B b;
+ b.n = 0;
+ }
+
+ A<int>::~A() {
+ B b;
+ b.n = 0;
+ }
+
+ template struct A<long>;
+}
+
+namespace test29 {
+ template <class T> class D;
+
+ template <class T> struct A {
+ template <class I> struct B {
+ template <class U> struct C {
+ static void h(D<T> &);
+ };
+ };
+ };
+
+ template <class V> class D {
+ int n;
+
+ template <class U>
+ friend class A<V>::B<int>::C;
+ };
+
+ template <class T>
+ template <class I>
+ template <class U>
+ void A<T>::B<I>::C<U>::h(D<T> &x) {
+ x.n = 0;
+ }
+
+ template struct A<int>::B<int>::C<double>;
+}
+
+namespace test30 {
+ class B;
+
+ template <class T> struct A {
+ template <class U> static void f(U);
+ };
+
+ template <> struct A<int> {
+ template <class U> static void f(U *);
+ };
+
+ class B {
+ int n; // #test30-B-n
+ template <class T>
+ template <class U>
+ friend void A<T>::f(U);
+ };
+
+ template <class U>
+ void A<int>::f(U *) {
+ B b;
+ b.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test30::B'}}
+ // expected-note@#test30-B-n {{implicitly declared private here}}
+ }
+
+ template void A<int>::f<double>(double *);
+}
+
+namespace test31 {
+ class B;
+
+ template <class T> struct A {
+ static void f(B &) noexcept;
+ };
+
+ template <> struct A<int> {
+ static void f(B &);
+ };
+
+ class B {
+ int n; // #test31-B-n
+ template <class T> friend void A<T>::f(B &) noexcept;
+ };
+
+ template <class T> void A<T>::f(B &x) noexcept {
+ x.n = 0;
+ }
+
+ void A<int>::f(B &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test31::B'}}
+ // expected-note@#test31-B-n {{implicitly declared private here}}
+ }
+
+ template void A<long>::f(B &) noexcept;
+}
+
+namespace test32 {
+ class B;
+
+ template <class T> struct A {
+ A();
+ operator int();
+ };
+
+ template <> struct A<char> {
+ A();
+ operator int();
+ };
+
+ class B {
+ int n;
+ template <class T> friend A<T>::A();
+ template <class T> friend A<T>::operator int();
+ };
+
+ template <class T> A<T>::A() {
+ B b;
+ b.n = 0;
+ }
+
+ template <class T> A<T>::operator int() {
+ B b;
+ b.n = 0;
+ return 0;
+ }
+
+ A<char>::A() {
+ B b;
+ b.n = 0;
+ }
+
+ A<char>::operator int() {
+ B b;
+ b.n = 0;
+ return 0;
+ }
+
+ template struct A<long>;
+}
+
+namespace test33 {
+ struct A {
+ template <class> struct D {
+ struct M;
+ template <class> struct N;
+ static void f();
+ template <class> static void g();
+ };
+ };
+
+ struct B {
+ struct M;
+ template <class> struct N;
+ static void f();
+ template <class> static void g();
+ };
+
+ struct C {
+ template <class> using D = B;
+ };
+
+ template <class P> struct D {
+ template <class U> friend struct P::template D<U>::M;
+ // expected-error at -1 {{'test33::C::template D<U>' does not name a class template}}
+ };
+
+ template struct D<A>;
+ template struct D<C>;
+ // expected-note at -1 {{in instantiation of template class 'test33::D<test33::C>' requested here}}
+
+ template <class P> struct E {
+ template <class U> friend struct P::template D<int>::N;
+ // expected-error at -1 {{'test33::C::template D<int>' does not name a class template}}
+ };
+
+ template struct E<A>;
+ template struct E<C>;
+ // expected-note at -1 {{in instantiation of template class 'test33::E<test33::C>' requested here}}
+
+ template <class P> struct F {
+ template <class U> friend void P::template D<U>::f();
+ // expected-error at -1 {{'test33::C::template D<U>' does not name a class template}}
+ };
+
+ template struct F<A>;
+ template struct F<C>;
+ // expected-note at -1 {{in instantiation of template class 'test33::F<test33::C>' requested here}}
+
+ template <class P> struct G {
+ template <class U> friend void P::template D<int>::g();
+ // expected-error at -1 {{'test33::C::template D<int>' does not name a class template}}
+ };
+
+ template struct G<A>;
+ template struct G<C>;
+ // expected-note at -1 {{in instantiation of template class 'test33::G<test33::C>' requested here}}
+}
+
+namespace test34 {
+ template <class V> class C;
+
+ template <class T> struct A {
+ template <class U> struct B {
+ static void f(C<long> &);
+ };
+ };
+
+ template <class V> class C {
+ int n; // #test34-C-n
+
+ template <class T>
+ friend struct A<T>::template B<V>;
+ };
+
+ template class C<long>;
+
+ template <class T>
+ template <class U>
+ void A<T>::B<U>::f(C<long> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test34::C<long>'}}
+ // expected-note@#test34-C-n {{implicitly declared private here}}
+ }
+
+ template struct A<char>::B<long>;
+ template struct A<char>::B<int>;
+ // expected-note at -1 {{in instantiation of member function 'test34::A<char>::B<int>::f' requested here}}
+}
+
+namespace test35 {
+ template <class V> class B;
+
+ template <class T> struct A {
+ template <class U, class V> static void f(B<V> &);
+ };
+
+ template <class V> class B {
+ int n; // #test35-B-n
+
+ template <class T>
+ friend void A<T>::template f<int, V>(B &);
+ };
+
+ template <class T>
+ template <class U, class V>
+ void A<T>::f(B<V> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test35::B<long>'}}
+ // expected-note@#test35-B-n {{implicitly declared private here}}
+ }
+
+ template class B<long>;
+ template void A<char>::f<int, long>(B<long> &);
+ template void A<char>::f<double, long>(B<long> &);
+ // expected-note at -1 {{in instantiation of function template specialization 'test35::A<char>::f<double, long>' requested here}}
+}
+
+namespace test36 {
+ template <class V> class B;
+
+ template <class T> struct A {
+ template <class U, class V> static void f(B<V> &, U);
+ };
+
+ template <class V> class B {
+ int n; // #test36-B-n
+
+ template <class T>
+ friend void A<T>::f(B &, int);
+ };
+
+ template <class T>
+ template <class U, class V>
+ void A<T>::f(B<V> &x, U) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test36::B<long>'}}
+ // expected-note@#test36-B-n {{implicitly declared private here}}
+ }
+
+ template class B<long>;
+ template void A<char>::f<int, long>(B<long> &, int);
+ template void A<char>::f<double, long>(B<long> &, double);
+ // expected-note at -1 {{in instantiation of function template specialization 'test36::A<char>::f<double, long>' requested here}}
+}
+
+namespace test37 {
+ class C;
+
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+
+ template <> struct A<int> {
+ template <class U> struct B {
+ static void f(C &);
+ };
+ };
+
+ class C {
+ int n; // #test37-C-n
+
+ template <class T>
+ friend struct A<T>::B<long>;
+ };
+
+ template <class U>
+ void A<int>::B<U>::f(C &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test37::C'}}
+ // expected-note@#test37-C-n {{implicitly declared private here}}
+ }
+
+ template struct A<int>::B<long>;
+ template struct A<int>::B<double>;
+ // expected-note at -1 {{in instantiation of member function 'test37::A<int>::B<double>::f' requested here}}
+}
+
+namespace test38 {
+ class B;
+
+ template <class T> struct A {
+ template <class U> static void f(B &);
+ };
+
+ template <> struct A<int> {
+ template <class U> static void f(B &);
+ };
+
+ class B {
+ int n; // #test38-B-n
+
+ template <class T>
+ friend void A<T>::f<long>(B &);
+ };
+
+ template <class U>
+ void A<int>::f(B &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test38::B'}}
+ // expected-note@#test38-B-n {{implicitly declared private here}}
+ }
+
+ template void A<int>::f<long>(B &);
+ template void A<int>::f<double>(B &);
+ // expected-note at -1 {{in instantiation of function template specialization 'test38::A<int>::f<double>' requested here}}
+}
+
+namespace test39 {
+ class D;
+
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+
+ template <> struct A<int> {
+ struct B {
+ static void f(D &);
+ };
+ };
+
+ template <> struct A<long> {
+ template <class U> union B {
+ static void f(D &);
+ };
+ };
+
+ template <> struct A<char> {
+ template <class U> struct C {
+ static void f(D &);
+ };
+ };
+
+ class D {
+ int n; // #test39-D-n
+
+ template <class T>
+ friend struct A<T>::B<double>;
+ };
+
+ void A<int>::B::f(D &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test39::D'}}
+ // expected-note@#test39-D-n {{implicitly declared private here}}
+ }
+
+ template <class U>
+ void A<long>::B<U>::f(D &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test39::D'}}
+ // expected-note@#test39-D-n {{implicitly declared private here}}
+ }
+
+ template <class U>
+ void A<char>::C<U>::f(D &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test39::D'}}
+ // expected-note@#test39-D-n {{implicitly declared private here}}
+ }
+}
+
+namespace test40 {
+ template <class> class C;
+
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+
+ template <> struct A<int> {
+ struct B { // expected-note {{previous definition is here}}
+ static void f(C<int> &);
+ };
+ };
+
+ template <> struct A<long> {
+ template <class U> struct B {
+ static void f(C<long> &);
+ };
+ };
+
+ template <class V> class C {
+ int n; // #test40-C-n
+
+ template <class U>
+ friend struct A<V>::B; // expected-error {{redefinition of 'B' as
diff erent kind of symbol}}
+ };
+
+ template class C<int>;
+ // expected-note at -1 {{in instantiation of template class 'test40::C<int>' requested here}}
+
+ void A<int>::B::f(C<int> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test40::C<int>'}}
+ // expected-note@#test40-C-n {{implicitly declared private here}}
+ }
+
+ template class C<long>;
+
+ template <class U>
+ void A<long>::B<U>::f(C<long> &x) {
+ x.n = 0;
+ }
+
+ template struct A<long>::B<double>;
+}
+
+namespace test41 {
+ class C;
+
+ template <class T> struct A {
+ struct B;
+ };
+
+ template <> struct A<int> {
+ template <class U> struct B {
+ static void f(C &);
+ };
+ };
+
+ class C {
+ int n; // #test41-C-n
+
+ template <class T>
+ friend struct A<T>::B;
+ };
+
+ template <class U>
+ void A<int>::B<U>::f(C &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test41::C'}}
+ // expected-note@#test41-C-n {{implicitly declared private here}}
+ }
+
+ template struct A<int>::B<double>;
+}
+
+namespace test42 {
+ template <class> class D;
+
+ struct A {
+ template <class U> struct B {
+ struct C {
+ static void f(D<A> &);
+ };
+ static void g(D<A> &);
+ };
+ };
+
+ template <class T> class D {
+ int n;
+
+ template <class U>
+ friend struct T::template B<U>::C;
+
+ template <class U>
+ friend void T::template B<U>::g(D &);
+ };
+
+ template <class U>
+ void A::B<U>::C::f(D<A> &x) {
+ x.n = 0;
+ }
+
+ template <class U>
+ void A::B<U>::g(D<A> &x) {
+ x.n = 0;
+ }
+
+ template struct A::B<int>;
+}
+
+namespace test43 {
+ template <class T> struct A {
+ static void f(typename T::type);
+ };
+
+ class B {
+ using type = int;
+ int n;
+
+ template <class T> friend void A<T>::f(typename T::type);
+ };
+
+ template <> struct A<B> {
+ static void f(int);
+ };
+
+ void A<B>::f(int) {
+ B b;
+ b.n = 0;
+ }
+}
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
new file mode 100644
index 0000000000000..f60f3dd42445c
--- /dev/null
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <class T> struct A;
+template <class T> struct B {
+ void f();
+};
+
+void t1() {
+ struct S {
+ template <class T> friend void f();
+ // expected-error at -1 {{templates can only be declared in namespace or class scope}}
+ };
+}
+
+void t2() {
+ struct S {
+ template <class T> friend struct A;
+ // expected-error at -1 {{templates cannot be declared inside of a local class}}
+ };
+}
+
+void t3() {
+ struct S {
+ template <class T> friend void B<T>::f();
+ // expected-error at -1 {{templates cannot be declared inside of a local class}}
+ };
+}
diff --git a/clang/test/Index/load-dependent-friend-template.cpp b/clang/test/Index/load-dependent-friend-template.cpp
new file mode 100644
index 0000000000000..855ebd3d95d07
--- /dev/null
+++ b/clang/test/Index/load-dependent-friend-template.cpp
@@ -0,0 +1,15 @@
+// RUN: c-index-test -test-load-source all -std=c++20 %s | FileCheck %s
+
+template <class T> struct A {
+ struct B;
+};
+
+struct C {
+ template <class T> friend struct A<T>::B;
+};
+
+// CHECK: load-dependent-friend-template.cpp:7:8: StructDecl=C:7:8 (Definition)
+// CHECK: load-dependent-friend-template.cpp:8:42: FriendDecl=:8:42
+// CHECK-NEXT: load-dependent-friend-template.cpp:8:19: TemplateTypeParameter=T:8:19 (Definition)
+// CHECK-NEXT: load-dependent-friend-template.cpp:8:36: TemplateRef=A:3:27
+// CHECK-NEXT: load-dependent-friend-template.cpp:8:38: TypeRef=T:8:19
diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
index f22f3c71f44d2..7f86b5574d78b 100644
--- a/clang/test/Modules/odr_hash.cpp
+++ b/clang/test/Modules/odr_hash.cpp
@@ -2631,6 +2631,32 @@ S5 s5;
// expected-note at first.h:* {{but in 'FirstModule' found friend function 'T5a'}}
#endif
+#if defined(FIRST) || defined(SECOND)
+template <class T> struct A {
+ struct B;
+};
+#endif
+
+#if defined(FIRST)
+struct S6 {
+ template <class T>
+ requires true
+ friend struct A<T>::B;
+};
+#elif defined(SECOND)
+struct S6 {
+ template <class T>
+ requires(sizeof(T) > 0)
+ friend struct A<T>::B;
+};
+#else
+S6 s6;
+// expected-error at second.h:* {{'Friend::S6' has
diff erent definitions in
diff erent modules}}
+// expected-note at second.h:* {{in first definition, possible
diff erence is here}}
+// expected-note at first.h:* {{definition in module 'FirstModule' is here}}
+// expected-note at first.h:* {{in second definition, possible
diff erence is here}}
+#endif
+
#define DECLS \
friend class FriendA; \
friend struct FriendB; \
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 621ae912c1ac9..951eb47099919 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -30,6 +30,7 @@ struct E { template<class T> class Nested; };
template<class... Ts> // expected-note {{template parameter is declared here}}
struct VS {
friend Ts...;
+ friend Ts...[0];
friend class Ts...; // expected-error {{declaration of 'Ts' shadows template parameter}}
// expected-error at -1 {{pack expansion does not contain any unexpanded parameter packs}}
@@ -56,13 +57,15 @@ struct VS {
template<bool... Bs>
friend class E<Bs>::Nested...; // expected-error {{friend declaration expands pack 'Bs' that is declared it its own template parameter list}}
- // FIXME: Both of these should be valid, but we can't handle these at
- // the moment because the NNS is dependent.
template<class ...T>
- friend class TS<Ts>::Nested...; // expected-warning {{dependent nested name specifier 'TS<Ts>' for friend template declaration is not supported; ignoring this friend declaration}}
+ friend class TS<Ts>::Nested...;
template<class T>
- friend class D<T, Ts>::Nested...; // expected-warning {{dependent nested name specifier 'D<T, Ts>' for friend class declaration is not supported; turning off access control for 'VS'}}
+ friend class D<T, Ts>::Nested...;
+
+ template<class T>
+ friend class Ts...[0]::Nested...;
+ // expected-error at -1 {{a pack indexing specifier cannot be used in a nested name specifier of a friend declaration}}
};
namespace length_mismatch {
diff --git a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
index 0b01907c8c722..910b7be9190d7 100644
--- a/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
+++ b/clang/test/SemaCXX/cxx2c-variadic-friends.cpp
@@ -154,3 +154,60 @@ void f() {
}
}
+namespace GH104057 {
+template <class... Vs> class C;
+
+template <class T> struct A {
+ template <class U> struct B {
+ static void f(C<int, long> &);
+ };
+};
+
+template <class... Vs> class C {
+ int n; // #GH104057-C-n
+
+ template <class T>
+ friend struct A<T>::template B<Vs>...;
+};
+
+template <class T>
+template <class U>
+void A<T>::B<U>::f(C<int, long> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'GH104057::C<int, long>'}}
+ // expected-note@#GH104057-C-n {{implicitly declared private here}}
+}
+
+template struct A<char>::B<int>;
+template struct A<char>::B<long>;
+template struct A<char>::B<double>;
+// expected-note at -1 {{in instantiation of member function 'GH104057::A<char>::B<double>::f' requested here}}
+template <class...> class D;
+
+template <class T> struct E { // expected-note {{candidate friend template ignored: could not match 'long' against 'char'}}
+ template <class U> struct F {
+ static void f(D<int, long> &);
+ };
+};
+
+template <class... Vs> class D {
+ int n; // #GH104057-D-n
+
+ template <class U>
+ friend struct E<Vs>::F...;
+};
+
+template <class T>
+template <class U>
+void E<T>::F<U>::f(D<int, long> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'GH104057::D<int, long>'}}
+ // expected-note@#GH104057-D-n {{implicitly declared private here}}
+}
+
+template struct D<int, long>;
+template struct E<int>::F<char>;
+template struct E<long>::F<double>;
+template struct E<char>::F<float>;
+// expected-note at -1 {{in instantiation of member function 'GH104057::E<char>::F<float>::f' requested here}}
+}
diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index f98005c7e6fb5..975803ed26bc1 100644
--- a/clang/test/SemaCXX/many-template-parameter-lists.cpp
+++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp
@@ -5,7 +5,7 @@
template <class T>
struct X {
template <class U>
- struct A { // expected-note {{not-yet-instantiated member is declared here}}
+ struct A {
template <class V>
struct B {
template <class W>
@@ -22,15 +22,16 @@ struct X {
};
};
- template <class U>
- template <class V>
- template <class W>
- template <class X>
+ template <class U> // expected-note {{non-deducible template parameter 'U'}}
+ template <class V> // expected-note {{non-deducible template parameter 'V'}}
+ template <class W> // expected-note {{non-deducible template parameter 'W'}}
+ template <class X> // expected-note {{non-deducible template parameter 'X'}}
template <class Y>
template <class Z>
- friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z); // expected-warning {{not supported}} expected-error {{no member 'A' in 'X<int>'; it has not yet been instantiated}}
+ friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z);
+ // expected-error at -1 {{template parameters of friend declaration cannot be deduced from 'A<U>::template B<V>::template C<W>::template D<X>::template E<Y>'}}
};
void test() {
- X<int>::A<int>::B<int>::C<int>::D<int>::E<int>() += 1.0; // expected-note {{in instantiation of template class 'X<int>' requested here}}
+ X<int>::A<int>::B<int>::C<int>::D<int>::E<int>() += 1.0;
}
diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
index daec9410e547a..c5c5d13036233 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -18,17 +18,21 @@ void f() {
template<class A>
class temp {
template<C<temp> T>
- friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
+ friend void g();
+ // expected-error at -1 {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
- temp();
+ temp(); // #temp-ctor
};
template<C<temp<int>> T>
void g() {
auto v = temp<T>();
+ // expected-error at -1 {{calling a private constructor of class 'temp<int>'}}
+ // expected-note@#temp-ctor {{implicitly declared private here}}
}
void h() {
f<int>();
g<int>();
+ // expected-note at -1 {{in instantiation of function template specialization 'g<int>' requested here}}
}
diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
index 11287aa773b1b..7fc0f043dd9a4 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,3 +566,61 @@ struct Test {
};
}
+
+namespace GH104057 {
+template <class T> concept A = requires { typename T::type; }; // #GH104057_A
+
+struct B {
+ using type = int;
+};
+struct C {};
+
+template <class T> struct D {
+ static void f()
+ requires A<T>; // #GH104057_D_f
+};
+
+class E {
+ static int n;
+ template <A T> friend void D<T>::f();
+};
+
+template <class T> struct F {
+ template <A U> // #GH104057_F_TPL
+ static void f() // #GH104057_F_f
+ requires A<T>;
+};
+
+class G {
+ static int n;
+ template <A T> template <A U>
+ friend void F<T>::f()
+ requires A<T>;
+};
+
+template <class T>
+void D<T>::f() requires A<T> {
+ E::n = 0;
+}
+
+template <class T>
+template <A U>
+void F<T>::f() requires A<T> {
+ G::n = 0;
+}
+
+void test() {
+ D<B>::f();
+ D<C>::f();
+ // expected-error at -1 {{invalid reference to function 'f': constraints not satisfied}}
+ // expected-note@#GH104057_D_f {{because 'GH104057::C' does not satisfy 'A'}}
+ // expected-note@#GH104057_A {{because 'typename T::type' would be invalid: no type named 'type' in 'GH104057::C'}}
+
+ F<B>::f<B>();
+ F<B>::f<C>();
+ // expected-error at -1 {{no matching function for call to 'f'}}
+ // expected-note@#GH104057_F_f {{candidate template ignored: constraints not satisfied}}
+ // expected-note@#GH104057_F_TPL {{because 'GH104057::C' does not satisfy 'A'}}
+ // expected-note@#GH104057_A {{because 'typename T::type' would be invalid: no type named 'type' in 'GH104057::C'}}
+}
+} // namespace GH104057
diff --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp
index 7a8a72a62e992..2681ee2a6081e 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -19,27 +19,31 @@ namespace pr41427 {
namespace Access {
struct B {
protected:
- struct type {};
+ struct type {}; // #Access-B-type
};
- template<typename T> struct D : B { // expected-note {{not viable}} \
- expected-note {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
- D(T, typename T::type); // expected-note {{private member}} \
- // expected-note {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
+ template<typename T> struct D : B { // #Access-D
+ D(T, typename T::type); // #Access-D-ctor
+ // expected-error at -1 {{'type' is a private member of 'Access::Y'}}
+ // expected-note@#Access-Y-type {{implicitly declared private here}}
+ // expected-note@#Access-D-ctor {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
};
D b = {B(), {}};
class X {
using type = int;
};
- D x = {X(), {}}; // expected-error {{no viable constructor or deduction guide}}
+ D x = {X(), {}};
+ // expected-error at -1 {{no viable constructor or deduction guide}}
+ // expected-note@#Access-D {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
+ // expected-note@#Access-D {{candidate function template not viable: requires 1 argument, but 2 were provided}}
+ // expected-note@#Access-D-ctor {{candidate template ignored: substitution failure [with T = X]: 'type' is a private member of 'Access::X'}}
- // Once we implement proper support for dependent nested name specifiers in
- // friends, this should still work.
class Y {
- template <typename T> friend D<T>::D(T, typename T::type); // expected-warning {{dependent nested name specifier}}
- struct type {};
+ template <typename T> friend D<T>::D(T, typename T::type);
+ struct type {}; // #Access-Y-type
};
D y = {Y(), {}};
+ // expected-note at -1 {{in instantiation of template class 'Access::D<Access::Y>' requested here}}
class Z {
template <typename T> friend class D;
diff --git a/clang/test/SemaTemplate/dependent-friend-constraints.cpp b/clang/test/SemaTemplate/dependent-friend-constraints.cpp
new file mode 100644
index 0000000000000..d4aeb7180deb5
--- /dev/null
+++ b/clang/test/SemaTemplate/dependent-friend-constraints.cpp
@@ -0,0 +1,263 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+namespace test0 {
+ class C;
+
+ template <class T> struct A { // #test0-A
+ struct B {
+ static void f(C &);
+ };
+ };
+
+ class C {
+ int n; // #test0-C-n
+ template <class T>
+ requires __is_same(T, int) // #test0-requires
+ friend struct A<T>::B;
+ };
+
+ template <class T> void A<T>::B::f(C &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test0::C'}}
+ // expected-note@#test0-C-n {{implicitly declared private here}}
+ // expected-note@#test0-A {{candidate template ignored: constraints not satisfied [with T = double]}}
+ // expected-note@#test0-requires {{because '__is_same(double, int)' evaluated to false}}
+ }
+
+ template void A<int>::B::f(C &);
+ template void A<double>::B::f(C &);
+ // expected-note at -1 {{in instantiation of member function 'test0::A<double>::B::f' requested here}}
+}
+
+namespace test1 {
+ class C;
+
+ template <class T> struct A {
+ template <class U>
+ requires __is_same(T, U)
+ struct B;
+ };
+
+ class C {
+ int n;
+ template <class T>
+ template <class U>
+ requires __is_same(T, U)
+ friend struct A<T>::B;
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ requires __is_same(int, U)
+ struct B {
+ static void f(C &x) { x.n = 0; }
+ };
+ };
+
+ template struct A<int>::B<int>;
+}
+
+namespace test2 {
+ class C;
+
+ template <class T> struct A {
+ template <class U>
+ requires __is_same(T, U)
+ struct B;
+ };
+
+ class C {
+ int n; // #test2-C-n
+ template <class T>
+ template <class U>
+ requires __is_same(T, U)
+ friend struct A<T>::B;
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ requires (sizeof(U) != 0)
+ struct B {
+ static void f(C &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-n {{implicitly declared private here}}
+ }
+ };
+ };
+
+ template struct A<int>::B<int>;
+}
+
+namespace test3 {
+ class B;
+
+ template <class T> struct A {
+ template <class U>
+ requires __is_same(T, U)
+ static void f(B &);
+ };
+
+ class B {
+ int n;
+ template <class T>
+ template <class U>
+ requires __is_same(T, U)
+ friend void A<T>::f(B &);
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ requires __is_same(int, U)
+ static void f(B &x) {
+ x.n = 0;
+ }
+ };
+
+ template void A<int>::f<int>(B &);
+}
+
+namespace test4 {
+ class B;
+
+ template <class T> struct A {
+ template <class U>
+ requires __is_same(T, U)
+ static void f(B &);
+ };
+
+ class B {
+ int n; // #test4-B-n
+ template <class T>
+ template <class U>
+ requires __is_same(T, U)
+ friend void A<T>::f(B &);
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ requires (sizeof(U) != 0)
+ static void f(B &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test4::B'}}
+ // expected-note@#test4-B-n {{implicitly declared private here}}
+ }
+ };
+
+ template void A<int>::f<int>(B &);
+}
+
+namespace test5 {
+ class B;
+
+ template <class T> struct A {
+ template <class U>
+ static void f(B &) requires (sizeof(U) != 0);
+ };
+
+ class B {
+ int n;
+ template <class T>
+ template <class U>
+ friend void A<T>::f(B &) requires (sizeof(U) != 0);
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ static void f(B &x) requires (sizeof(U) != 0) {
+ x.n = 0;
+ }
+ };
+
+ template void A<int>::f<int>(B &);
+}
+
+namespace test6 {
+ class B;
+
+ template <class T> struct A {
+ template <class U>
+ static void f(B &) requires (sizeof(U) != 0);
+ };
+
+ class B {
+ int n; // #test6-B-n
+ template <class T>
+ template <class U>
+ friend void A<T>::f(B &) requires (sizeof(U) != 0);
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ static void f(B &x) requires (sizeof(U) > 1) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test6::B'}}
+ // expected-note@#test6-B-n {{implicitly declared private here}}
+ }
+ };
+
+ template void A<int>::f<int>(B &);
+}
+
+namespace test7 {
+ template <class T> struct A; // #test7-A
+
+ template <class V> class C {
+ int n; // #test7-C-n
+ template <class T>
+ requires __is_same(T, V) // #test7-requires
+ friend struct A<T>::B;
+ };
+
+ template <> struct A<int> {
+ struct B {
+ static void f(C<int> &x) { x.n = 0; }
+ };
+ };
+
+ template <> struct A<double> {
+ struct B {
+ static void f(C<int> &x) {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test7::C<int>'}}
+ // expected-note@#test7-C-n {{implicitly declared private here}}
+ // expected-note@#test7-A {{candidate template ignored: constraints not satisfied [with T = double]}}
+ // expected-note@#test7-requires {{because '__is_same(double, int)' evaluated to false}}
+ }
+ };
+ };
+}
+
+namespace test8 {
+ template <class T, class U>
+ concept Same = __is_same(T, U);
+
+ class B;
+
+ template <class T> struct A {
+ template <class U>
+ static void f(B &) requires Same<T, T>;
+ };
+
+ template <> struct A<int> {
+ template <class U>
+ static void f(B &) requires Same<U, U>;
+ };
+
+ class B {
+ int n; // #test8-B-n
+
+ template <class T>
+ template <class U>
+ friend void A<T>::f(B &) requires Same<T, T>;
+ };
+
+ template <class U>
+ void A<int>::f(B &x) requires Same<U, U> {
+ x.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test8::B'}}
+ // expected-note@#test8-B-n {{implicitly declared private here}}
+ }
+
+ template void A<int>::f<double>(B &);
+}
diff --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 2b5a226c3b33c..05ab57cebfec8 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -111,6 +111,31 @@ namespace test5 {
};
}
+namespace GH104057 {
+template <class T>
+struct A { // #GH104057-A
+ template <class> struct B;
+
+private:
+ static void f(); // #GH104057-A-f
+ template <class U> friend struct A<U *>::B;
+};
+
+template <class T>
+template <class U> struct A<T>::B {
+ static void g() {
+ A<int>::f();
+ // expected-error at -1 {{'f' is a private member of 'GH104057::A<int>'}}
+ // expected-note@#GH104057-A-f {{declared private here}}
+ // expected-note@#GH104057-A {{candidate friend template ignored: could not match 'U *' against 'double'}}
+ }
+};
+
+void test() {
+ A<double>::B<int>::g(); // expected-note {{in instantiation of member function 'GH104057::A<double>::B<int>::g' requested here}}
+}
+}
+
// PR6022
namespace PR6022 {
template <class T1, class T2 , class T3 > class A;
@@ -235,20 +260,19 @@ namespace rdar11147355 {
template <class T>
struct A {
template <class U> class B;
- template <class S> template <class U> friend class A<S>::B; // expected-warning {{dependent nested name specifier 'A<S>' for friend template declaration is not supported; ignoring this friend declaration}}
+ template <class S> template <class U> friend class A<S>::B;
private:
- int n; // expected-note {{here}}
+ int n;
};
template <class S> template <class U> class A<S>::B {
public:
- // FIXME: This should be permitted.
- int f(A<S*> a) { return a.n; } // expected-error {{private}}
+ int f(A<S*> a) { return a.n; }
};
A<double>::B<double> ab;
A<double*> a;
- int k = ab.f(a); // expected-note {{instantiation of}}
+ int k = ab.f(a);
}
namespace RedeclUnrelated {
diff --git a/clang/test/SemaTemplate/nested-template.cpp b/clang/test/SemaTemplate/nested-template.cpp
index b5da1b1c3ccd4..fcf9a1d647ab9 100644
--- a/clang/test/SemaTemplate/nested-template.cpp
+++ b/clang/test/SemaTemplate/nested-template.cpp
@@ -168,3 +168,17 @@ namespace RefPack {
int arr[10];
void g() { A<k>().f(arr); }
}
+
+namespace GH104057 {
+ template <class T>
+ struct A {
+ template <bool> struct B {};
+ };
+
+ template <class T>
+ struct C {
+ using type = typename A<T>::template B<true>;
+ };
+
+ A<int>::B<true> *p = (C<int>::type *)0;
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index e485e6e2e5547..47d6d2cb51efd 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1312,6 +1312,14 @@ bool CursorVisitor::VisitFriendDecl(FriendDecl *D) {
return false;
}
+bool CursorVisitor::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ for (TemplateParameterList *TPL : D->getTemplateParameterLists())
+ if (VisitTemplateParameters(TPL))
+ return true;
+
+ return VisitFriendDecl(D);
+}
+
bool CursorVisitor::VisitDecompositionDecl(DecompositionDecl *D) {
for (auto *B : D->bindings()) {
if (Visit(MakeCXCursor(B, TU, RegionOfInterest)))
diff --git a/clang/tools/libclang/CursorVisitor.h b/clang/tools/libclang/CursorVisitor.h
index d5ab699756988..1b359d2920e11 100644
--- a/clang/tools/libclang/CursorVisitor.h
+++ b/clang/tools/libclang/CursorVisitor.h
@@ -248,6 +248,7 @@ class CursorVisitor : public DeclVisitor<CursorVisitor, bool>,
bool VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
bool VisitStaticAssertDecl(StaticAssertDecl *D);
bool VisitFriendDecl(FriendDecl *D);
+ bool VisitFriendTemplateDecl(FriendTemplateDecl *D);
bool VisitDecompositionDecl(DecompositionDecl *D);
bool VisitConceptDecl(ConceptDecl *D);
bool VisitTypeConstraint(const TypeConstraint &TC);
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 4919977300fb8..195b8ab4c4e66 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -666,6 +666,126 @@ TEST(Decl, TemplateArgumentDefaulted) {
EXPECT_TRUE(ArgList.get(3).getIsDefaulted());
}
+TEST(Decl, InstantiatedDependentFriendTemplate) {
+ StringRef Code = R"cpp(
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+
+ template <class T> struct C {
+ template <class U> friend struct A<T>::B;
+ };
+
+ template struct C<int>;
+ )cpp";
+
+ auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
+ ASTContext &Ctx = AST->getASTContext();
+ const auto *CTemplate = selectFirst<ClassTemplateDecl>(
+ "c", match(classTemplateDecl(hasName("C")).bind("c"), Ctx));
+ ASSERT_NE(CTemplate, nullptr);
+
+ const FriendTemplateDecl *DependentFriend = nullptr;
+ for (const Decl *D : CTemplate->getTemplatedDecl()->decls()) {
+ if (const auto *FTD = dyn_cast<FriendTemplateDecl>(D)) {
+ DependentFriend = FTD;
+ break;
+ }
+ }
+ ASSERT_NE(DependentFriend, nullptr);
+ ASSERT_NE(DependentFriend->getFriendType(), nullptr);
+ EXPECT_TRUE(DependentFriend->getFriendTemplateName().isDependent());
+
+ const auto *CSpecialization = selectFirst<ClassTemplateSpecializationDecl>(
+ "c", match(classTemplateSpecializationDecl(hasName("C")).bind("c"), Ctx));
+ ASSERT_NE(CSpecialization, nullptr);
+
+ const FriendTemplateDecl *InstFriend = nullptr;
+ for (const Decl *D : CSpecialization->decls()) {
+ if (const auto *FTD = dyn_cast<FriendTemplateDecl>(D)) {
+ InstFriend = FTD;
+ break;
+ }
+ }
+ ASSERT_NE(InstFriend, nullptr);
+ ASSERT_EQ(InstFriend->getFriendType(), nullptr);
+ ASSERT_FALSE(InstFriend->getFriendTemplateName().isNull());
+
+ const FriendDecl *Friend = InstFriend;
+ EXPECT_EQ(Friend->getFriendDecl(),
+ InstFriend->getFriendTemplateName().getAsTemplateDecl());
+ EXPECT_EQ(InstFriend->getSourceRange().getEnd(), InstFriend->getLocation());
+}
+
+TEST(Decl, InstantiatedDependentFriendTemplateParameters) {
+ StringRef Code = R"cpp(
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+
+ template <class V> struct C {
+ template <class U>
+ friend struct A<U>::template B<V>;
+ };
+
+ template struct C<int>;
+ )cpp";
+
+ auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
+ ASTContext &Ctx = AST->getASTContext();
+ const auto *CSpecialization = selectFirst<ClassTemplateSpecializationDecl>(
+ "c", match(classTemplateSpecializationDecl(hasName("C")).bind("c"), Ctx));
+ ASSERT_NE(CSpecialization, nullptr);
+ ASSERT_NE(CSpecialization->friend_begin(), CSpecialization->friend_end());
+
+ const auto *Friend =
+ dyn_cast<FriendTemplateDecl>(*CSpecialization->friend_begin());
+ ASSERT_NE(Friend, nullptr);
+ ASSERT_EQ(Friend->getTemplateParameterLists().size(), 1u);
+ ASSERT_NE(Friend->getFriendType(), nullptr);
+
+ TemplateSpecializationTypeLoc FriendTL =
+ Friend->getFriendType()
+ ->getTypeLoc()
+ .castAs<TemplateSpecializationTypeLoc>();
+ const Type *QualifierType =
+ FriendTL.getQualifierLoc().getNestedNameSpecifier().getAsType();
+ ASSERT_NE(QualifierType, nullptr);
+ const auto *TST = QualifierType->getAs<TemplateSpecializationType>();
+ ASSERT_NE(TST, nullptr);
+ const auto *TTP =
+ TST->template_arguments()[0].getAsType()->getAs<TemplateTypeParmType>();
+ ASSERT_NE(TTP, nullptr);
+ EXPECT_EQ(TTP->getDecl(),
+ Friend->getTemplateParameterLists().front()->getParam(0));
+}
+
+TEST(Decl, InvalidFunctionFriendIsRetained) {
+ StringRef Code = R"cpp(
+ int f();
+ struct A {
+ friend void f();
+ };
+ )cpp";
+
+ IgnoringDiagConsumer Diags;
+ auto AST = tooling::buildASTFromCodeWithArgs(
+ Code, {"-std=c++20"}, "input.cc", "clang-tool",
+ std::make_shared<PCHContainerOperations>(),
+ tooling::getClangStripDependencyFileAdjuster(),
+ tooling::FileContentMappings(), &Diags);
+ ASTContext &Ctx = AST->getASTContext();
+ const auto *Record = selectFirst<CXXRecordDecl>(
+ "a", match(cxxRecordDecl(hasName("A"), isDefinition()).bind("a"), Ctx));
+ ASSERT_NE(Record, nullptr);
+ ASSERT_NE(Record->friend_begin(), Record->friend_end());
+
+ const FriendDecl *Friend = *Record->friend_begin();
+ EXPECT_TRUE(Friend->isInvalidDecl());
+ ASSERT_NE(Friend->getFriendDecl(), nullptr);
+ EXPECT_TRUE(Friend->getFriendDecl()->isInvalidDecl());
+}
+
TEST(Decl, CXXDestructorDeclsShouldHaveWellFormedNameInfoRanges) {
// GH71161
llvm::Annotations Code(R"cpp(
diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp
index 24e20c7471f3c..a368474f2841a 100644
--- a/clang/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp
@@ -863,6 +863,25 @@ TEST_F(StructuralEquivalenceRecordTest, SameFriendsSameOrder) {
EXPECT_TRUE(testStructuralMatch(t));
}
+TEST_F(StructuralEquivalenceRecordTest, InstantiatedFriendTemplates) {
+ std::string Code = R"(
+ template <class T> struct A {
+ template <class U> struct B;
+ };
+ template <class T> struct C {
+ template <class U> friend struct A<T>::B;
+ };
+ template struct C<int>;
+ )";
+ auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
+ Code, Code, Lang_CXX11, classTemplateSpecializationDecl(hasName("C")));
+ ASSERT_NE(get<0>(Decls)->friend_begin(), get<0>(Decls)->friend_end());
+ auto *Friend = dyn_cast<FriendTemplateDecl>(*get<0>(Decls)->friend_begin());
+ ASSERT_NE(Friend, nullptr);
+ ASSERT_EQ(Friend->getFriendType(), nullptr);
+ EXPECT_TRUE(testStructuralMatch(Decls));
+}
+
struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) {
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index a2a96f470d94a..ec29200198554 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -4516,7 +4516,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
<td>CD2</td>
<td>Explicit specialization and friendship</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr id="639">
<td><a href="https://cplusplus.github.io/CWG/issues/639.html">639</a></td>
@@ -12819,7 +12819,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
<td>CD5</td>
<td>Determining “corresponding members” for friendship</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr id="1863">
<td><a href="https://cplusplus.github.io/CWG/issues/1863.html">1863</a></td>
@@ -13215,7 +13215,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
<td>CD5</td>
<td><TT>friend</TT> templates with dependent scopes</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr class="open" id="1919">
<td><a href="https://cplusplus.github.io/CWG/issues/1919.html">1919</a></td>
@@ -13404,7 +13404,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/temp.friend">temp.friend</a>]</td>
<td>CD5</td>
<td>Friend declarations naming members of class templates in non-templates</td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr id="1946">
<td><a href="https://cplusplus.github.io/CWG/issues/1946.html">1946</a></td>
More information about the cfe-commits
mailing list