[clang] [Clang] support friend declarations with a dependent nested-name-specifier (PR #191268)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 07:59:43 PDT 2026
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/191268
>From 7f3f8fcd0c2e4fef014b408d97288c17783e76f3 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 9 Apr 2026 18:04:29 +0300
Subject: [PATCH 1/3] [Clang] support friend declarations with a dependent
nested-name-specifier
---
.../clang/AST/ASTStructuralEquivalence.h | 2 +
clang/include/clang/AST/DeclFriend.h | 117 ++----
clang/include/clang/AST/DeclTemplate.h | 67 ++--
clang/include/clang/AST/RecursiveASTVisitor.h | 5 +-
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Sema/Sema.h | 3 +
clang/include/clang/Sema/Template.h | 2 +-
clang/lib/AST/ASTImporter.cpp | 125 +++++-
clang/lib/AST/ASTStructuralEquivalence.cpp | 48 +++
clang/lib/AST/DeclFriend.cpp | 64 +--
clang/lib/AST/DeclPrinter.cpp | 17 +-
clang/lib/AST/DeclTemplate.cpp | 39 +-
clang/lib/Sema/Sema.cpp | 5 +-
clang/lib/Sema/SemaAccess.cpp | 379 +++++++++++++++---
clang/lib/Sema/SemaDeclCXX.cpp | 209 ++++++----
clang/lib/Sema/SemaTemplate.cpp | 14 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 102 +++--
clang/lib/Serialization/ASTReaderDecl.cpp | 16 +-
clang/lib/Serialization/ASTWriterDecl.cpp | 14 +-
.../class.access/class.friend/p3-cxx0x.cpp | 4 +-
clang/test/CXX/drs/cwg18xx.cpp | 11 +-
clang/test/CXX/drs/cwg19xx.cpp | 8 +-
clang/test/CXX/drs/cwg6xx.cpp | 17 +-
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 151 ++++++-
.../CXX/temp/temp.decls/temp.friend/p6.cpp | 24 ++
.../SemaCXX/many-template-parameter-lists.cpp | 6 +-
clang/test/SemaTemplate/GH71595.cpp | 6 +-
clang/test/SemaTemplate/ctad.cpp | 9 +-
29 files changed, 1050 insertions(+), 436 deletions(-)
create mode 100644 clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
diff --git a/clang/include/clang/AST/ASTStructuralEquivalence.h b/clang/include/clang/AST/ASTStructuralEquivalence.h
index 6f82de1ae136d..89d7f8d6ba8ff 100644
--- a/clang/include/clang/AST/ASTStructuralEquivalence.h
+++ b/clang/include/clang/AST/ASTStructuralEquivalence.h
@@ -135,6 +135,8 @@ struct StructuralEquivalenceContext {
/// \c VisitedDecls members) and can cause faulty equivalent results.
bool IsEquivalent(Stmt *S1, Stmt *S2);
+ bool IsEquivalent(TemplateParameterList *TPL1, TemplateParameterList *TPL2);
+
/// Find the index of the given anonymous struct/union within its
/// context.
///
diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 1f8c210263677..2cd5a1af17fd8 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,20 +15,14 @@
#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>
namespace clang {
@@ -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,35 @@ class FriendDecl final
friend class CXXRecordDecl;
friend class CXXRecordDecl::friend_iterator;
- // 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;
+ SourceLocation FriendLoc;
+
/// 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());
- }
+protected:
+ // The declaration that's a friend of this class.
+ FriendUnion Friend;
- FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
- : Decl(Decl::Friend, Empty), UnsupportedFriend(false),
- NumTPLists(NumFriendTypeTPLists) {}
+ LazyDeclPtr NextFriend;
+
+ FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend,
+ SourceLocation FriendL, SourceLocation EllipsisLoc = {})
+ : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FriendL),
+ UnsupportedFriend(false), Friend(Friend), NextFriend() {}
+
+ FriendDecl(Kind K, EmptyShell Empty)
+ : Decl(K, Empty), UnsupportedFriend(false) {}
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 +90,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,58 +104,18 @@ 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 {
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; }
+
+ SourceRange getSourceRange() const override LLVM_READONLY;
/// Determines if this friend kind is unsupported.
bool isUnsupportedFriend() const {
@@ -191,9 +129,10 @@ class FriendDecl final
// 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 == Decl::Friend || K == Decl::FriendTemplate;
+ }
};
-
/// An iterator over the friend declarations of a class.
class CXXRecordDecl::friend_iterator {
friend class CXXRecordDecl;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4a1bb9c13c79..1653c26910b8b 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"
@@ -2473,42 +2474,43 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
///
/// \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 : 31;
- // 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 *> FriendTypeTPLists)
+ : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
+ EllipsisLoc),
+ NumTPLists(FriendTypeTPLists.size()) {
+ llvm::copy(FriendTypeTPLists, getTrailingObjects());
+ }
- FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {}
+ FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
+ : FriendDecl(Decl::FriendTemplate, Empty),
+ NumTPLists(NumFriendTypeTPLists) {}
public:
friend class ASTDeclReader;
+ friend class ASTDeclWriter;
+ friend TrailingObjects;
static FriendTemplateDecl *
Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
- MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend,
- SourceLocation FriendLoc);
+ FriendUnion Friend, SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> FriendTypeTPLists = {},
+ SourceLocation EllipsisLoc = {});
- static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
+ static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned FriendTypeNumTPLists);
+
+ SourceRange getSourceRange() const override LLVM_READONLY;
/// If this friend declaration names a templated type (or
/// a dependent member type of a templated type), return that
@@ -2524,19 +2526,12 @@ class FriendTemplateDecl : public Decl {
return Friend.dyn_cast<NamedDecl*>();
}
- /// Retrieves the location of the 'friend' keyword.
- SourceLocation getFriendLoc() const {
- return FriendLoc;
+ TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
+ assert(N < NumTPLists);
+ return getTrailingObjects()[N];
}
- TemplateParameterList *getTemplateParameterList(unsigned i) const {
- assert(i <= NumParams);
- return Params[i];
- }
-
- unsigned getNumTemplateParameters() const {
- return NumParams;
- }
+ unsigned getFriendTypeNumTemplateParameterLists() const { return NumTPLists; }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index ce6ad723191e0..c06ceec13b4dd 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1727,8 +1727,9 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, {
TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
else
TRY_TO(TraverseDecl(D->getFriendDecl()));
- for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) {
- TemplateParameterList *TPL = D->getTemplateParameterList(I);
+ for (unsigned I = 0, E = D->getFriendTypeNumTemplateParameterLists(); I < E;
+ ++I) {
+ TemplateParameterList *TPL = D->getFriendTypeTemplateParameterList(I);
for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
ITPL != ETPL; ++ITPL) {
TRY_TO(TraverseDecl(*ITPL));
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6d2fae551566f..9835c66c71816 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1921,6 +1921,8 @@ 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_dependent_friend_not_member : Error<
+ "friend declaration does not name a member of a class template specialization">;
def err_invalid_base_in_interface : Error<
"interface type cannot inherit from "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 760555d9c8b9b..544d892c1535c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12012,6 +12012,9 @@ class Sema final : public SemaBase {
bool CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous);
void CompleteMemberSpecialization(NamedDecl *Member, LookupResult &Previous);
+ bool CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+ TemplateParameterList *FPL);
+
// Explicit instantiation of a class template specialization
DeclResult ActOnExplicitInstantiation(
Scope *S, SourceLocation ExternLoc, SourceLocation TemplateLoc,
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index b0170c21feb1a..62b1c16de82c1 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -715,7 +715,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);
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 41ba98c53247d..782e4d710599c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -537,6 +537,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);
@@ -4541,10 +4542,40 @@ static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
Importer.getToContext().getLangOpts(), FD1->getASTContext(),
FD2->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default,
- /* StrictTypeSpelling = */ false, /* Complain = */ false);
+ /*StrictTypeSpelling=*/false, /*Complain=*/false);
return Ctx.IsEquivalent(FD1, FD2);
}
+static bool IsEquivalentFriend(ASTImporter &Importer, FriendTemplateDecl *FTD1,
+ FriendTemplateDecl *FTD2) {
+ if (FTD1->getFriendTypeNumTemplateParameterLists() !=
+ FTD2->getFriendTypeNumTemplateParameterLists())
+ return false;
+
+ ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
+ StructuralEquivalenceContext Ctx(
+ Importer.getToContext().getLangOpts(), FTD1->getASTContext(),
+ FTD2->getASTContext(), NonEquivalentDecls,
+ StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
+ /*Complain=*/false);
+
+ for (unsigned I = 0, N = FTD1->getFriendTypeNumTemplateParameterLists();
+ I != N; ++I) {
+ if (!Ctx.IsEquivalent(FTD1->getFriendTypeTemplateParameterList(I),
+ FTD2->getFriendTypeTemplateParameterList(I)))
+ return false;
+ }
+
+ if ((!FTD1->getFriendType()) != (!FTD2->getFriendType()))
+ return false;
+
+ if (const TypeSourceInfo *TSI = FTD1->getFriendType())
+ return Importer.IsStructurallyEquivalent(
+ TSI->getType(), FTD2->getFriendType()->getType(), /*Complain=*/false);
+
+ return Ctx.IsEquivalent(FTD1, FTD2);
+}
+
static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
FriendDecl *FD) {
unsigned int FriendCount = 0;
@@ -4561,7 +4592,6 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
}
assert(FriendPosition && "Friend decl not found in own parent.");
-
return {FriendCount, *FriendPosition};
}
@@ -4576,9 +4606,11 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
// We try to maintain order and count of redundant friend declarations.
const auto *RD = cast<CXXRecordDecl>(DC);
SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
- for (FriendDecl *ImportedFriend : RD->friends())
- if (IsEquivalentFriend(Importer, D, ImportedFriend))
- ImportedEquivalentFriends.push_back(ImportedFriend);
+ for (FriendDecl *ImportedFriend : RD->friends()) {
+ if (ImportedFriend->getKind() == Decl::Friend &&
+ IsEquivalentFriend(Importer, D, cast<FriendDecl>(ImportedFriend)))
+ ImportedEquivalentFriends.push_back(cast<FriendDecl>(ImportedFriend));
+ }
FriendCountAndPosition CountAndPosition =
getFriendCountAndPosition(Importer, D);
@@ -4609,15 +4641,6 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
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 LocationOrErr = import(D->getLocation());
if (!LocationOrErr)
return LocationOrErr.takeError();
@@ -4631,7 +4654,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());
@@ -4640,6 +4663,78 @@ 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()) {
+ if (isa<FriendTemplateDecl>(ImportedFriend) &&
+ IsEquivalentFriend(Importer, D,
+ cast<FriendTemplateDecl>(ImportedFriend)))
+ ImportedEquivalentFriends.push_back(
+ cast<FriendTemplateDecl>(ImportedFriend));
+ }
+
+ 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 (NamedDecl *FriendD = D->getFriendDecl()) {
+ NamedDecl *ToFriendD;
+ if (Error Err = importInto(ToFriendD, FriendD))
+ return std::move(Err);
+ ToFU = ToFriendD;
+ } else {
+ if (auto TSIOrErr = import(D->getFriendType()))
+ ToFU = *TSIOrErr;
+ else
+ return TSIOrErr.takeError();
+ }
+
+ SmallVector<TemplateParameterList *, 1> ToParams(
+ D->getFriendTypeNumTemplateParameterLists());
+ for (unsigned I = 0, N = D->getFriendTypeNumTemplateParameterLists(); I != N;
+ ++I) {
+ if (auto ParamsOrErr = import(D->getFriendTypeTemplateParameterList(I)))
+ ToParams[I] = *ParamsOrErr;
+ else
+ return ParamsOrErr.takeError();
+ }
+
+ 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, ToParams,
+ *EllipsisLocOrErr))
+ return FTD;
+
+ FTD->setUnsupportedFriend(D->isUnsupportedFriend());
+ 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 9d970651a9e65..8da7cca6665fb 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2430,6 +2430,35 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ FriendTemplateDecl *FTD1,
+ FriendTemplateDecl *FTD2) {
+ if (FTD1->getFriendTypeNumTemplateParameterLists() !=
+ FTD2->getFriendTypeNumTemplateParameterLists())
+ return false;
+
+ for (unsigned I = 0, N = FTD1->getFriendTypeNumTemplateParameterLists();
+ I != N; ++I) {
+ if (!Context.IsEquivalent(FTD1->getFriendTypeTemplateParameterList(I),
+ FTD2->getFriendTypeTemplateParameterList(I)))
+ return false;
+ }
+
+ if ((FTD1->getFriendType() && FTD2->getFriendDecl()) ||
+ (FTD1->getFriendDecl() && FTD2->getFriendType()))
+ return false;
+
+ if (FTD1->getFriendDecl() && FTD2->getFriendDecl())
+ return IsStructurallyEquivalent(Context, FTD1->getFriendDecl(),
+ FTD2->getFriendDecl());
+
+ if (FTD1->getFriendType() && FTD2->getFriendType())
+ return IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
+ FTD2->getFriendType()->getType());
+
+ return false;
+}
+
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TypedefNameDecl *D1, TypedefNameDecl *D2) {
if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
@@ -2769,6 +2798,25 @@ bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
return true;
}
+bool StructuralEquivalenceContext::IsEquivalent(TemplateParameterList *TPL1,
+ TemplateParameterList *TPL2) {
+ if (TPL1 == TPL2)
+ return true;
+
+ if (!TPL1 || !TPL2)
+ return false;
+
+ if (TPL1->size() != TPL2->size())
+ return false;
+
+ for (unsigned I = 0, N = TPL1->size(); I != N; ++I) {
+ if (!IsEquivalent(TPL1->getParam(I), TPL2->getParam(I)))
+ return false;
+ }
+
+ return true;
+}
+
bool StructuralEquivalenceContext::CheckKindSpecificEquivalence(
Decl *D1, Decl *D2) {
diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp
index 6bfc2eb62b284..aea0600441d25 100644
--- a/clang/lib/AST/DeclFriend.cpp
+++ b/clang/lib/AST/DeclFriend.cpp
@@ -13,11 +13,9 @@
#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>
@@ -25,16 +23,9 @@ 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 +37,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 +60,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 5e377a6c0c247..755f93e1ab22f 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -888,24 +888,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);
}
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 99d02fdc99e92..6c19c347d87fc 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1231,21 +1231,34 @@ 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 *> FriendTypeTPLists,
+ SourceLocation EllipsisLoc) {
+ std::size_t Extra =
+ FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+ FriendTypeTPLists.size());
+ auto *FTD = new (Context, DC, Extra) FriendTemplateDecl(
+ DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTypeTPLists);
+ cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+ return FTD;
}
-FriendTemplateDecl *FriendTemplateDecl::CreateDeserialized(ASTContext &C,
- GlobalDeclID ID) {
- return new (C, ID) FriendTemplateDecl(EmptyShell());
+FriendTemplateDecl *
+FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
+ unsigned NumFriendTypeTPLists) {
+ std::size_t Extra =
+ FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+ NumFriendTypeTPLists);
+ return new (C, ID, Extra)
+ FriendTemplateDecl(EmptyShell(), NumFriendTypeTPLists);
+}
+
+SourceRange FriendTemplateDecl::getSourceRange() const {
+ SourceLocation Begin =
+ getFriendTypeTemplateParameterList(0)->getTemplateLoc();
+ SourceLocation End = FriendDecl::getSourceRange().getEnd();
+ return SourceRange(Begin, End);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index ef45d5842c795..27c99ef7691a6 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1163,8 +1163,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);
@@ -1173,7 +1174,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 17415b4185eff..c516aee3e62d6 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -21,6 +21,7 @@
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Template.h"
using namespace clang;
using namespace sema;
@@ -274,6 +275,99 @@ struct AccessTarget : public AccessedEntity {
}
+static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+ ArrayRef<TemplateArgument> PatternArgs,
+ ArrayRef<TemplateArgument> Args,
+ SourceLocation Loc) {
+ if (PatternArgs.size() != Args.size())
+ return false;
+
+ auto Equal =
+ llvm::equal(PatternArgs, Args,
+ [](const TemplateArgument &LHS, const TemplateArgument &RHS) {
+ return LHS.structurallyEquals(RHS);
+ });
+ if (Equal)
+ return true;
+
+ TemplateDeductionInfo Info(Loc);
+ SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
+ S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
+ /*NumberOfArgumentsMustMatch=*/false);
+
+ for (const DeducedTemplateArgument &Arg : Deduced)
+ if (Arg.isNull())
+ return false;
+
+ return true;
+}
+
+static CanQual<FunctionProtoType>
+GetCanonicalFunctionProto(Sema &S, const FunctionDecl *FD) {
+ return S.Context.getCanonicalType(FD->getType())->getAs<FunctionProtoType>();
+}
+
+static const TemplateSpecializationType *
+TryGetTemplateSpecializationType(Sema &S, NestedNameSpecifier NNS) {
+ if (!NNS)
+ return nullptr;
+
+ QualType Ty(NNS.getAsType(), 0);
+ if (Ty.isNull())
+ return nullptr;
+
+ Ty = S.Context.getCanonicalType(Ty);
+ if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
+ Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(S.Context);
+
+ return Ty->getAs<TemplateSpecializationType>();
+}
+
+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 bool MatchesFriendContext(Sema &S, FunctionDecl *FD,
+ ClassTemplateDecl *FriendCTD,
+ ArrayRef<TemplateArgument> FriendArgs,
+ TemplateParameterList *FriendTPL,
+ SourceLocation Loc) {
+ const auto *RD = dyn_cast<CXXRecordDecl>(FD->getDeclContext());
+ if (!RD)
+ return false;
+
+ ClassTemplateDecl *ContextCTD = RD->getDescribedClassTemplate();
+ ArrayRef<TemplateArgument> ContextArgs;
+ if (ContextCTD) {
+ ContextArgs = ContextCTD->getInjectedTemplateArgs(S.Context);
+ } else {
+ const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+ if (!CTSD)
+ return false;
+ ContextCTD = CTSD->getSpecializedTemplate();
+ ContextArgs = CTSD->getTemplateArgs().asArray();
+ }
+
+ if (ContextCTD->getCanonicalDecl() != FriendCTD->getCanonicalDecl())
+ return false;
+
+ return CanDeduceTemplateArguments(S, FriendTPL, FriendArgs, ContextArgs, Loc);
+}
+
/// Checks whether one class might instantiate to the other.
static bool MightInstantiateTo(const CXXRecordDecl *From,
const CXXRecordDecl *To) {
@@ -283,8 +377,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;
@@ -342,7 +440,6 @@ static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
return OnFailure;
}
-
static bool MightInstantiateTo(Sema &S, DeclContext *Context,
DeclContext *Friend) {
if (Friend == Context)
@@ -374,49 +471,67 @@ 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(Sema &S, 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(S, Context->getReturnType(), Friend->getReturnType()))
+ return false;
+
+ for (unsigned I = 0, E = Friend->getNumParams(); I != E; ++I)
+ if (!MightInstantiateTo(S, Context->getParamType(I),
+ Friend->getParamType(I)))
+ return false;
+
+ return true;
+}
+
+static bool MightInstantiateTo(Sema &S, DeclarationName Context,
+ DeclarationName Friend) {
+ if (Context == Friend)
+ return true;
+
+ if (Context.getNameKind() != Friend.getNameKind())
return false;
- CanQual<FunctionProtoType> FriendTy
- = S.Context.getCanonicalType(Friend->getType())
- ->getAs<FunctionProtoType>();
- CanQual<FunctionProtoType> ContextTy
- = S.Context.getCanonicalType(Context->getType())
- ->getAs<FunctionProtoType>();
+ switch (Context.getNameKind()) {
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ return MightInstantiateTo(
+ S, S.Context.getCanonicalType(Context.getCXXNameType()),
+ S.Context.getCanonicalType(Friend.getCXXNameType()));
- // There isn't any way that I know of to add qualifiers
- // during instantiation.
- if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
+ default:
return false;
+ }
+}
- if (FriendTy->getNumParams() != ContextTy->getNumParams())
+static bool MightInstantiateTo(Sema &S, FunctionDecl *Context,
+ FunctionDecl *Friend) {
+ if (!MightInstantiateTo(S, Context->getDeclName(), Friend->getDeclName()))
return false;
- if (!MightInstantiateTo(S, ContextTy->getReturnType(),
- FriendTy->getReturnType()))
+ DeclContext *ContextDC = Context->getDeclContext();
+ DeclContext *FriendDC = Friend->getDeclContext();
+
+ if (!FriendDC->isDependentContext() &&
+ !MightInstantiateTo(S, ContextDC, FriendDC))
return false;
- for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
- if (!MightInstantiateTo(S, ContextTy->getParamType(I),
- FriendTy->getParamType(I)))
- return false;
+ CanQual<FunctionProtoType> FriendTy = GetCanonicalFunctionProto(S, Friend);
+ CanQual<FunctionProtoType> ContextTy = GetCanonicalFunctionProto(S, Context);
- return true;
+ return MightInstantiateTo(S, ContextTy, FriendTy);
}
-static bool MightInstantiateTo(Sema &S,
- FunctionTemplateDecl *Context,
+static bool MightInstantiateTo(Sema &S, FunctionTemplateDecl *Context,
FunctionTemplateDecl *Friend) {
- return MightInstantiateTo(S,
- Context->getTemplatedDecl(),
+ return MightInstantiateTo(S, Context->getTemplatedDecl(),
Friend->getTemplatedDecl());
}
@@ -533,14 +648,10 @@ 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;
@@ -551,6 +662,166 @@ static AccessResult MatchesFriend(Sema &S,
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, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ FunctionTemplateDecl *FriendFTD) {
+ AccessResult OnFailure = AR_inaccessible;
+ NestedNameSpecifier FriendNNS = FriendFTD->getTemplatedDecl()->getQualifier();
+ const auto *FriendTST = TryGetTemplateSpecializationType(S, FriendNNS);
+ if (!FriendTST)
+ return OnFailure;
+
+ auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+ FriendTST->getTemplateName().getAsTemplateDecl());
+ if (!FriendCTD)
+ return OnFailure;
+
+ TemplateParameterList *FriendTPL =
+ FriendTD->getFriendTypeTemplateParameterList(0);
+
+ if (!FriendTPL)
+ return OnFailure;
+
+ ArrayRef<TemplateArgument> FriendArgs = FriendTST->template_arguments();
+ SourceLocation FriendLoc = FriendTD->getLocation();
+
+ for (FunctionDecl *FD : EC.Functions) {
+ if (!MatchesFriendContext(S, FD, FriendCTD, FriendArgs, FriendTPL,
+ FriendLoc))
+ continue;
+
+ FunctionTemplateDecl *ContextFTD = TryGetFunctionTemplateDecl(FD);
+ if (ContextFTD && MightInstantiateTo(S, FriendFTD, ContextFTD))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ FunctionDecl *FriendFD) {
+ AccessResult OnFailure = AR_inaccessible;
+ NestedNameSpecifier FriendNNS = FriendFD->getQualifier();
+ const auto *FriendTST = TryGetTemplateSpecializationType(S, FriendNNS);
+ if (!FriendTST)
+ return OnFailure;
+
+ auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+ FriendTST->getTemplateName().getAsTemplateDecl());
+ if (!FriendCTD)
+ return OnFailure;
+
+ TemplateParameterList *FriendTPL =
+ FriendTD->getFriendTypeTemplateParameterList(0);
+ if (!FriendTPL)
+ return OnFailure;
+
+ CanQual<FunctionProtoType> FriendProto =
+ GetCanonicalFunctionProto(S, FriendFD);
+
+ ArrayRef<TemplateArgument> FriendArgs = FriendTST->template_arguments();
+ SourceLocation FriendLoc = FriendTD->getLocation();
+
+ for (FunctionDecl *FD : EC.Functions) {
+ if (!MightInstantiateTo(S, FD->getDeclName(), FriendFD->getDeclName()))
+ continue;
+
+ if (!MatchesFriendContext(S, FD, FriendCTD, FriendArgs, FriendTPL,
+ FriendLoc))
+ continue;
+
+ CanQual<FunctionProtoType> ContextProto = GetCanonicalFunctionProto(S, FD);
+ if (MightInstantiateTo(S, ContextProto, FriendProto))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD, NamedDecl *ND) {
+ if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND))
+ return MatchesFriend(S, EC, FTD, TD);
+
+ if (auto *FD = dyn_cast<FunctionDecl>(ND))
+ return MatchesFriend(S, EC, FTD, FD);
+
+ return MatchesFriend(S, EC, ND);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ TypeSourceInfo *TSI) {
+ QualType TypeAsWritten = TSI->getType();
+ if (!TypeAsWritten->isDependentType())
+ return MatchesFriend(S, EC, S.Context.getCanonicalType(TypeAsWritten));
+
+ AccessResult OnFailure = EC.isDependent() ? AR_dependent : AR_inaccessible;
+ const auto *DNT = TypeAsWritten->getAs<DependentNameType>();
+ if (!DNT)
+ return OnFailure;
+
+ NestedNameSpecifier NNS = DNT->getQualifier();
+ if (!NNS)
+ return OnFailure;
+
+ const auto *T = NNS.getAsType();
+ if (!T)
+ return OnFailure;
+
+ const auto *TST =
+ S.Context.getCanonicalType(T)->getAsNonAliasTemplateSpecializationType();
+ if (!TST)
+ return OnFailure;
+
+ auto *CTD =
+ dyn_cast<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl());
+ if (!CTD)
+ return OnFailure;
+
+ TemplateParameterList *TPL = FTD->getFriendTypeTemplateParameterList(0);
+ if (!TPL)
+ return OnFailure;
+
+ for (CXXRecordDecl *RD : EC.Records) {
+ if (RD->getDeclName() != DNT->getIdentifier())
+ continue;
+
+ const auto *CTSD =
+ dyn_cast<ClassTemplateSpecializationDecl>(RD->getDeclContext());
+ if (!CTSD)
+ continue;
+
+ if (CTSD->getSpecializedTemplate()->getCanonicalDecl() !=
+ CTD->getCanonicalDecl())
+ continue;
+
+ if (CanDeduceTemplateArguments(S, TPL, TST->template_arguments(),
+ CTSD->getTemplateArgs().asArray(),
+ FTD->getLocation()))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
/// Determines whether the given friend declaration matches anything
/// in the effective context.
static AccessResult MatchesFriend(Sema &S,
@@ -561,25 +832,27 @@ static AccessResult MatchesFriend(Sema &S,
if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
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.
+ return AR_inaccessible;
+}
- if (isa<ClassTemplateDecl>(Friend))
- return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD) {
+ if (FTD->isInvalidDecl() || FTD->isUnsupportedFriend())
+ return AR_accessible;
- if (isa<FunctionTemplateDecl>(Friend))
- return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
+ if (NamedDecl *ND = FTD->getFriendDecl())
+ return MatchesFriend(S, EC, FTD, ND);
- if (isa<CXXRecordDecl>(Friend))
- return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+ if (TypeSourceInfo *TSI = FTD->getFriendType())
+ return MatchesFriend(S, EC, FTD, TSI);
- assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
- return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
+ return AR_inaccessible;
}
static AccessResult GetFriendKind(Sema &S,
@@ -588,8 +861,14 @@ static AccessResult GetFriendKind(Sema &S,
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);
+ else
+ AR = MatchesFriend(S, EC, cast<FriendDecl>(Friend));
+
+ switch (AR) {
case AR_accessible:
return AR_accessible;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c1d3960e65ef6..6435dc250470d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -58,6 +58,7 @@
#include <set>
using namespace clang;
+using namespace sema;
//===----------------------------------------------------------------------===//
// CheckDefaultArgumentVisitor
@@ -6211,8 +6212,8 @@ static void CheckAbstractClassUsage(AbstractUsageInfo &Info,
if (D->isImplicit()) continue;
// Step through friends to the befriended declaration.
- if (auto *FD = dyn_cast<FriendDecl>(D)) {
- D = FD->getFriendDecl();
+ if (D->getKind() == Decl::Friend) {
+ D = cast<FriendDecl>(D)->getFriendDecl();
if (!D) continue;
}
@@ -7330,9 +7331,9 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
if (!isa<CXXDestructorDecl>(M))
CompleteMemberFunction(M);
- } else if (auto *F = dyn_cast<FriendDecl>(D)) {
+ } else if (D->getKind() == Decl::Friend) {
CheckForDefaultedFunction(
- dyn_cast_or_null<FunctionDecl>(F->getFriendDecl()));
+ dyn_cast_or_null<FunctionDecl>(cast<FriendDecl>(D)->getFriendDecl()));
}
}
@@ -18029,6 +18030,33 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
return Decl;
}
+bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+ TemplateParameterList *FPL) {
+ if (!NNS || !FPL || FPL->size() == 0)
+ return false;
+
+ if (NNS.isDependent()) {
+ if (NNS.getKind() == NestedNameSpecifier::Kind::Type) {
+ QualType T(NNS.getCanonical().getAsType(), 0);
+ if (isa<PackIndexingType>(T))
+ return false;
+
+ if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+ if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()))
+ return false;
+ }
+
+ if (isa<InjectedClassNameType>(T))
+ return false;
+ }
+
+ Diag(Loc, diag::err_dependent_friend_not_member);
+ return true;
+ }
+
+ return false;
+}
+
DeclResult Sema::ActOnTemplatedFriendTag(
Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
@@ -18099,9 +18127,8 @@ DeclResult Sema::ActOnTemplatedFriendTag(
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;
@@ -18127,25 +18154,41 @@ DeclResult Sema::ActOnTemplatedFriendTag(
}
}
- // 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);
+ NestedNameSpecifier NNS = SS.getScopeRep();
+ if (EllipsisLoc.isInvalid() &&
+ CheckDependentFriend(TagLoc, NNS, TempParamLists.front()))
+ return true;
+
ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind);
- QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name);
+ QualType T = Context.getDependentNameType(ETK, NNS, 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 =
- FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
- EllipsisLoc, TempParamLists);
+ Decl *Friend;
+ if (TempParamLists.empty())
+ Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
+ EllipsisLoc);
+ else {
+ if (CheckTemplateDeclScope(S, TempParamLists.back()))
+ return true;
+
+ Friend = FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI,
+ FriendLoc, TempParamLists, EllipsisLoc);
+ }
+
+ if (EllipsisLoc.isValid() && NNS.isDependent()) {
+ Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
+ << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext);
+ cast<FriendDecl>(Friend)->setUnsupportedFriend(true);
+ }
+
Friend->setAccess(AS_public);
- Friend->setUnsupportedFriend(true);
CurContext->addDecl(Friend);
+
return Friend;
}
@@ -18246,11 +18289,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);
@@ -18437,6 +18483,11 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?");
}
+ if (TemplateParams.size() && SS.isValid() &&
+ CheckDependentFriend(NameInfo.getLoc(), SS.getScopeRep(),
+ TemplateParams.front()))
+ return nullptr;
+
if (!DC->isRecord()) {
int DiagArg = -1;
switch (D.getName().getKind()) {
@@ -18500,54 +18551,51 @@ 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);
+ if (ND->isInvalidDecl())
+ 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);
- }
+ if (DC->isRecord())
+ CheckFriendAccess(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);
}
+ }
// C++11 [dcl.fct.default]p4: If a friend declaration specifies a
// default argument expression, that declaration shall be a definition
@@ -18565,18 +18613,27 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
}
- // Mark templated-scope function declarations as unsupported.
- if (FD->getNumTemplateParameterLists() && SS.isValid()) {
- Diag(FD->getLocation(), diag::warn_template_qualified_friend_unsupported)
- << SS.getScopeRep() << SS.getRange()
- << cast<CXXRecordDecl>(CurContext);
- FrD->setUnsupportedFriend(true);
- }
- }
+ unsigned NumTPLists = FD->getNumTemplateParameterLists();
+ Decl *Friend;
+ if (NumTPLists && SS.isValid()) {
+ SmallVector<TemplateParameterList *, 1> TPL(NumTPLists);
+ for (unsigned I = 0, N = NumTPLists; I != N; ++I)
+ TPL[I] = FD->getTemplateParameterList(I);
- warnOnReservedIdentifier(ND);
+ if (CheckTemplateDeclScope(S, TPL.back()))
+ return nullptr;
- return ND;
+ Friend =
+ FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
+ ND, DS.getFriendSpecLoc(), TPL);
+ } else {
+ Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
+ DS.getFriendSpecLoc());
+ }
+ Friend->setAccess(AS_public);
+ CurContext->addDecl(Friend);
+
+ return ND;
}
void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..afd087afa048d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -11266,14 +11266,11 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
return T;
}
-/// Build the type that describes a C++ typename specifier,
-/// e.g., "typename T::type".
-QualType
-Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
- SourceLocation KeywordLoc,
- NestedNameSpecifierLoc QualifierLoc,
- const IdentifierInfo &II,
- SourceLocation IILoc, bool DeducedTSTContext) {
+QualType Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
+ SourceLocation KeywordLoc,
+ NestedNameSpecifierLoc QualifierLoc,
+ const IdentifierInfo &II, SourceLocation IILoc,
+ bool DeducedTSTContext) {
assert((Keyword != ElaboratedTypeKeyword::None) == KeywordLoc.isValid());
CXXScopeSpec SS;
@@ -11307,6 +11304,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
LookupQualifiedName(Result, Ctx, SS);
else
LookupName(Result, CurScope);
+
unsigned DiagID = 0;
Decl *Referenced = nullptr;
switch (Result.getResultKind()) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 5381a5a6f110d..3d9e9d1748c84 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1321,6 +1321,7 @@ namespace {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
DeclarationName Entity;
+
// Whether to evaluate the C++20 constraints or simply substitute into them.
bool EvaluateConstraints = true;
// Whether Substitution was Incomplete, that is, we tried to substitute in
@@ -2831,8 +2832,7 @@ TemplateInstantiator::TransformNestedRequirement(
TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
const MultiLevelTemplateArgumentList &Args,
- SourceLocation Loc,
- DeclarationName Entity,
+ SourceLocation Loc, DeclarationName Entity,
bool AllowDeducedTST) {
assert(!CodeSynthesisContexts.empty() &&
"Cannot perform an instantiation without some context on the "
@@ -2849,8 +2849,7 @@ TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
TypeSourceInfo *Sema::SubstType(TypeLoc TL,
const MultiLevelTemplateArgumentList &Args,
- SourceLocation Loc,
- DeclarationName Entity) {
+ SourceLocation Loc, DeclarationName Entity) {
assert(!CodeSynthesisContexts.empty() &&
"Cannot perform an instantiation without some context on the "
"instantiation stack");
@@ -2922,13 +2921,10 @@ static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) {
return false;
}
-TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
- const MultiLevelTemplateArgumentList &Args,
- SourceLocation Loc,
- DeclarationName Entity,
- CXXRecordDecl *ThisContext,
- Qualifiers ThisTypeQuals,
- bool EvaluateConstraints) {
+TypeSourceInfo *Sema::SubstFunctionDeclType(
+ TypeSourceInfo *T, const MultiLevelTemplateArgumentList &Args,
+ SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
+ Qualifiers ThisTypeQuals, bool EvaluateConstraints) {
assert(!CodeSynthesisContexts.empty() &&
"Cannot perform an instantiation without some context on the "
"instantiation stack");
@@ -3147,7 +3143,7 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
}
} else {
NewTSI = SubstType(OldTSI, TemplateArgs, OldParm->getLocation(),
- OldParm->getDeclName());
+ OldParm->getDeclName(), /*AllowDeducedTST=*/false);
}
if (!NewTSI)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 09c2482168ab7..0a33307ae535e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3180,29 +3180,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
D->setTypeSourceInfo(TSI);
}
- SmallVector<ParmVarDecl *, 4> Params;
- TypeSourceInfo *TInfo = SubstFunctionType(D, Params);
- if (!TInfo)
- return nullptr;
- QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
-
- if (TemplateParams && TemplateParams->size()) {
- auto *LastParam =
- dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
- if (LastParam && LastParam->isImplicit() &&
- LastParam->hasTypeConstraint()) {
- // In abbreviated templates, the type-constraints of invented template
- // type parameters are instantiated with the function type, invalidating
- // the TemplateParameterList which relied on the template type parameter
- // not having a type constraint. Recreate the TemplateParameterList with
- // the updated parameter list.
- TemplateParams = TemplateParameterList::Create(
- SemaRef.Context, TemplateParams->getTemplateLoc(),
- TemplateParams->getLAngleLoc(), TemplateParams->asArray(),
- TemplateParams->getRAngleLoc(), TemplateParams->getRequiresClause());
- }
- }
-
NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc();
if (QualifierLoc) {
QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc,
@@ -3233,14 +3210,39 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
+ if (!NameInfo.getName())
+ return nullptr;
+
+ CXXMethodDecl *Method = nullptr;
+ SourceLocation StartLoc = D->getInnerLocStart();
+
+ SmallVector<ParmVarDecl *, 4> Params;
+ TypeSourceInfo *TInfo = SubstFunctionType(D, Params);
+ if (!TInfo)
+ return nullptr;
+
+ QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
+
+ if (TemplateParams && TemplateParams->size()) {
+ auto *LastParam =
+ dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
+ if (LastParam && LastParam->isImplicit() &&
+ LastParam->hasTypeConstraint()) {
+ // In abbreviated templates, the type-constraints of invented template
+ // type parameters are instantiated with the function type, invalidating
+ // the TemplateParameterList which relied on the template type parameter
+ // not having a type constraint. Recreate the TemplateParameterList with
+ // the updated parameter list.
+ TemplateParams = TemplateParameterList::Create(
+ SemaRef.Context, TemplateParams->getTemplateLoc(),
+ TemplateParams->getLAngleLoc(), TemplateParams->asArray(),
+ TemplateParams->getRAngleLoc(), TemplateParams->getRequiresClause());
+ }
+ }
if (FunctionRewriteKind != RewriteKind::None)
adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
- // Build the instantiated method declaration.
- CXXMethodDecl *Method = nullptr;
-
- SourceLocation StartLoc = D->getInnerLocStart();
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
Method = CXXConstructorDecl::Create(
SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
@@ -4712,12 +4714,36 @@ 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();
+ unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
+ SmallVector<TemplateParameterList *, 1> TPL(NumTPLists);
+ for (unsigned I = 0, N = NumTPLists; I != N; ++I) {
+ TemplateParameterList *InstParams =
+ SubstTemplateParams(D->getFriendTypeTemplateParameterList(I));
+ if (!InstParams)
+ return nullptr;
+
+ TPL[I] = InstParams;
+ }
+
+ Decl *FTD = nullptr;
+ if (TypeSourceInfo *FT = D->getFriendType()) {
+ TypeSourceInfo *TSI = SemaRef.SubstType(FT, TemplateArgs, D->getLocation(),
+ DeclarationName());
+ if (TSI)
+ FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ TSI, D->getFriendLoc(), TPL);
+ } else {
+ if (cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
+ D->getLocation(), D->getFriendDecl(), TemplateArgs)))
+ FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ D->getFriendDecl(), D->getFriendLoc(),
+ TPL);
+ }
+
+ if (FTD) {
+ FTD->setAccess(AS_public);
+ Owner->addDecl(FTD);
+ }
return nullptr;
}
@@ -5099,9 +5125,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");
@@ -5170,8 +5195,9 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
continue;
}
- ParmVarDecl *Parm =
- cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam));
+ ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
+ OldParam, TemplateArgs, /*indexAdjustment=*/0, 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 9033ea55bc5e2..320782f9733af 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2397,8 +2397,6 @@ 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();
@@ -2407,16 +2405,16 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
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();
+ for (unsigned i = 0; i != D->NumTPLists; ++i)
+ D->getTrailingObjects()[i] = Record.readTemplateParameterList();
if (Record.readInt()) // HasFriendDecl
D->Friend = readDeclAs<NamedDecl>();
else
D->Friend = readTypeSourceInfo();
+ D->NextFriend = readDeclID().getRawValue();
+ D->UnsupportedFriend = (Record.readInt() != 0);
D->FriendLoc = readSourceLocation();
+ D->EllipsisLoc = readSourceLocation();
}
void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4044,10 +4042,10 @@ 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, 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 e415ac1e47862..287b763105b4c 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1819,7 +1819,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);
@@ -1827,8 +1826,6 @@ 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);
@@ -1838,15 +1835,18 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
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->NumTPLists);
+ for (unsigned i = 0, e = D->NumTPLists; i != e; ++i)
+ Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
Record.push_back(D->getFriendDecl() != nullptr);
if (D->getFriendDecl())
Record.AddDeclRef(D->getFriendDecl());
else
Record.AddTypeSourceInfo(D->getFriendType());
- Record.AddSourceLocation(D->getFriendLoc());
+ Record.AddDeclRef(D->getNextFriend());
+ Record.push_back(D->UnsupportedFriend);
+ Record.AddSourceLocation(D->FriendLoc);
+ Record.AddSourceLocation(D->EllipsisLoc);
Code = serialization::DECL_FRIEND_TEMPLATE;
}
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 ebee9bc4c3e16..236423067dcb5 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -420,25 +420,20 @@ class C {
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 {{friend declaration does not name a member of a class template specialization}}
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,15 @@ 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 at -30 {{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; }
void A<int>::D::g() { (void)c.private_int; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note at -37 {{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..4f7031ddec602 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -102,18 +102,18 @@ 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 {{friend declaration does not name a member of a class template specialization}}
};
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 at -12 {{implicitly declared private here}}
};
};
} // namespace cwg1918
@@ -170,7 +170,7 @@ class 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 {{friend declaration does not name a member of a class template specialization}}
};
} // namespace cwg1945
diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index b7b2ebf700375..1fca7390c371d 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -409,24 +409,27 @@ namespace cwg638 { // cwg638: no
class X {
typedef int 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 {{friend declaration does not name a member of a class template specialization}}
};
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 at -11 {{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 at -17 {{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 at -22 {{implicitly declared private here}}
};
};
} // namespace cwg638
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..5b03aba342f8b 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -6,7 +6,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 +19,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 +35,24 @@ 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(); // expected-note 3 {{implicitly declared private here}}
+ template <class T> friend void A<T>::g();
};
template <class T> struct A {
- void f() { C::foo(); }
+ void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}}
};
template <class T> struct A<T*> {
- void f() { C::foo(); }
+ void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}}
};
template <> struct A<char> {
- void f() { C::foo(); }
+ void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}}
};
}
@@ -66,7 +65,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() {
@@ -81,22 +80,148 @@ namespace test3 {
namespace test4 {
template <class T> struct X {
template <class U> void operator+=(U);
-
+
template <class V>
template <class U>
- friend void X<V>::operator+=(U); // expected-warning {{not supported}}
+ friend void X<V>::operator+=(U);
};
- void test() {
+ void test() {
X<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 {{friend declaration does not name a member of a class template specialization}}
+ };
+}
+
+namespace test8 {
+ template <class T> struct A {
+ T h();
+ };
+
+ template <> struct A<int> {
+ int h();
+ };
+
+ template <> struct A<float *> {
+ int *h();
+ };
+
+ class C {
+ int n; // expected-note {{implicitly declared private here}}
+ 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 {{'n' is a private member of 'test8::C'}}
+ 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(); // expected-note {{implicitly declared private here}}
+ 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 {{'foo' is a private member of 'test10::C'}}
+ return 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..67bb7e19b4aaf
--- /dev/null
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
@@ -0,0 +1,24 @@
+// 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 {{templates can only be declared in namespace or class scope}}
+ };
+}
+
+void t2() {
+ struct S {
+ template <class T> friend struct A; // expected-error {{templates cannot be declared inside of a local class}}
+ };
+}
+
+void t3() {
+ struct S {
+ template <class T> friend void B<T>::f(); // expected-error {{templates cannot be declared inside of a local class}}
+ };
+}
diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index f98005c7e6fb5..cbd04db3301d6 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>
@@ -28,7 +28,9 @@ struct X {
template <class 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 {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}}
+ // expected-note at -2 {{not-yet-instantiated member is declared here}}
};
void test() {
diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
index daec9410e547a..637f00e886ef7 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -20,15 +20,15 @@ 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}}
- temp();
+ temp(); // expected-note {{implicitly declared private here}}
};
template<C<temp<int>> T>
void g() {
- auto v = temp<T>();
+ auto v = temp<T>(); // expected-error {{calling a private constructor of class 'temp<int>'}}
}
void h() {
f<int>();
- g<int>();
+ g<int>(); // expected-note {{in instantiation of function template specialization 'g<int>' requested here}}
}
diff --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp
index 52ffef980fcdf..8822931f3a8dc 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -23,7 +23,8 @@ namespace Access {
};
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}} \
+ D(T, typename T::type); // expected-error {{'type' is a private member of 'Access::Y'}} \
+ // expected-note {{private member}} \
// expected-note {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
};
D b = {B(), {}};
@@ -36,10 +37,10 @@ namespace Access {
// 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 {}; // expected-note {{implicitly declared private here}}
};
- D y = {Y(), {}};
+ D y = {Y(), {}}; // expected-note {{in instantiation of template class 'Access::D<Access::Y>' requested here}}
class Z {
template <typename T> friend class D;
>From 0f6736d979a58b2b6e36c76e111480f5fa602fc9 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 15 Apr 2026 10:11:14 +0300
Subject: [PATCH 2/3] remove unused comment
---
clang/include/clang/AST/DeclTemplate.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 1653c26910b8b..ac77076175d91 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -2471,9 +2471,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
/// 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 final
: public FriendDecl,
private llvm::TrailingObjects<FriendTemplateDecl,
>From 06e0ff6953fa9d3186744b45dc80f21f29dfda76 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 15 Apr 2026 17:59:27 +0300
Subject: [PATCH 3/3] update tests
---
clang/test/CXX/drs/cwg18xx.cpp | 15 ++++++--------
clang/test/CXX/drs/cwg19xx.cpp | 13 +++++-------
clang/test/CXX/drs/cwg6xx.cpp | 20 ++++++++-----------
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 11 ++++++----
.../SemaCXX/many-template-parameter-lists.cpp | 5 ++---
5 files changed, 28 insertions(+), 36 deletions(-)
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index 236423067dcb5..d46bdffa26384 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -416,7 +416,7 @@ struct A<float*> {
};
class C {
- int private_int;
+ int private_int; // #C_private_int
template<class T>
friend struct A<T>::B;
@@ -425,8 +425,7 @@ class C {
friend void A<T>::f();
template<class T>
- friend void A<T>::D::g();
- // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
+ friend void A<T>::D::g(); // expected-error {{friend declaration does not name a member of a class template specialization}}
template<class T>
friend int *A<T*>::h();
@@ -444,16 +443,14 @@ 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 at -30 {{implicitly declared private here}}
+int A<int>::f() { (void)c.private_int; return 0; } // expected-error {{'private_int' is a private member of 'cwg1862::C'}} \
+ // expected-note@#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; }
-void A<int>::D::g() { (void)c.private_int; }
-// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
-// expected-note at -37 {{implicitly declared private here}}
+void A<int>::D::g() { (void)c.private_int; } // expected-error {{'private_int' is a private member of 'cwg1862::C'}} \
+ // expected-note@#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 4f7031ddec602..4f20412d2ab39 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -101,19 +101,17 @@ template<typename T> struct A {
};
};
class X {
- static int x;
+ static int x; // #X_c
template <typename T>
- friend class A<T>::B::C;
- // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
+ friend class A<T>::B::C; // expected-error {{friend declaration does not name a member of a class template specialization}}
};
template<> struct A<int> {
typedef struct Q B;
};
struct Q {
class C {
- int f() { return X::x; }
- // expected-error at -1 {{'x' is a private member of 'cwg1918::X'}}
- // expected-note at -12 {{implicitly declared private here}}
+ int f() { return X::x; } // expected-error {{'x' is a private member of 'cwg1918::X'}} \
+ // expected-note@#X_c {{implicitly declared private here}}
};
};
} // namespace cwg1918
@@ -169,8 +167,7 @@ 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-error at -1 {{friend declaration does not name a member of a class template specialization}}
+ friend class A<T>::B::C; // expected-error {{friend declaration does not name a member of a class template specialization}}
};
} // namespace cwg1945
diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index 1fca7390c371d..32f9c7314c59a 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -407,29 +407,25 @@ namespace cwg638 { // cwg638: no
};
class X {
- typedef int type;
+ typedef int type; // #X_type
template<class T> friend struct A<T>::B;
template<class T> friend void A<T>::f();
template<class T> friend void A<T>::g();
- template<class T> friend void A<T>::C::h();
- // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
+ template<class T> friend void A<T>::C::h(); // expected-error {{friend declaration does not name a member of a class template specialization}}
};
template<> struct A<int> {
- X::type a;
- // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
- // expected-note at -11 {{implicitly declared private here}}
+ X::type a; // expected-error {{'type' is a private member of 'cwg638::X'}} \
+ // expected-note@#X_type {{implicitly declared private here}}
struct B {
X::type b; // ok
};
- int f() { X::type c; }
- // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
- // expected-note at -17 {{implicitly declared private here}}
+ int f() { X::type c; } // expected-error {{'type' is a private member of 'cwg638::X'}} \
+ // expected-note@#X_type {{implicitly declared private here}}
void g() { X::type d; } // ok
struct D {
- void h() { X::type e; }
- // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
- // expected-note at -22 {{implicitly declared private here}}
+ void h() { X::type e; } // expected-error {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#X_type {{implicitly declared private here}}
};
};
} // namespace cwg638
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 5b03aba342f8b..94a4c0ceea0a9 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -39,20 +39,23 @@ namespace test2 {
template <class T> struct A;
class C {
- static void foo(); // expected-note 3 {{implicitly declared private here}}
+ static void foo(); // #C_foo
template <class T> friend void A<T>::g();
};
template <class T> struct A {
- void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}}
+ void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} \
+ // expected-note@#C_foo {{implicitly declared private here}}
};
template <class T> struct A<T*> {
- void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}}
+ void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} \
+ // expected-note@#C_foo {{implicitly declared private here}}
};
template <> struct A<char> {
- void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}}
+ void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} \
+ // expected-note@#C_foo {{implicitly declared private here}}
};
}
diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index cbd04db3301d6..3f44a9f82e9d7 100644
--- a/clang/test/SemaCXX/many-template-parameter-lists.cpp
+++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp
@@ -28,9 +28,8 @@ struct X {
template <class 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-error at -1 {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}}
- // expected-note at -2 {{not-yet-instantiated member is declared here}}
+ friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z); // expected-error {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}} \
+ // expected-note {{not-yet-instantiated member is declared here}}
};
void test() {
More information about the cfe-commits
mailing list