[clang] [clang-tools-extra] [Clang] support friend declarations with a dependent nested-name-specifier (PR #191268)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 5 04:34:44 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 01/38] [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 02/38] 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 03/38] 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() {
>From 726716ea5430a622065f75640778456bb828f32c Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 17 Apr 2026 00:07:27 +0300
Subject: [PATCH 04/38] use ArrayRef for FriendTemplateDecl parameter lists.
finish deduction for friend access checks
---
clang/include/clang/AST/DeclTemplate.h | 8 +--
clang/include/clang/AST/RecursiveASTVisitor.h | 4 +-
clang/include/clang/Sema/Sema.h | 6 ++
clang/lib/AST/ASTImporter.cpp | 24 ++++----
clang/lib/AST/ASTStructuralEquivalence.cpp | 14 ++---
clang/lib/AST/DeclTemplate.cpp | 2 +-
clang/lib/Sema/SemaAccess.cpp | 56 +++++++++++++------
clang/lib/Sema/SemaTemplateDeduction.cpp | 10 ++++
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 ++--
clang/lib/Serialization/ASTWriterDecl.cpp | 4 +-
10 files changed, 87 insertions(+), 51 deletions(-)
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index ac77076175d91..24218cfb65da6 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -2523,13 +2523,11 @@ class FriendTemplateDecl final
return Friend.dyn_cast<NamedDecl*>();
}
- TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
- assert(N < NumTPLists);
- return getTrailingObjects()[N];
+ ArrayRef<TemplateParameterList *>
+ getFriendTypeTemplateParameterLists() const {
+ return ArrayRef(getTrailingObjects(), NumTPLists);
}
- unsigned getFriendTypeNumTemplateParameterLists() const { return NumTPLists; }
-
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Decl::FriendTemplate; }
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index cd4fd5b598d4a..af72a71db9ffb 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1727,9 +1727,7 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, {
TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
else
TRY_TO(TraverseDecl(D->getFriendDecl()));
- for (unsigned I = 0, E = D->getFriendTypeNumTemplateParameterLists(); I < E;
- ++I) {
- TemplateParameterList *TPL = D->getFriendTypeTemplateParameterList(I);
+ for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists()) {
for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
ITPL != ETPL; ++ITPL) {
TRY_TO(TraverseDecl(*ITPL));
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 544d892c1535c..0cd1b29289e3d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12750,6 +12750,12 @@ class Sema final : public SemaBase {
return false;
});
+ TemplateDeductionResult FinishTemplateArgumentDeduction(
+ TemplateDecl *TD, TemplateParameterList *TPL,
+ ArrayRef<TemplateArgument> PatternArgs, ArrayRef<TemplateArgument> Args,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ sema::TemplateDeductionInfo &Info, bool CopyDeducedArgs);
+
/// Perform template argument deduction from a function call
/// (C++ [temp.deduct.call]).
///
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 782e4d710599c..3667689d3d462 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4548,8 +4548,11 @@ static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
static bool IsEquivalentFriend(ASTImporter &Importer, FriendTemplateDecl *FTD1,
FriendTemplateDecl *FTD2) {
- if (FTD1->getFriendTypeNumTemplateParameterLists() !=
- FTD2->getFriendTypeNumTemplateParameterLists())
+ ArrayRef<TemplateParameterList *> TPL1 =
+ FTD1->getFriendTypeTemplateParameterLists();
+ ArrayRef<TemplateParameterList *> TPL2 =
+ FTD2->getFriendTypeTemplateParameterLists();
+ if (TPL1.size() != TPL2.size())
return false;
ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
@@ -4559,12 +4562,9 @@ static bool IsEquivalentFriend(ASTImporter &Importer, FriendTemplateDecl *FTD1,
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)))
+ for (unsigned I = 0, N = TPL1.size(); I != N; ++I)
+ if (!Ctx.IsEquivalent(TPL1[I], TPL2[I]))
return false;
- }
if ((!FTD1->getFriendType()) != (!FTD2->getFriendType()))
return false;
@@ -4700,11 +4700,11 @@ ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
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)))
+ ArrayRef<TemplateParameterList *> TPLs =
+ D->getFriendTypeTemplateParameterLists();
+ SmallVector<TemplateParameterList *, 1> ToParams(TPLs.size());
+ for (unsigned I = 0, N = TPLs.size(); I != N; ++I) {
+ if (auto ParamsOrErr = import(TPLs[I]))
ToParams[I] = *ParamsOrErr;
else
return ParamsOrErr.takeError();
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 8da7cca6665fb..e85caf1e766fc 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2433,16 +2433,16 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
FriendTemplateDecl *FTD1,
FriendTemplateDecl *FTD2) {
- if (FTD1->getFriendTypeNumTemplateParameterLists() !=
- FTD2->getFriendTypeNumTemplateParameterLists())
+ ArrayRef<TemplateParameterList *> TPL1 =
+ FTD1->getFriendTypeTemplateParameterLists();
+ ArrayRef<TemplateParameterList *> TPL2 =
+ FTD2->getFriendTypeTemplateParameterLists();
+ if (TPL1.size() != TPL2.size())
return false;
- for (unsigned I = 0, N = FTD1->getFriendTypeNumTemplateParameterLists();
- I != N; ++I) {
- if (!Context.IsEquivalent(FTD1->getFriendTypeTemplateParameterList(I),
- FTD2->getFriendTypeTemplateParameterList(I)))
+ for (unsigned I = 0, N = TPL1.size(); I != N; ++I)
+ if (!Context.IsEquivalent(TPL1[I], TPL2[I]))
return false;
- }
if ((FTD1->getFriendType() && FTD2->getFriendDecl()) ||
(FTD1->getFriendDecl() && FTD2->getFriendType()))
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 6c19c347d87fc..623b62fc219ac 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1256,7 +1256,7 @@ FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
SourceRange FriendTemplateDecl::getSourceRange() const {
SourceLocation Begin =
- getFriendTypeTemplateParameterList(0)->getTemplateLoc();
+ getFriendTypeTemplateParameterLists().front()->getTemplateLoc();
SourceLocation End = FriendDecl::getSourceRange().getEnd();
return SourceRange(Begin, End);
}
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index c516aee3e62d6..91e84d62f27c9 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -19,6 +19,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Sema/DelayedDiagnostic.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Template.h"
@@ -276,12 +277,10 @@ struct AccessTarget : public AccessedEntity {
}
static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+ TemplateDecl *TD,
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) {
@@ -290,16 +289,28 @@ static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
if (Equal)
return true;
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
TemplateDeductionInfo Info(Loc);
+ Sema::SFINAETrap Trap(S, Info);
+ LocalInstantiationScope InstantiationScope(S);
SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
- S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
- /*NumberOfArgumentsMustMatch=*/false);
+ if (S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
+ /*NumberOfArgumentsMustMatch=*/false) !=
+ TemplateDeductionResult::Success)
+ return false;
- for (const DeducedTemplateArgument &Arg : Deduced)
- if (Arg.isNull())
- return false;
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
+ Sema::InstantiatingTemplate Inst(S, Info.getLocation(), TD, DeducedArgs);
+ if (Inst.isInvalid())
+ return false;
- return true;
+ if (S.FinishTemplateArgumentDeduction(
+ TD, TPL, PatternArgs, Args, Deduced, Info,
+ /*CopyDeducedArgs=*/false) != TemplateDeductionResult::Success)
+ return false;
+
+ return !Trap.hasErrorOccurred();
}
static CanQual<FunctionProtoType>
@@ -365,7 +376,8 @@ static bool MatchesFriendContext(Sema &S, FunctionDecl *FD,
if (ContextCTD->getCanonicalDecl() != FriendCTD->getCanonicalDecl())
return false;
- return CanDeduceTemplateArguments(S, FriendTPL, FriendArgs, ContextArgs, Loc);
+ return CanDeduceTemplateArguments(S, FriendTPL, FriendCTD, FriendArgs,
+ ContextArgs, Loc);
}
/// Checks whether one class might instantiate to the other.
@@ -692,9 +704,12 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!FriendCTD)
return OnFailure;
- TemplateParameterList *FriendTPL =
- FriendTD->getFriendTypeTemplateParameterList(0);
+ ArrayRef<TemplateParameterList *> FriendTPLists =
+ FriendTD->getFriendTypeTemplateParameterLists();
+ if (FriendTPLists.empty())
+ return OnFailure;
+ TemplateParameterList *FriendTPL = FriendTPLists.front();
if (!FriendTPL)
return OnFailure;
@@ -728,8 +743,12 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!FriendCTD)
return OnFailure;
- TemplateParameterList *FriendTPL =
- FriendTD->getFriendTypeTemplateParameterList(0);
+ ArrayRef<TemplateParameterList *> FriendTPLists =
+ FriendTD->getFriendTypeTemplateParameterLists();
+ if (FriendTPLists.empty())
+ return OnFailure;
+
+ TemplateParameterList *FriendTPL = FriendTPLists.front();
if (!FriendTPL)
return OnFailure;
@@ -796,7 +815,12 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!CTD)
return OnFailure;
- TemplateParameterList *TPL = FTD->getFriendTypeTemplateParameterList(0);
+ ArrayRef<TemplateParameterList *> FriendTPLists =
+ FTD->getFriendTypeTemplateParameterLists();
+ if (FriendTPLists.empty())
+ return OnFailure;
+
+ TemplateParameterList *TPL = FriendTPLists.front();
if (!TPL)
return OnFailure;
@@ -813,7 +837,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
CTD->getCanonicalDecl())
continue;
- if (CanDeduceTemplateArguments(S, TPL, TST->template_arguments(),
+ if (CanDeduceTemplateArguments(S, TPL, CTD, TST->template_arguments(),
CTSD->getTemplateArgs().asArray(),
FTD->getLocation()))
return AR_accessible;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c71c40526ccdc..a8303d615b5e5 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4108,6 +4108,16 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
return TemplateDeductionResult::Success;
}
+TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
+ TemplateDecl *TD, TemplateParameterList *TPL,
+ ArrayRef<TemplateArgument> PatternArgs, ArrayRef<TemplateArgument> Args,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ sema::TemplateDeductionInfo &Info, bool CopyDeducedArgs) {
+ return ::FinishTemplateArgumentDeduction(
+ *this, TD, TPL, TD, /*PartialOrdering=*/false, PatternArgs, Args, Deduced,
+ Info, CopyDeducedArgs);
+}
+
/// Gets the type of a function for template-argument-deducton
/// purposes when it's considered as part of an overload set.
static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0a33307ae535e..5a8944d7902f3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4714,11 +4714,11 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
}
Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
- unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists();
- SmallVector<TemplateParameterList *, 1> TPL(NumTPLists);
- for (unsigned I = 0, N = NumTPLists; I != N; ++I) {
- TemplateParameterList *InstParams =
- SubstTemplateParams(D->getFriendTypeTemplateParameterList(I));
+ ArrayRef<TemplateParameterList *> TPLists =
+ D->getFriendTypeTemplateParameterLists();
+ SmallVector<TemplateParameterList *, 1> TPL(TPLists.size());
+ for (unsigned I = 0, N = TPLists.size(); I != N; ++I) {
+ TemplateParameterList *InstParams = SubstTemplateParams(TPLists[I]);
if (!InstParams)
return nullptr;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 287b763105b4c..33b8c5bb23e11 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1836,8 +1836,8 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
VisitDecl(D);
Record.push_back(D->NumTPLists);
- for (unsigned i = 0, e = D->NumTPLists; i != e; ++i)
- Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
+ for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+ Record.AddTemplateParameterList(TPL);
Record.push_back(D->getFriendDecl() != nullptr);
if (D->getFriendDecl())
Record.AddDeclRef(D->getFriendDecl());
>From ab8d8f596d3637c7525e0812212fc37a25e2233e Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 23 Apr 2026 10:26:06 +0300
Subject: [PATCH 05/38] fix tests formatting
---
clang/test/CXX/drs/cwg18xx.cpp | 15 +++++++++------
clang/test/CXX/drs/cwg19xx.cpp | 13 ++++++++-----
clang/test/CXX/drs/cwg6xx.cpp | 20 ++++++++++++--------
3 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index d46bdffa26384..d72e2089039ca 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; // #C_private_int
+ int private_int; // #cwg1862-C-private_int
template<class T>
friend struct A<T>::B;
@@ -425,7 +425,8 @@ class C {
friend void A<T>::f();
template<class T>
- friend void A<T>::D::g(); // expected-error {{friend declaration does not name a member of a class template specialization}}
+ friend void A<T>::D::g();
+ // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
template<class T>
friend int *A<T*>::h();
@@ -443,14 +444,16 @@ void A<int>::B::e() { (void)c.private_int; }
template<class T>
void A<T>::f() { (void)c.private_int; }
-int A<int>::f() { (void)c.private_int; return 0; } // expected-error {{'private_int' is a private member of 'cwg1862::C'}} \
- // expected-note@#C_private_int {{implicitly declared private here}}
+int A<int>::f() { (void)c.private_int; return 0; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
// FIXME: both definition of 'D::g' are not friends, so they don't have access to 'private_int'
template<class T>
void A<T>::D::g() { (void)c.private_int; }
-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}}
+void A<int>::D::g() { (void)c.private_int; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
template<class T>
T A<T>::h() { (void)c.private_int; }
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 4f20412d2ab39..7317c8beedaaa 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -101,17 +101,19 @@ template<typename T> struct A {
};
};
class X {
- static int x; // #X_c
+ static int x; // #cwg1918-X-x
template <typename T>
- friend class A<T>::B::C; // expected-error {{friend declaration does not name a member of a class template specialization}}
+ friend class A<T>::B::C;
+ // 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 {
- int f() { return X::x; } // expected-error {{'x' is a private member of 'cwg1918::X'}} \
- // expected-note@#X_c {{implicitly declared private here}}
+ int f() { return X::x; }
+ // expected-error at -1 {{'x' is a private member of 'cwg1918::X'}}
+ // expected-note@#cwg1918-X-x {{implicitly declared private here}}
};
};
} // namespace cwg1918
@@ -167,7 +169,8 @@ 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 {{friend declaration does not name a member of a class template specialization}}
+ friend class A<T>::B::C;
+ // 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 32f9c7314c59a..a61f161e5ed62 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -407,25 +407,29 @@ namespace cwg638 { // cwg638: no
};
class X {
- typedef int type; // #X_type
+ typedef int type; // #cwg638-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 {{friend declaration does not name a member of a class template specialization}}
+ 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<> struct A<int> {
- X::type a; // expected-error {{'type' is a private member of 'cwg638::X'}} \
- // expected-note@#X_type {{implicitly declared private here}}
+ X::type a;
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
struct B {
X::type b; // ok
};
- int f() { X::type c; } // expected-error {{'type' is a private member of 'cwg638::X'}} \
- // expected-note@#X_type {{implicitly declared private here}}
+ int f() { X::type c; }
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
void g() { X::type d; } // ok
struct D {
- void h() { X::type e; } // expected-error {{'type' is a private member of 'cwg638::X'}}
- // expected-note@#X_type {{implicitly declared private here}}
+ void h() { X::type e; }
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
};
};
} // namespace cwg638
>From 1ed9754477f44fcca9c34dcb85273988c2c5df19 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 23 Apr 2026 11:14:12 +0300
Subject: [PATCH 06/38] fix tests formatting
---
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 32 ++++++++++++-------
.../CXX/temp/temp.decls/temp.friend/p6.cpp | 9 ++++--
.../SemaCXX/many-template-parameter-lists.cpp | 8 +++--
clang/test/SemaTemplate/GH71595.cpp | 12 ++++---
clang/test/SemaTemplate/ctad.cpp | 23 +++++++------
5 files changed, 53 insertions(+), 31 deletions(-)
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 94a4c0ceea0a9..c3c8c3668d91e 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -39,23 +39,26 @@ namespace test2 {
template <class T> struct A;
class C {
- static void foo(); // #C_foo
+ static void foo(); // #test2-C-foo
template <class T> friend void A<T>::g();
};
template <class T> struct A {
- void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} \
- // expected-note@#C_foo {{implicitly declared private here}}
+ void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
template <class T> struct A<T*> {
- void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} \
- // expected-note@#C_foo {{implicitly declared private here}}
+ void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
template <> struct A<char> {
- void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} \
- // expected-note@#C_foo {{implicitly declared private here}}
+ void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
}
@@ -132,7 +135,8 @@ namespace test7 {
};
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}}
+ 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}}
};
}
@@ -150,7 +154,7 @@ namespace test8 {
};
class C {
- int n; // expected-note {{implicitly declared private here}}
+ int n; // #test8-C-n
template <class T> friend int *A<T *>::h();
};
@@ -160,7 +164,9 @@ namespace test8 {
int A<int>::h() {
C c;
- c.n = 0; // expected-error {{'n' is a private member of 'test8::C'}}
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test8::C'}}
+ // expected-note@#test8-C-n {{implicitly declared private here}}
return 0;
}
@@ -213,7 +219,7 @@ namespace test9 {
namespace test10 {
template <class T> struct A;
class C {
- static void foo(); // expected-note {{implicitly declared private here}}
+ static void foo(); // #test10-C-foo
template <class T> friend void A<T>::f();
};
@@ -223,7 +229,9 @@ namespace test10 {
template <> struct A<int> {
int f() {
- C::foo(); // expected-error {{'foo' is a private member of 'test10::C'}}
+ C::foo();
+ // expected-error at -1 {{'foo' is a private member of 'test10::C'}}
+ // expected-note@#test10-C-foo {{implicitly declared private here}}
return 0;
}
};
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
index 67bb7e19b4aaf..f60f3dd42445c 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
@@ -7,18 +7,21 @@ template <class T> struct B {
void t1() {
struct S {
- template <class T> friend void f(); // expected-error {{templates can only be declared in namespace or class scope}}
+ template <class T> friend void f();
+ // expected-error at -1 {{templates can only be declared in namespace or class scope}}
};
}
void t2() {
struct S {
- template <class T> friend struct A; // expected-error {{templates cannot be declared inside of a local class}}
+ template <class T> friend struct A;
+ // expected-error at -1 {{templates cannot be declared inside of a local class}}
};
}
void t3() {
struct S {
- template <class T> friend void B<T>::f(); // expected-error {{templates cannot be declared inside of a local class}}
+ template <class T> friend void B<T>::f();
+ // expected-error at -1 {{templates cannot be declared inside of a local class}}
};
}
diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index 3f44a9f82e9d7..ad6de9ee5b25e 100644
--- a/clang/test/SemaCXX/many-template-parameter-lists.cpp
+++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp
@@ -28,10 +28,12 @@ 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 {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}} \
- // expected-note {{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); // #X-friend-operator-plus-eq
+ // expected-error at -1 {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}}
+ // expected-note@#X-friend-operator-plus-eq {{not-yet-instantiated member is declared here}}
};
void test() {
- X<int>::A<int>::B<int>::C<int>::D<int>::E<int>() += 1.0; // expected-note {{in instantiation of template class 'X<int>' requested here}}
+ X<int>::A<int>::B<int>::C<int>::D<int>::E<int>() += 1.0;
+ // expected-note at -1 {{in instantiation of template class 'X<int>' requested here}}
}
diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
index 637f00e886ef7..c5c5d13036233 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -18,17 +18,21 @@ void f() {
template<class A>
class temp {
template<C<temp> T>
- friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
+ friend void g();
+ // expected-error at -1 {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
- temp(); // expected-note {{implicitly declared private here}}
+ temp(); // #temp-ctor
};
template<C<temp<int>> T>
void g() {
- auto v = temp<T>(); // expected-error {{calling a private constructor of class 'temp<int>'}}
+ auto v = temp<T>();
+ // expected-error at -1 {{calling a private constructor of class 'temp<int>'}}
+ // expected-note@#temp-ctor {{implicitly declared private here}}
}
void h() {
f<int>();
- g<int>(); // expected-note {{in instantiation of function template specialization 'g<int>' requested here}}
+ g<int>();
+ // expected-note at -1 {{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 8822931f3a8dc..0f713faf0d8dc 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -19,28 +19,33 @@ namespace pr41427 {
namespace Access {
struct B {
protected:
- struct type {};
+ struct type {}; // #Access-B-type
};
- template<typename T> struct D : B { // expected-note {{not viable}} \
- expected-note {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
- D(T, typename T::type); // expected-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>'}}
+ template<typename T> struct D : B { // #Access-D
+ D(T, typename T::type); // #Access-D-ctor
+ // expected-error at -1 {{'type' is a private member of 'Access::Y'}}
+ // expected-note@#Access-Y-type {{implicitly declared private here}}
+ // expected-note@#Access-D-ctor {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
};
D b = {B(), {}};
class X {
using type = int;
};
- D x = {X(), {}}; // expected-error {{no viable constructor or deduction guide}}
+ D x = {X(), {}};
+ // expected-error at -1 {{no viable constructor or deduction guide}}
+ // expected-note@#Access-D {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
+ // expected-note@#Access-D {{candidate function template not viable: requires 1 argument, but 2 were provided}}
+ // expected-note@#Access-D-ctor {{candidate template ignored: substitution failure [with T = X]: 'type' is a private member of 'Access::X'}}
// Once we implement proper support for dependent nested name specifiers in
// friends, this should still work.
class Y {
template <typename T> friend D<T>::D(T, typename T::type);
- struct type {}; // expected-note {{implicitly declared private here}}
+ struct type {}; // #Access-Y-type
};
- D y = {Y(), {}}; // expected-note {{in instantiation of template class 'Access::D<Access::Y>' requested here}}
+ D y = {Y(), {}};
+ // expected-note at -1 {{in instantiation of template class 'Access::D<Access::Y>' requested here}}
class Z {
template <typename T> friend class D;
>From a47fba8549ab7559fb51b6d89e126d8ee32dbece Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 23 Apr 2026 18:15:43 +0300
Subject: [PATCH 07/38] update release notes
---
clang/docs/ReleaseNotes.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8d60450f54669..79f65b9ce92a9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -131,6 +131,7 @@ C++ Language Changes
--------------------
- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)
+- Clang now supports friend declarations with a dependent nested name specifier. (#GH104057)
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
>From afe8dc24a413f7b6a0983fa35d0c244f8ef597bb Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 15:48:35 +0300
Subject: [PATCH 08/38] fix formatting
---
clang/lib/AST/DeclPrinter.cpp | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index b84745382a976..d9f4ebbe9bb63 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -890,15 +890,19 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
if (TypeSourceInfo *TSI = D->getFriendType()) {
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);
}
>From ad032bc8e618dc9be9fd65b61479fb3b3c57b0da Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 15:49:20 +0300
Subject: [PATCH 09/38] combine conditions
---
clang/lib/AST/ASTStructuralEquivalence.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index e85caf1e766fc..4b2663420b80a 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2803,10 +2803,7 @@ bool StructuralEquivalenceContext::IsEquivalent(TemplateParameterList *TPL1,
if (TPL1 == TPL2)
return true;
- if (!TPL1 || !TPL2)
- return false;
-
- if (TPL1->size() != TPL2->size())
+ if (!TPL1 || !TPL2 || TPL1->size() != TPL2->size())
return false;
for (unsigned I = 0, N = TPL1->size(); I != N; ++I) {
>From 291ccff3cad08bcce72a1aad463eb8548bd821aa Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 15:51:02 +0300
Subject: [PATCH 10/38] remove redundant parameter list loop
---
clang/lib/Sema/SemaDeclCXX.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 31ae89aad0851..5a25e0a112922 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -58,7 +58,6 @@
#include <set>
using namespace clang;
-using namespace sema;
//===----------------------------------------------------------------------===//
// CheckDefaultArgumentVisitor
@@ -18660,13 +18659,9 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
}
- unsigned NumTPLists = FD->getNumTemplateParameterLists();
+ ArrayRef<TemplateParameterList *> TPL = FD->getTemplateParameterLists();
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);
-
+ if (TPL.size() > 0 && SS.isValid()) {
if (CheckTemplateDeclScope(S, TPL.back()))
return nullptr;
@@ -18677,6 +18672,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
DS.getFriendSpecLoc());
}
+
Friend->setAccess(AS_public);
CurContext->addDecl(Friend);
>From 66c0c4851e84fe841b4898e2f80065fa5adcb8d4 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 15:56:22 +0300
Subject: [PATCH 11/38] fix formatting
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 096b0d562469d..59a3b01e963ca 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1321,7 +1321,6 @@ 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
@@ -2832,7 +2831,8 @@ 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,7 +2849,8 @@ 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");
@@ -2921,10 +2922,13 @@ 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");
@@ -3149,7 +3153,7 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
}
} else {
NewTSI = SubstType(OldTSI, TemplateArgs, OldParm->getLocation(),
- OldParm->getDeclName(), /*AllowDeducedTST=*/false);
+ OldParm->getDeclName());
}
if (!NewTSI)
>From 38bd8183bc83edec8708c7f266a0100adf2676de Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 16:01:43 +0300
Subject: [PATCH 12/38] add overload comment
---
clang/include/clang/Sema/Sema.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 506419d85ee72..c045c93fd93ba 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12759,6 +12759,9 @@ class Sema final : public SemaBase {
return false;
});
+ /// Finish template argument deduction for a template declaration, checking
+ /// the deduced template arguments for completeness and forming the deduced
+ /// template argument list.
TemplateDeductionResult FinishTemplateArgumentDeduction(
TemplateDecl *TD, TemplateParameterList *TPL,
ArrayRef<TemplateArgument> PatternArgs, ArrayRef<TemplateArgument> Args,
>From dc55c13d9ef0f60193413e7193b6c5f498614d42 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 16:06:07 +0300
Subject: [PATCH 13/38] handle pack-indexed qualifiers in dependent friend
declarations
---
clang/lib/Sema/SemaDeclCXX.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5a25e0a112922..751a81a65b1a1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18068,8 +18068,9 @@ bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
if (NNS.isDependent()) {
if (NNS.getKind() == NestedNameSpecifier::Kind::Type) {
QualType T(NNS.getCanonical().getAsType(), 0);
- if (isa<PackIndexingType>(T))
- return false;
+
+ if (const auto *PIT = dyn_cast<PackIndexingType>(T))
+ T = PIT->getPattern();
if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()))
>From 6234a6fec5c4f2eeec817ef3d5598a627df1250a Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 16:59:48 +0300
Subject: [PATCH 14/38] revert reorder changes
---
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 61 +++++++++----------
clang/test/CXX/drs/cwg28xx.cpp | 1 +
2 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0c672fa8e168d..ed3ed8e437327 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3193,6 +3193,29 @@ 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,
@@ -3223,39 +3246,14 @@ 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,
@@ -5136,8 +5134,9 @@ 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");
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 02c8f30249683..6876cfa12ff51 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -183,6 +183,7 @@ struct A {
friend void Ts...[0]::f();
template<typename U>
friend void Ts...[0]::g();
+ // since-cxx26-error at -1 {{friend declaration does not name a member of a class template specialization}}
friend struct Ts...[0]::B;
// FIXME: The index of the pack-index-specifier is printed as a memory address in the diagnostic.
>From 4a1cb61071f833c9278776b129c97b06d73d5f4b Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 17:19:33 +0300
Subject: [PATCH 15/38] add additional tests
---
clang/test/SemaTemplate/concepts-friends.cpp | 32 ++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
index 11287aa773b1b..0dcfdd097e2d4 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,3 +566,35 @@ struct Test {
};
}
+
+namespace DependentFriends {
+template <class T> concept X = requires { typename T::type; }; // #DependentFriends_X
+
+struct A {
+ using type = int;
+};
+struct B {};
+
+template <class T> struct C {
+ static void f()
+ requires X<T>; // #DependentFriends_C_f
+};
+
+class D {
+ static int n;
+ template <X T> friend void C<T>::f();
+};
+
+template <class T>
+void C<T>::f() requires X<T> {
+ D::n = 0;
+}
+
+void test() {
+ C<A>::f();
+ C<B>::f();
+ // expected-error at -1 {{invalid reference to function 'f': constraints not satisfied}}
+ // expected-note@#DependentFriends_C_f {{because 'DependentFriends::B' does not satisfy 'X'}}
+ // expected-note@#DependentFriends_X {{because 'typename T::type' would be invalid: no type named 'type' in 'DependentFriends::B'}}
+}
+}
>From d1ffffd15e45840262ffd37a7866e988d8dbbc5e Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 17:29:53 +0300
Subject: [PATCH 16/38] fix formatting
---
clang/lib/AST/DeclPrinter.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index d9f4ebbe9bb63..b84745382a976 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -890,19 +890,15 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
if (TypeSourceInfo *TSI = D->getFriendType()) {
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);
}
>From 7a0ad25525b48ffe5a6624616579c89608029f6c Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 21:01:37 +0300
Subject: [PATCH 17/38] handle requires clause in tpl params list
---
clang/lib/AST/ASTStructuralEquivalence.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 4b2663420b80a..aa09ea7efcf92 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2279,7 +2279,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
- return true;
+ return IsStructurallyEquivalent(Context, Params1->getRequiresClause(),
+ Params2->getRequiresClause());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
@@ -2811,7 +2812,7 @@ bool StructuralEquivalenceContext::IsEquivalent(TemplateParameterList *TPL1,
return false;
}
- return true;
+ return IsEquivalent(TPL1->getRequiresClause(), TPL2->getRequiresClause());
}
bool StructuralEquivalenceContext::CheckKindSpecificEquivalence(
>From 600949e57d4aabf6c163ba458b8abe22fef2b6c4 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 21:05:32 +0300
Subject: [PATCH 18/38] wrap FinishTemplateArgumentDeduction into
runWithSufficientStackSpace
---
clang/lib/Sema/SemaAccess.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 91e84d62f27c9..d124acdb8c864 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -305,12 +305,12 @@ static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
if (Inst.isInvalid())
return false;
- if (S.FinishTemplateArgumentDeduction(
- TD, TPL, PatternArgs, Args, Deduced, Info,
- /*CopyDeducedArgs=*/false) != TemplateDeductionResult::Success)
- return false;
-
- return !Trap.hasErrorOccurred();
+ TemplateDeductionResult Result;
+ S.runWithSufficientStackSpace(Info.getLocation(), [&] {
+ Result = S.FinishTemplateArgumentDeduction(
+ TD, TPL, PatternArgs, Args, Deduced, Info, /*CopyDeducedArgs=*/false);
+ });
+ return Result == TemplateDeductionResult::Success && !Trap.hasErrorOccurred();
}
static CanQual<FunctionProtoType>
>From cac422f588b8e0c6b7e9ca13823c89d50a90a6c8 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 30 Apr 2026 14:22:07 +0300
Subject: [PATCH 19/38] update FriendTemplate in DeclNodes to inherit from
Friend
---
clang/include/clang/AST/DeclFriend.h | 2 +-
clang/include/clang/Basic/DeclNodes.td | 2 +-
clang/lib/AST/ASTImporter.cpp | 13 ++++++-------
clang/lib/Sema/SemaAccess.cpp | 2 +-
clang/lib/Sema/SemaDeclCXX.cpp | 6 +++---
5 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 2cd5a1af17fd8..16fe6219bdbb0 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -130,7 +130,7 @@ class FriendDecl : public Decl {
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) {
- return K == Decl::Friend || K == Decl::FriendTemplate;
+ return K >= firstFriend && K <= lastFriend;
}
};
/// An iterator over the friend declarations of a class.
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index ffb58b43812dc..c55a3d68184f4 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -99,7 +99,7 @@ def FileScopeAsm : DeclNode<Decl>;
def TopLevelStmt : DeclNode<Decl>, DeclContext;
def AccessSpec : DeclNode<Decl>;
def Friend : DeclNode<Decl>;
-def FriendTemplate : DeclNode<Decl>;
+def FriendTemplate : DeclNode<Friend>;
def StaticAssert : DeclNode<Decl>;
def ExplicitInstantiation : DeclNode<Decl>;
def Block : DeclNode<Decl, "blocks">, DeclContext;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 305d4f16a41f5..4228904c39a05 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4608,8 +4608,8 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
for (FriendDecl *ImportedFriend : RD->friends()) {
if (ImportedFriend->getKind() == Decl::Friend &&
- IsEquivalentFriend(Importer, D, cast<FriendDecl>(ImportedFriend)))
- ImportedEquivalentFriends.push_back(cast<FriendDecl>(ImportedFriend));
+ IsEquivalentFriend(Importer, D, ImportedFriend))
+ ImportedEquivalentFriends.push_back(ImportedFriend);
}
FriendCountAndPosition CountAndPosition =
@@ -4671,11 +4671,10 @@ ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
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));
+ auto *ImportedFriendTemplate = dyn_cast<FriendTemplateDecl>(ImportedFriend);
+ if (ImportedFriendTemplate &&
+ IsEquivalentFriend(Importer, D, ImportedFriendTemplate))
+ ImportedEquivalentFriends.push_back(ImportedFriendTemplate);
}
FriendCountAndPosition CountAndPosition =
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index d124acdb8c864..7c28708783b99 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -890,7 +890,7 @@ static AccessResult GetFriendKind(Sema &S,
if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend))
AR = MatchesFriend(S, EC, FTD);
else
- AR = MatchesFriend(S, EC, cast<FriendDecl>(Friend));
+ AR = MatchesFriend(S, EC, Friend);
switch (AR) {
case AR_accessible:
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 751a81a65b1a1..f69c28967c0bb 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18199,7 +18199,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
TL.setQualifierLoc(SS.getWithLocInContext(Context));
TL.setNameLoc(NameLoc);
- Decl *Friend;
+ FriendDecl *Friend;
if (TempParamLists.empty())
Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc,
EllipsisLoc);
@@ -18214,7 +18214,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
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->setUnsupportedFriend(true);
}
Friend->setAccess(AS_public);
@@ -18661,7 +18661,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
}
ArrayRef<TemplateParameterList *> TPL = FD->getTemplateParameterLists();
- Decl *Friend;
+ FriendDecl *Friend;
if (TPL.size() > 0 && SS.isValid()) {
if (CheckTemplateDeclScope(S, TPL.back()))
return nullptr;
>From 76c27e076a7abe1a4a96e9d9d6e8ee67fe55c704 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 30 Apr 2026 16:58:23 +0300
Subject: [PATCH 20/38] remove unsupported friend flag
---
clang/include/clang/AST/DeclFriend.h | 19 +--
clang/include/clang/Basic/DiagnosticGroups.td | 1 -
.../clang/Basic/DiagnosticSemaKinds.td | 10 --
clang/include/clang/Sema/Template.h | 9 ++
clang/lib/AST/ASTImporter.cpp | 1 -
clang/lib/Sema/SemaAccess.cpp | 7 +-
clang/lib/Sema/SemaDeclCXX.cpp | 20 +--
clang/lib/Sema/SemaTemplate.cpp | 9 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 147 ++++++++++--------
clang/lib/Serialization/ASTReaderDecl.cpp | 2 -
clang/lib/Serialization/ASTWriterDecl.cpp | 2 -
clang/test/CXX/drs/cwg28xx.cpp | 2 +-
clang/test/Parser/cxx2c-variadic-friends.cpp | 6 +-
clang/test/SemaTemplate/friend-template.cpp | 9 +-
14 files changed, 112 insertions(+), 132 deletions(-)
diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h
index 16fe6219bdbb0..4f0b865dbfbc7 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -58,12 +58,6 @@ class FriendDecl : public Decl {
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;
-
protected:
// The declaration that's a friend of this class.
FriendUnion Friend;
@@ -73,10 +67,9 @@ class FriendDecl : public Decl {
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() {}
+ Friend(Friend), NextFriend() {}
- FriendDecl(Kind K, EmptyShell Empty)
- : Decl(K, Empty), UnsupportedFriend(false) {}
+ FriendDecl(Kind K, EmptyShell Empty) : Decl(K, Empty) {}
FriendDecl *getNextFriend() {
if (NextFriend.isOffset())
@@ -117,14 +110,6 @@ class FriendDecl : public Decl {
SourceRange getSourceRange() const override LLVM_READONLY;
- /// Determines if this friend kind is unsupported.
- bool isUnsupportedFriend() const {
- return UnsupportedFriend;
- }
- void setUnsupportedFriend(bool Unsupported) {
- UnsupportedFriend = Unsupported;
- }
-
bool isPackExpansion() const { return EllipsisLoc.isValid(); }
// Implement isa/cast/dyncast/etc.
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 74f9fab14d82c..93af83301655b 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1142,7 +1142,6 @@ def Attributes : DiagGroup<"attributes", [UnknownAttributes,
def UnknownSanitizers : DiagGroup<"unknown-sanitizers">;
def UnnamedTypeTemplateArgs : DiagGroup<"unnamed-type-template-args",
[CXX98CompatUnnamedTypeTemplateArgs]>;
-def UnsupportedFriend : DiagGroup<"unsupported-friend">;
def UnusedArgument : DiagGroup<"unused-argument">;
def UnusedCommandLineArgument : DiagGroup<"unused-command-line-argument">;
def IgnoredOptimizationArgument : DiagGroup<"ignored-optimization-argument">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a0c3eb361a66..f5c93412abf76 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1908,16 +1908,6 @@ def err_friend_not_first_in_declaration : Error<
"'friend' must appear first in a non-function declaration">;
def err_using_decl_friend : Error<
"cannot befriend target of using declaration">;
-def warn_template_qualified_friend_unsupported
- : Warning<
- "dependent nested name specifier %0 for friend class declaration is "
- "not supported; turning off access control for %1">,
- InGroup<UnsupportedFriend>;
-def warn_template_qualified_friend_ignored
- : Warning<"dependent nested name specifier %0 for friend template "
- "declaration is "
- "not supported; ignoring this friend declaration">,
- InGroup<UnsupportedFriend>;
def ext_friend_tag_redecl_outside_namespace : ExtWarn<
"unqualified friend declaration referring to type outside of the nearest "
"enclosing namespace is a Microsoft extension; add a nested name specifier">,
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 62b1c16de82c1..42febace0a671 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -724,6 +724,10 @@ enum class TemplateSubstitutionKind : char {
TemplateParameterList *
SubstTemplateParams(TemplateParameterList *List);
+ bool SubstTemplateParameterLists(
+ ArrayRef<TemplateParameterList *> TPL,
+ SmallVectorImpl<TemplateParameterList *> &InstTPL);
+
bool SubstQualifier(const DeclaratorDecl *OldDecl,
DeclaratorDecl *NewDecl);
bool SubstQualifier(const TagDecl *OldDecl,
@@ -734,6 +738,11 @@ enum class TemplateSubstitutionKind : char {
ArrayRef<TemplateArgument> Converted,
VarTemplateSpecializationDecl *PrevDecl = nullptr);
+ template <typename FriendTy>
+ bool
+ InstantiateFriendPackExpansion(FriendTy *D, TypeSourceInfo *TSI,
+ ArrayRef<TemplateParameterList *> TPL = {});
+
Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias);
Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
ClassTemplatePartialSpecializationDecl *
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 4228904c39a05..5533cacac129c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4727,7 +4727,6 @@ ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
*EllipsisLocOrErr))
return FTD;
- FTD->setUnsupportedFriend(D->isUnsupportedFriend());
FTD->setAccess(D->getAccess());
FTD->setLexicalDeclContext(LexicalDC);
LexicalDC->addDeclInternal(FTD);
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 7c28708783b99..0a67b2b9d8f6b 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -851,9 +851,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
FriendDecl *FriendD) {
- // Whitelist accesses if there's an invalid or unsupported friend
- // declaration.
- if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
+ // Whitelist accesses if there's an invalid friend declaration.
+ if (FriendD->isInvalidDecl())
return AR_accessible;
if (NamedDecl *Friend = FriendD->getFriendDecl())
@@ -867,7 +866,7 @@ static AccessResult MatchesFriend(Sema &S,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FTD) {
- if (FTD->isInvalidDecl() || FTD->isUnsupportedFriend())
+ if (FTD->isInvalidDecl())
return AR_accessible;
if (NamedDecl *ND = FTD->getFriendDecl())
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f69c28967c0bb..d9657afc260cd 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18103,16 +18103,16 @@ DeclResult Sema::ActOnTemplatedFriendTag(
TagLoc, NameLoc, SS, nullptr, TempParamLists, /*friend*/ true,
IsMemberSpecialization, Invalid)) {
if (TemplateParams->size() > 0) {
- // This is a declaration of a class template.
if (Invalid)
return true;
- return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS,
- Name, NameLoc, Attr, TemplateParams, AS_public,
- /*ModulePrivateLoc=*/SourceLocation(),
- FriendLoc, TempParamLists.size() - 1,
- TempParamLists.data())
- .get();
+ if (SS.isEmpty() || !SS.getScopeRep().isDependent()) {
+ DeclResult Result = CheckClassTemplate(
+ S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr,
+ TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(),
+ FriendLoc, TempParamLists.size() - 1, TempParamLists.data());
+ return Result.get();
+ }
} else {
// The "template<>" header is extraneous.
Diag(TemplateParams->getTemplateLoc(), diag::err_template_tag_noparams)
@@ -18211,12 +18211,6 @@ DeclResult Sema::ActOnTemplatedFriendTag(
FriendLoc, TempParamLists, EllipsisLoc);
}
- if (EllipsisLoc.isValid() && NNS.isDependent()) {
- Diag(NameLoc, diag::warn_template_qualified_friend_unsupported)
- << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext);
- Friend->setUnsupportedFriend(true);
- }
-
Friend->setAccess(AS_public);
CurContext->addDecl(Friend);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 1f5d12c2cc068..ef8ab574eb0d2 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1955,14 +1955,9 @@ DeclResult Sema::CheckClassTemplate(
if (SS.isNotEmpty() && !SS.isInvalid()) {
SemanticContext = computeDeclContext(SS, true);
if (!SemanticContext) {
- // FIXME: Horrible, horrible hack! We can't currently represent this
- // in the AST, and historically we have just ignored such friend
- // class templates, so don't complain here.
- Diag(NameLoc, TUK == TagUseKind::Friend
- ? diag::warn_template_qualified_friend_ignored
- : diag::err_template_qualified_declarator_no_match)
+ Diag(NameLoc, diag::err_template_qualified_declarator_no_match)
<< SS.getScopeRep() << SS.getRange();
- return TUK != TagUseKind::Friend;
+ return true;
}
if (RequireCompleteDeclContext(SS, SemanticContext))
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index ed3ed8e437327..a4fde32e41552 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2001,68 +2001,69 @@ Decl *TemplateDeclInstantiator::VisitIndirectFieldDecl(IndirectFieldDecl *D) {
return IndirectField;
}
-Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
- // Handle friend type expressions by simply substituting template
- // parameters into the pattern type and checking the result.
- if (TypeSourceInfo *Ty = D->getFriendType()) {
- TypeSourceInfo *InstTy;
- // If this is an unsupported friend, don't bother substituting template
- // arguments into it. The actual type referred to won't be used by any
- // parts of Clang, and may not be valid for instantiating. Just use the
- // same info for the instantiated friend.
- if (D->isUnsupportedFriend()) {
- InstTy = Ty;
- } else {
- if (D->isPackExpansion()) {
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- SemaRef.collectUnexpandedParameterPacks(Ty->getTypeLoc(), Unexpanded);
- assert(!Unexpanded.empty() && "Pack expansion without packs");
-
- bool ShouldExpand = true;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = std::nullopt;
- if (SemaRef.CheckParameterPacksForExpansion(
- D->getEllipsisLoc(), D->getSourceRange(), Unexpanded,
- TemplateArgs, /*FailOnPackProducingTemplates=*/true,
- ShouldExpand, RetainExpansion, NumExpansions))
- return nullptr;
+template <typename FriendTy>
+bool TemplateDeclInstantiator::InstantiateFriendPackExpansion(
+ FriendTy *D, TypeSourceInfo *TSI, ArrayRef<TemplateParameterList *> TPL) {
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ SemaRef.collectUnexpandedParameterPacks(TSI->getTypeLoc(), Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without packs");
- assert(!RetainExpansion &&
- "should never retain an expansion for a variadic friend decl");
-
- if (ShouldExpand) {
- SmallVector<FriendDecl *> Decls;
- for (unsigned I = 0; I != *NumExpansions; I++) {
- Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
- TypeSourceInfo *TSI = SemaRef.SubstType(
- Ty, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
- if (!TSI)
- return nullptr;
-
- auto FD =
- FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
- TSI, D->getFriendLoc());
-
- FD->setAccess(AS_public);
- Owner->addDecl(FD);
- Decls.push_back(FD);
- }
+ bool ShouldExpand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions = std::nullopt;
+ if (SemaRef.CheckParameterPacksForExpansion(
+ D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
+ /*FailOnPackProducingTemplates=*/true, ShouldExpand, RetainExpansion,
+ NumExpansions))
+ return true;
- // Just drop this node; we have no use for it anymore.
- return nullptr;
- }
- }
+ assert(!RetainExpansion &&
+ "should never retain an expansion for a friend declaration");
+
+ if (!ShouldExpand)
+ return false;
+
+ for (unsigned I = 0; I != *NumExpansions; I++) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
+ SmallVector<TemplateParameterList *, 1> InstTPL;
+ if (SubstTemplateParameterLists(TPL, InstTPL))
+ return true;
+
+ TypeSourceInfo *InstTy = SemaRef.SubstType(
+ TSI, TemplateArgs, D->getEllipsisLoc(), DeclarationName());
+ if (!InstTy)
+ return true;
- InstTy = SemaRef.SubstType(Ty, TemplateArgs, D->getLocation(),
- DeclarationName());
+ FriendDecl *FD;
+ if (isa<FriendTemplateDecl>(D))
+ FD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ InstTy, D->getFriendLoc(), InstTPL);
+ else {
+ assert(InstTPL.empty() && "unexpected template parameter lists");
+ FD = FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), InstTy,
+ D->getFriendLoc());
}
+
+ FD->setAccess(AS_public);
+ Owner->addDecl(FD);
+ }
+
+ return true;
+}
+
+Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
+ if (TypeSourceInfo *Ty = D->getFriendType()) {
+ if (D->isPackExpansion() && InstantiateFriendPackExpansion(D, Ty))
+ return nullptr;
+
+ TypeSourceInfo *InstTy = SemaRef.SubstType(
+ Ty, TemplateArgs, D->getLocation(), DeclarationName());
if (!InstTy)
return nullptr;
FriendDecl *FD = FriendDecl::Create(
SemaRef.Context, Owner, D->getLocation(), InstTy, D->getFriendLoc());
FD->setAccess(AS_public);
- FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}
@@ -2081,7 +2082,6 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(),
cast<NamedDecl>(NewND), D->getFriendLoc());
FD->setAccess(AS_public);
- FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
return FD;
}
@@ -4725,23 +4725,27 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
ArrayRef<TemplateParameterList *> TPLists =
D->getFriendTypeTemplateParameterLists();
- SmallVector<TemplateParameterList *, 1> TPL(TPLists.size());
- for (unsigned I = 0, N = TPLists.size(); I != N; ++I) {
- TemplateParameterList *InstParams = SubstTemplateParams(TPLists[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)
+ if (D->isPackExpansion() && InstantiateFriendPackExpansion(D, FT, TPLists))
+ return nullptr;
+
+ SmallVector<TemplateParameterList *, 1> TPL;
+ if (SubstTemplateParameterLists(TPLists, TPL))
+ return nullptr;
+
+ TypeSourceInfo *InstTy = SemaRef.SubstType(
+ FT, TemplateArgs, D->getLocation(), DeclarationName());
+ if (InstTy) {
FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
- TSI, D->getFriendLoc(), TPL);
+ InstTy, D->getFriendLoc(), TPL);
+ }
} else {
+ SmallVector<TemplateParameterList *, 1> TPL;
+ if (SubstTemplateParameterLists(TPLists, TPL))
+ return nullptr;
+
if (cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
D->getLocation(), D->getFriendDecl(), TemplateArgs)))
FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
@@ -4892,6 +4896,19 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
return InstL;
}
+bool TemplateDeclInstantiator::SubstTemplateParameterLists(
+ ArrayRef<TemplateParameterList *> TPL,
+ SmallVectorImpl<TemplateParameterList *> &InstTPL) {
+ for (TemplateParameterList *L : TPL) {
+ TemplateParameterList *InstParams = SubstTemplateParams(L);
+ if (!InstParams)
+ return true;
+
+ InstTPL.push_back(InstParams);
+ }
+ return false;
+}
+
TemplateParameterList *
Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs,
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 0896bae6abc28..cbcd92c9cc810 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2399,7 +2399,6 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
else
D->Friend = readTypeSourceInfo();
D->NextFriend = readDeclID().getRawValue();
- D->UnsupportedFriend = (Record.readInt() != 0);
D->FriendLoc = readSourceLocation();
D->EllipsisLoc = readSourceLocation();
}
@@ -2413,7 +2412,6 @@ void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
else
D->Friend = readTypeSourceInfo();
D->NextFriend = readDeclID().getRawValue();
- D->UnsupportedFriend = (Record.readInt() != 0);
D->FriendLoc = readSourceLocation();
D->EllipsisLoc = readSourceLocation();
}
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index c67467e3d6b01..4a6fdba869480 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1832,7 +1832,6 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
else
Record.AddTypeSourceInfo(D->getFriendType());
Record.AddDeclRef(D->getNextFriend());
- Record.push_back(D->UnsupportedFriend);
Record.AddSourceLocation(D->FriendLoc);
Record.AddSourceLocation(D->EllipsisLoc);
Code = serialization::DECL_FRIEND;
@@ -1849,7 +1848,6 @@ void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
else
Record.AddTypeSourceInfo(D->getFriendType());
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/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 6876cfa12ff51..e574a5f48c507 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -189,7 +189,7 @@ struct A {
// FIXME: The index of the pack-index-specifier is printed as a memory address in the diagnostic.
template<typename U>
friend struct Ts...[0]::C;
- // since-cxx26-warning at -1 {{dependent nested name specifier 'Ts...[0]' for friend template declaration is not supported; ignoring this friend declaration}}
+ // since-cxx26-error at -1 {{friend declaration does not name a member of a class template specialization}}
};
#endif
diff --git a/clang/test/Parser/cxx2c-variadic-friends.cpp b/clang/test/Parser/cxx2c-variadic-friends.cpp
index 621ae912c1ac9..23451e1a80a12 100644
--- a/clang/test/Parser/cxx2c-variadic-friends.cpp
+++ b/clang/test/Parser/cxx2c-variadic-friends.cpp
@@ -56,13 +56,11 @@ struct VS {
template<bool... Bs>
friend class E<Bs>::Nested...; // expected-error {{friend declaration expands pack 'Bs' that is declared it its own template parameter list}}
- // FIXME: Both of these should be valid, but we can't handle these at
- // the moment because the NNS is dependent.
template<class ...T>
- friend class TS<Ts>::Nested...; // expected-warning {{dependent nested name specifier 'TS<Ts>' for friend template declaration is not supported; ignoring this friend declaration}}
+ friend class TS<Ts>::Nested...;
template<class T>
- friend class D<T, Ts>::Nested...; // expected-warning {{dependent nested name specifier 'D<T, Ts>' for friend class declaration is not supported; turning off access control for 'VS'}}
+ friend class D<T, Ts>::Nested...;
};
namespace length_mismatch {
diff --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 2b5a226c3b33c..811a5c352b885 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -235,20 +235,19 @@ namespace rdar11147355 {
template <class T>
struct A {
template <class U> class B;
- template <class S> template <class U> friend class A<S>::B; // expected-warning {{dependent nested name specifier 'A<S>' for friend template declaration is not supported; ignoring this friend declaration}}
+ template <class S> template <class U> friend class A<S>::B;
private:
- int n; // expected-note {{here}}
+ int n;
};
template <class S> template <class U> class A<S>::B {
public:
- // FIXME: This should be permitted.
- int f(A<S*> a) { return a.n; } // expected-error {{private}}
+ int f(A<S*> a) { return a.n; }
};
A<double>::B<double> ab;
A<double*> a;
- int k = ab.f(a); // expected-note {{instantiation of}}
+ int k = ab.f(a);
}
namespace RedeclUnrelated {
>From 6a2425d3c72f080c62fde1e681234b1ca9beda78 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 30 Apr 2026 19:49:04 +0300
Subject: [PATCH 21/38] remove unsupported friend
---
clang-tools-extra/clang-doc/Serialize.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index ef7fed6cc8501..e8538aa84a7f2 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -996,9 +996,6 @@ void Serializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
return;
for (const FriendDecl *FD : D->friends()) {
- if (FD->isUnsupportedFriend())
- continue;
-
FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
const auto *ActualDecl = FD->getFriendDecl();
if (!ActualDecl) {
>From 2e33fcd98bfc9a5d08463795b214bb46ef3f1772 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 1 May 2026 00:21:41 +0300
Subject: [PATCH 22/38] add support class template friends with dependent
qualifiers
---
clang/lib/Sema/SemaAccess.cpp | 77 +++++++++++++++----
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 28 ++++++-
2 files changed, 85 insertions(+), 20 deletions(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 0a67b2b9d8f6b..1b53349a1b175 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -319,7 +319,8 @@ GetCanonicalFunctionProto(Sema &S, const FunctionDecl *FD) {
}
static const TemplateSpecializationType *
-TryGetTemplateSpecializationType(Sema &S, NestedNameSpecifier NNS) {
+GetCanonicalQualifierTemplateSpecializationType(Sema &S,
+ NestedNameSpecifier NNS) {
if (!NNS)
return nullptr;
@@ -352,12 +353,12 @@ static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
return nullptr;
}
-static bool MatchesFriendContext(Sema &S, FunctionDecl *FD,
+static bool MatchesFriendContext(Sema &S, DeclContext *DC,
ClassTemplateDecl *FriendCTD,
ArrayRef<TemplateArgument> FriendArgs,
TemplateParameterList *FriendTPL,
SourceLocation Loc) {
- const auto *RD = dyn_cast<CXXRecordDecl>(FD->getDeclContext());
+ const auto *RD = dyn_cast<CXXRecordDecl>(DC);
if (!RD)
return false;
@@ -690,12 +691,56 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
return MatchesFriend(S, EC, cast<FunctionDecl>(ND));
}
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ ClassTemplateDecl *FriendCTD) {
+ AccessResult OnFailure = AR_inaccessible;
+ const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
+ S, FriendCTD->getTemplatedDecl()->getQualifier());
+ if (!FriendTST)
+ return MatchesFriend(S, EC, FriendCTD);
+
+ auto *FriendContextCTD = dyn_cast<ClassTemplateDecl>(
+ FriendTST->getTemplateName().getAsTemplateDecl());
+ if (!FriendContextCTD)
+ return OnFailure;
+
+ ArrayRef<TemplateParameterList *> FriendTPLists =
+ FriendTD->getFriendTypeTemplateParameterLists();
+ if (FriendTPLists.empty())
+ return OnFailure;
+
+ TemplateParameterList *FriendTPL = FriendTPLists.front();
+ if (!FriendTPL)
+ return OnFailure;
+
+ ArrayRef<TemplateArgument> FriendArgs = FriendTST->template_arguments();
+ for (CXXRecordDecl *RD : EC.Records) {
+ ClassTemplateDecl *ContextCTD = nullptr;
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ ContextCTD = CTSD->getSpecializedTemplate();
+ else
+ ContextCTD = RD->getDescribedClassTemplate();
+
+ if (!ContextCTD)
+ continue;
+
+ if (MightInstantiateTo(ContextCTD->getTemplatedDecl(),
+ FriendCTD->getTemplatedDecl()) &&
+ MatchesFriendContext(S, RD->getDeclContext(), FriendContextCTD,
+ FriendArgs, FriendTPL, FriendTD->getLocation()))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
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);
+ const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
+ S, FriendFTD->getTemplatedDecl()->getQualifier());
if (!FriendTST)
return OnFailure;
@@ -717,8 +762,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
SourceLocation FriendLoc = FriendTD->getLocation();
for (FunctionDecl *FD : EC.Functions) {
- if (!MatchesFriendContext(S, FD, FriendCTD, FriendArgs, FriendTPL,
- FriendLoc))
+ if (!MatchesFriendContext(S, FD->getDeclContext(), FriendCTD, FriendArgs,
+ FriendTPL, FriendLoc))
continue;
FunctionTemplateDecl *ContextFTD = TryGetFunctionTemplateDecl(FD);
@@ -733,8 +778,8 @@ 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);
+ const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
+ S, FriendFD->getQualifier());
if (!FriendTST)
return OnFailure;
@@ -762,8 +807,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!MightInstantiateTo(S, FD->getDeclName(), FriendFD->getDeclName()))
continue;
- if (!MatchesFriendContext(S, FD, FriendCTD, FriendArgs, FriendTPL,
- FriendLoc))
+ if (!MatchesFriendContext(S, FD->getDeclContext(), FriendCTD, FriendArgs,
+ FriendTPL, FriendLoc))
continue;
CanQual<FunctionProtoType> ContextProto = GetCanonicalFunctionProto(S, FD);
@@ -776,6 +821,9 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FTD, NamedDecl *ND) {
+ if (auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
+ return MatchesFriend(S, EC, FTD, CTD);
+
if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND))
return MatchesFriend(S, EC, FTD, TD);
@@ -801,12 +849,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!NNS)
return OnFailure;
- const auto *T = NNS.getAsType();
- if (!T)
- return OnFailure;
-
- const auto *TST =
- S.Context.getCanonicalType(T)->getAsNonAliasTemplateSpecializationType();
+ const auto *TST = GetCanonicalQualifierTemplateSpecializationType(S, NNS);
if (!TST)
return OnFailure;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index a4fde32e41552..32de665297205 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4735,9 +4735,31 @@ Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
if (SubstTemplateParameterLists(TPLists, TPL))
return nullptr;
- TypeSourceInfo *InstTy = SemaRef.SubstType(
- FT, TemplateArgs, D->getLocation(), DeclarationName());
- if (InstTy) {
+ ClassTemplateDecl *CTD = nullptr;
+ if (auto DNT = FT->getTypeLoc().getAs<DependentNameTypeLoc>()) {
+ NestedNameSpecifierLoc QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(
+ DNT.getQualifierLoc(), TemplateArgs);
+ if (QualifierLoc) {
+ CXXScopeSpec SS;
+ SS.Adopt(QualifierLoc);
+
+ DeclContext *DC =
+ SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
+ if (DC) {
+ LookupResult Result(SemaRef, DNT.getTypePtr()->getIdentifier(),
+ DNT.getNameLoc(), Sema::LookupOrdinaryName,
+ SemaRef.forRedeclarationInCurContext());
+ SemaRef.LookupQualifiedName(Result, DC);
+ CTD = Result.getAsSingle<ClassTemplateDecl>();
+ }
+ }
+ }
+
+ if (CTD) {
+ FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ CTD, D->getFriendLoc(), TPL);
+ } else if (TypeSourceInfo *InstTy = SemaRef.SubstType(
+ FT, TemplateArgs, D->getLocation(), DeclarationName())) {
FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
InstTy, D->getFriendLoc(), TPL);
}
>From 4843b85d794c0f591949dfc1bf839cb66406dbb2 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 1 May 2026 08:31:53 +0300
Subject: [PATCH 23/38] fix friend template serialization
---
clang/lib/Serialization/ASTWriterDecl.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 4a6fdba869480..6e96fc6021696 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1822,8 +1822,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.
VisitDecl(D);
bool hasFriendDecl = isa<NamedDecl *>(D->Friend);
Record.push_back(hasFriendDecl);
@@ -1838,8 +1836,10 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
}
void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
- VisitDecl(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);
for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
Record.AddTemplateParameterList(TPL);
Record.push_back(D->getFriendDecl() != nullptr);
>From 4b98963f0bab293c1d25f50b8dff96877a60b7c0 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sat, 2 May 2026 10:18:00 +0300
Subject: [PATCH 24/38] handle failed friends template deduction
---
clang/lib/Sema/SemaAccess.cpp | 199 ++++++++++++------
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 3 +-
clang/test/SemaTemplate/friend-template.cpp | 25 +++
3 files changed, 163 insertions(+), 64 deletions(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 1b53349a1b175..2c11521ddf211 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -23,6 +23,7 @@
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Template.h"
+#include "clang/Sema/TemplateDeduction.h"
using namespace clang;
using namespace sema;
@@ -276,11 +277,32 @@ struct AccessTarget : public AccessedEntity {
}
+static void AddFriendTemplateDeductionCandidate(
+ Sema &S, TemplateDecl *TD, Decl *Declaration, TemplateDeductionInfo &Info,
+ TemplateDeductionResult Result, TemplateSpecCandidateSet *FailedTSC) {
+ if (!FailedTSC)
+ return;
+
+ const Decl *CanonicalDeclaration = Declaration->getCanonicalDecl();
+ for (TemplateSpecCandidate &Candidate : *FailedTSC) {
+ if (!Candidate.Specialization)
+ continue;
+
+ if (Candidate.Specialization->getCanonicalDecl() == CanonicalDeclaration)
+ return;
+ }
+
+ FailedTSC->addCandidate().set(
+ DeclAccessPair::make(TD, AS_public), Declaration,
+ MakeDeductionFailureInfo(S.Context, Result, Info));
+}
+
static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
TemplateDecl *TD,
ArrayRef<TemplateArgument> PatternArgs,
ArrayRef<TemplateArgument> Args,
- SourceLocation Loc) {
+ SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC) {
auto Equal =
llvm::equal(PatternArgs, Args,
[](const TemplateArgument &LHS, const TemplateArgument &RHS) {
@@ -295,22 +317,41 @@ static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
Sema::SFINAETrap Trap(S, Info);
LocalInstantiationScope InstantiationScope(S);
SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
- if (S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
- /*NumberOfArgumentsMustMatch=*/false) !=
- TemplateDeductionResult::Success)
+ TemplateDeductionResult DeductionResult =
+ S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced,
+ /*NumberOfArgumentsMustMatch=*/false);
+ if (DeductionResult != TemplateDeductionResult::Success) {
+ AddFriendTemplateDeductionCandidate(S, TD, TD->getTemplatedDecl(), Info,
+ DeductionResult, FailedTSC);
return false;
+ }
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
Sema::InstantiatingTemplate Inst(S, Info.getLocation(), TD, DeducedArgs);
- if (Inst.isInvalid())
+ if (Inst.isInvalid()) {
+ AddFriendTemplateDeductionCandidate(
+ S, TD, TD->getTemplatedDecl(), Info,
+ TemplateDeductionResult::InstantiationDepth, FailedTSC);
return false;
+ }
TemplateDeductionResult Result;
S.runWithSufficientStackSpace(Info.getLocation(), [&] {
Result = S.FinishTemplateArgumentDeduction(
TD, TPL, PatternArgs, Args, Deduced, Info, /*CopyDeducedArgs=*/false);
});
- return Result == TemplateDeductionResult::Success && !Trap.hasErrorOccurred();
+
+ if (Result != TemplateDeductionResult::Success || Trap.hasErrorOccurred()) {
+ TemplateDeductionResult Failure =
+ Result != TemplateDeductionResult::Success
+ ? Result
+ : TemplateDeductionResult::SubstitutionFailure;
+ AddFriendTemplateDeductionCandidate(S, TD, TD->getTemplatedDecl(), Info,
+ Failure, FailedTSC);
+ return false;
+ }
+
+ return true;
}
static CanQual<FunctionProtoType>
@@ -328,7 +369,6 @@ GetCanonicalQualifierTemplateSpecializationType(Sema &S,
if (Ty.isNull())
return nullptr;
- Ty = S.Context.getCanonicalType(Ty);
if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(S.Context);
@@ -357,7 +397,8 @@ static bool MatchesFriendContext(Sema &S, DeclContext *DC,
ClassTemplateDecl *FriendCTD,
ArrayRef<TemplateArgument> FriendArgs,
TemplateParameterList *FriendTPL,
- SourceLocation Loc) {
+ SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC) {
const auto *RD = dyn_cast<CXXRecordDecl>(DC);
if (!RD)
return false;
@@ -378,7 +419,7 @@ static bool MatchesFriendContext(Sema &S, DeclContext *DC,
return false;
return CanDeduceTemplateArguments(S, FriendTPL, FriendCTD, FriendArgs,
- ContextArgs, Loc);
+ ContextArgs, Loc, FailedTSC);
}
/// Checks whether one class might instantiate to the other.
@@ -693,7 +734,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FriendTD,
- ClassTemplateDecl *FriendCTD) {
+ ClassTemplateDecl *FriendCTD,
+ TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
S, FriendCTD->getTemplatedDecl()->getQualifier());
@@ -728,7 +770,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (MightInstantiateTo(ContextCTD->getTemplatedDecl(),
FriendCTD->getTemplatedDecl()) &&
MatchesFriendContext(S, RD->getDeclContext(), FriendContextCTD,
- FriendArgs, FriendTPL, FriendTD->getLocation()))
+ FriendArgs, FriendTPL, FriendTD->getLocation(),
+ FailedTSC))
return AR_accessible;
}
@@ -737,7 +780,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FriendTD,
- FunctionTemplateDecl *FriendFTD) {
+ FunctionTemplateDecl *FriendFTD,
+ TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
S, FriendFTD->getTemplatedDecl()->getQualifier());
@@ -763,7 +807,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
for (FunctionDecl *FD : EC.Functions) {
if (!MatchesFriendContext(S, FD->getDeclContext(), FriendCTD, FriendArgs,
- FriendTPL, FriendLoc))
+ FriendTPL, FriendLoc, FailedTSC))
continue;
FunctionTemplateDecl *ContextFTD = TryGetFunctionTemplateDecl(FD);
@@ -776,7 +820,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FriendTD,
- FunctionDecl *FriendFD) {
+ FunctionDecl *FriendFD,
+ TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
S, FriendFD->getQualifier());
@@ -808,7 +853,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
continue;
if (!MatchesFriendContext(S, FD->getDeclContext(), FriendCTD, FriendArgs,
- FriendTPL, FriendLoc))
+ FriendTPL, FriendLoc, FailedTSC))
continue;
CanQual<FunctionProtoType> ContextProto = GetCanonicalFunctionProto(S, FD);
@@ -820,22 +865,23 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
}
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
- FriendTemplateDecl *FTD, NamedDecl *ND) {
+ FriendTemplateDecl *FTD, NamedDecl *ND,
+ TemplateSpecCandidateSet *FailedTSC) {
if (auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
- return MatchesFriend(S, EC, FTD, CTD);
+ return MatchesFriend(S, EC, FTD, CTD, FailedTSC);
if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND))
- return MatchesFriend(S, EC, FTD, TD);
+ return MatchesFriend(S, EC, FTD, TD, FailedTSC);
if (auto *FD = dyn_cast<FunctionDecl>(ND))
- return MatchesFriend(S, EC, FTD, FD);
+ return MatchesFriend(S, EC, FTD, FD, FailedTSC);
return MatchesFriend(S, EC, ND);
}
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
- FriendTemplateDecl *FTD,
- TypeSourceInfo *TSI) {
+ FriendTemplateDecl *FTD, TypeSourceInfo *TSI,
+ TemplateSpecCandidateSet *FailedTSC) {
QualType TypeAsWritten = TSI->getType();
if (!TypeAsWritten->isDependentType())
return MatchesFriend(S, EC, S.Context.getCanonicalType(TypeAsWritten));
@@ -882,7 +928,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (CanDeduceTemplateArguments(S, TPL, CTD, TST->template_arguments(),
CTSD->getTemplateArgs().asArray(),
- FTD->getLocation()))
+ FTD->getLocation(), FailedTSC))
return AR_accessible;
}
@@ -908,29 +954,30 @@ static AccessResult MatchesFriend(Sema &S,
}
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
- FriendTemplateDecl *FTD) {
+ FriendTemplateDecl *FTD,
+ TemplateSpecCandidateSet *FailedTSC) {
if (FTD->isInvalidDecl())
return AR_accessible;
if (NamedDecl *ND = FTD->getFriendDecl())
- return MatchesFriend(S, EC, FTD, ND);
+ return MatchesFriend(S, EC, FTD, ND, FailedTSC);
if (TypeSourceInfo *TSI = FTD->getFriendType())
- return MatchesFriend(S, EC, FTD, TSI);
+ return MatchesFriend(S, EC, FTD, TSI, FailedTSC);
return AR_inaccessible;
}
-static AccessResult GetFriendKind(Sema &S,
- const EffectiveContext &EC,
- const CXXRecordDecl *Class) {
+static AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC,
+ const CXXRecordDecl *Class,
+ TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
// Okay, check friends.
for (FriendDecl *Friend : Class->friends()) {
AccessResult AR;
if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend))
- AR = MatchesFriend(S, EC, FTD);
+ AR = MatchesFriend(S, EC, FTD, FailedTSC);
else
AR = MatchesFriend(S, EC, Friend);
@@ -958,6 +1005,7 @@ namespace {
struct ProtectedFriendContext {
Sema &S;
const EffectiveContext &EC;
+ TemplateSpecCandidateSet *FailedTSC;
const CXXRecordDecl *NamingClass;
bool CheckDependent;
bool EverDependent;
@@ -967,18 +1015,19 @@ struct ProtectedFriendContext {
ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
const CXXRecordDecl *InstanceContext,
- const CXXRecordDecl *NamingClass)
- : S(S), EC(EC), NamingClass(NamingClass),
- CheckDependent(InstanceContext->isDependentContext() ||
- NamingClass->isDependentContext()),
- EverDependent(false) {}
+ const CXXRecordDecl *NamingClass,
+ TemplateSpecCandidateSet *FailedTSC)
+ : S(S), EC(EC), FailedTSC(FailedTSC), NamingClass(NamingClass),
+ CheckDependent(InstanceContext->isDependentContext() ||
+ NamingClass->isDependentContext()),
+ EverDependent(false) {}
/// Check classes in the current path for friendship, starting at
/// the given index.
bool checkFriendshipAlongPath(unsigned I) {
assert(I < CurPath.size());
for (unsigned E = CurPath.size(); I != E; ++I) {
- switch (GetFriendKind(S, EC, CurPath[I])) {
+ switch (GetFriendKind(S, EC, CurPath[I], FailedTSC)) {
case AR_accessible: return true;
case AR_inaccessible: continue;
case AR_dependent: EverDependent = true; continue;
@@ -1065,9 +1114,9 @@ struct ProtectedFriendContext {
/// because the original target might have been more accessible
/// because of crazy subclassing.
/// So we don't implement that.
-static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
- const CXXRecordDecl *InstanceContext,
- const CXXRecordDecl *NamingClass) {
+static AccessResult GetProtectedFriendKind(
+ Sema &S, const EffectiveContext &EC, const CXXRecordDecl *InstanceContext,
+ const CXXRecordDecl *NamingClass, TemplateSpecCandidateSet *FailedTSC) {
assert(InstanceContext == nullptr ||
InstanceContext->getCanonicalDecl() == InstanceContext);
assert(NamingClass->getCanonicalDecl() == NamingClass);
@@ -1075,19 +1124,20 @@ static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
// If we don't have an instance context, our constraints give us
// that NamingClass <= P <= NamingClass, i.e. P == NamingClass.
// This is just the usual friendship check.
- if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
+ if (!InstanceContext)
+ return GetFriendKind(S, EC, NamingClass, FailedTSC);
- ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
+ ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass, FailedTSC);
if (PRC.findFriendship(InstanceContext)) return AR_accessible;
if (PRC.EverDependent) return AR_dependent;
return AR_inaccessible;
}
-static AccessResult HasAccess(Sema &S,
- const EffectiveContext &EC,
+static AccessResult HasAccess(Sema &S, const EffectiveContext &EC,
const CXXRecordDecl *NamingClass,
AccessSpecifier Access,
- const AccessTarget &Target) {
+ const AccessTarget &Target,
+ TemplateSpecCandidateSet *FailedTSC) {
assert(NamingClass->getCanonicalDecl() == NamingClass &&
"declaration should be canonicalized before being passed here");
@@ -1207,7 +1257,8 @@ static AccessResult HasAccess(Sema &S,
if (!InstanceContext) return AR_dependent;
}
- switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
+ switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass,
+ FailedTSC)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: return OnFailure;
case AR_dependent: return AR_dependent;
@@ -1215,7 +1266,7 @@ static AccessResult HasAccess(Sema &S,
llvm_unreachable("impossible friendship kind");
}
- switch (GetFriendKind(S, EC, NamingClass)) {
+ switch (GetFriendKind(S, EC, NamingClass, FailedTSC)) {
case AR_accessible: return AR_accessible;
case AR_inaccessible: return OnFailure;
case AR_dependent: return AR_dependent;
@@ -1328,7 +1379,8 @@ static CXXBasePath *FindBestPath(Sema &S,
AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
PathAccess = std::max(PathAccess, BaseAccess);
- switch (HasAccess(S, EC, NC, PathAccess, Target)) {
+ switch (HasAccess(S, EC, NC, PathAccess, Target,
+ /*FailedTSC=*/nullptr)) {
case AR_inaccessible: break;
case AR_accessible:
PathAccess = AS_public;
@@ -1524,7 +1576,8 @@ static void DiagnoseAccessPath(Sema &S,
accessSoFar = D->getAccess();
const CXXRecordDecl *declaringClass = entity.getDeclaringClass();
- switch (HasAccess(S, EC, declaringClass, accessSoFar, entity)) {
+ switch (HasAccess(S, EC, declaringClass, accessSoFar, entity,
+ /*FailedTSC=*/nullptr)) {
// If the declaration is accessible when named in its declaring
// class, then we must be constrained by the path.
case AR_accessible:
@@ -1567,7 +1620,8 @@ static void DiagnoseAccessPath(Sema &S,
accessSoFar = baseAccess;
}
- switch (HasAccess(S, EC, derivingClass, accessSoFar, entity)) {
+ switch (HasAccess(S, EC, derivingClass, accessSoFar, entity,
+ /*FailedTSC=*/nullptr)) {
case AR_inaccessible: break;
case AR_accessible:
accessSoFar = AS_public;
@@ -1671,9 +1725,9 @@ static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S,
/// Determines whether the accessed entity is accessible. Public members
/// have been weeded out by this point.
-static AccessResult IsAccessible(Sema &S,
- const EffectiveContext &EC,
- AccessTarget &Entity) {
+static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC,
+ AccessTarget &Entity,
+ TemplateSpecCandidateSet *FailedTSC) {
// Determine the actual naming class.
const CXXRecordDecl *NamingClass = Entity.getEffectiveNamingClass();
@@ -1685,7 +1739,8 @@ static AccessResult IsAccessible(Sema &S,
// which don't require [M4] or [B4]. These are by far the most
// common forms of privileged access.
if (UnprivilegedAccess != AS_none) {
- switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
+ switch (
+ HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity, FailedTSC)) {
case AR_dependent:
// This is actually an interesting policy decision. We don't
// *have* to delay immediately here: we can do the full access
@@ -1714,7 +1769,7 @@ static AccessResult IsAccessible(Sema &S,
const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
FinalAccess = Target->getAccess();
- switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
+ switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity, FailedTSC)) {
case AR_accessible:
// Target is accessible at EC when named in its declaring class.
// We can now hill-climb and simply check whether the declaring
@@ -1766,25 +1821,30 @@ static void DelayDependentAccess(Sema &S,
Entity.getDiag());
}
-/// Checks access to an entity from the given effective context.
-static AccessResult CheckEffectiveAccess(Sema &S,
- const EffectiveContext &EC,
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
SourceLocation Loc,
- AccessTarget &Entity) {
- assert(Entity.getAccess() != AS_public && "called for public access!");
+ AccessTarget &Entity,
+ TemplateSpecCandidateSet *FailedTSC) {
+ assert((Entity.isQuiet() || FailedTSC) &&
+ "non-quiet access check requires a candidate set");
- switch (IsAccessible(S, EC, Entity)) {
+ switch (IsAccessible(S, EC, Entity, FailedTSC)) {
case AR_dependent:
DelayDependentAccess(S, EC, Loc, Entity);
return AR_dependent;
- case AR_inaccessible:
+ case AR_inaccessible: {
if (S.getLangOpts().MSVCCompat &&
IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity))
return AR_accessible;
- if (!Entity.isQuiet())
- DiagnoseBadAccess(S, Loc, EC, Entity);
+
+ if (Entity.isQuiet())
+ return AR_inaccessible;
+
+ DiagnoseBadAccess(S, Loc, EC, Entity);
+ FailedTSC->NoteCandidates(S, Loc);
return AR_inaccessible;
+ }
case AR_accessible:
return AR_accessible;
@@ -1794,6 +1854,18 @@ static AccessResult CheckEffectiveAccess(Sema &S,
llvm_unreachable("invalid access result");
}
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
+ SourceLocation Loc,
+ AccessTarget &Entity) {
+ assert(Entity.getAccess() != AS_public && "called for public access!");
+
+ if (Entity.isQuiet())
+ return CheckEffectiveAccess(S, EC, Loc, Entity, /*FailedTSC=*/nullptr);
+
+ TemplateSpecCandidateSet FailedTSC(Loc);
+ return CheckEffectiveAccess(S, EC, Loc, Entity, &FailedTSC);
+}
+
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
AccessTarget &Entity) {
// If the access path is public, it's accessible everywhere.
@@ -2278,7 +2350,8 @@ bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass,
AccessTarget Entity(Context, AccessedEntity::Member, NamingClass,
DeclAccessPair::make(Target, AS_none), BaseType);
EffectiveContext EC(CurContext);
- return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
+ return ::IsAccessible(*this, EC, Entity, /*FailedTSC=*/nullptr) !=
+ ::AR_inaccessible;
}
if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index c3c8c3668d91e..46f8139d4451f 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -141,7 +141,7 @@ namespace test7 {
}
namespace test8 {
- template <class T> struct A {
+ template <class T> struct A { // #test8-A
T h();
};
@@ -167,6 +167,7 @@ namespace test8 {
c.n = 0;
// expected-error at -1 {{'n' is a private member of 'test8::C'}}
// expected-note@#test8-C-n {{implicitly declared private here}}
+ // expected-note@#test8-A {{candidate template ignored: could not match 'T *' against 'int'}}
return 0;
}
diff --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 811a5c352b885..3555bb2eb8cb3 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -337,3 +337,28 @@ class Foo {
bool aux;
};
}
+
+namespace GH104057 {
+template <class T>
+struct A { // #GH104057-A
+ template <class> struct B;
+
+private:
+ static void f(); // #GH104057-A-f
+ template <class U> friend struct A<U *>::B;
+};
+
+template <class T>
+template <class U> struct A<T>::B {
+ static void g() {
+ A<int>::f();
+ // expected-error at -1 {{'f' is a private member of 'GH104057::A<int>'}}
+ // expected-note@#GH104057-A-f {{declared private here}}
+ // expected-note@#GH104057-A {{candidate template ignored: could not match 'U *' against 'double'}}
+ }
+};
+
+void test() {
+ A<double>::B<int>::g(); // expected-note {{in instantiation of member function 'GH104057::A<double>::B<int>::g' requested here}}
+}
+}
>From d4918a8d7d1b0d9c94962523203be22e07f1eedd Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sat, 2 May 2026 12:52:41 +0300
Subject: [PATCH 25/38] cleanup
---
clang/lib/Sema/SemaAccess.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 2c11521ddf211..78ab1cdb6cc60 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -313,7 +313,7 @@ static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
EnterExpressionEvaluationContext Unevaluated(
S, Sema::ExpressionEvaluationContext::Unevaluated);
- TemplateDeductionInfo Info(Loc);
+ TemplateDeductionInfo Info(FailedTSC ? FailedTSC->getLocation() : Loc);
Sema::SFINAETrap Trap(S, Info);
LocalInstantiationScope InstantiationScope(S);
SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
>From 08209a79689e176346dfee9def64c8b601500d0d Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 14 May 2026 23:35:35 +0300
Subject: [PATCH 26/38] preserve TemplateName in FriendTemplateDecl
---
clang/include/clang/AST/DeclTemplate.h | 18 +++++++--
clang/include/clang/AST/RecursiveASTVisitor.h | 13 ++++--
clang/lib/AST/ASTImporter.cpp | 40 +++++++++++++------
clang/lib/AST/ASTStructuralEquivalence.cpp | 27 ++++++++-----
clang/lib/AST/DeclTemplate.cpp | 16 ++++++++
clang/lib/AST/ODRHash.cpp | 12 ++++++
clang/lib/Sema/SemaAccess.cpp | 30 +++++++++++++-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 35 ++++++++++++----
clang/lib/Serialization/ASTReaderDecl.cpp | 13 ++++--
clang/lib/Serialization/ASTWriterDecl.cpp | 17 +++++---
10 files changed, 175 insertions(+), 46 deletions(-)
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index ff962f73a8148..999f5ba2f4c1c 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -2479,13 +2479,15 @@ class FriendTemplateDecl final
private:
unsigned NumTPLists : 31;
+ TemplateName Template;
FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, FriendUnion Friend,
SourceLocation FriendLoc, SourceLocation EllipsisLoc,
- ArrayRef<TemplateParameterList *> FriendTypeTPLists)
+ ArrayRef<TemplateParameterList *> FriendTypeTPLists,
+ TemplateName Template = {})
: FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
EllipsisLoc),
- NumTPLists(FriendTypeTPLists.size()) {
+ NumTPLists(FriendTypeTPLists.size()), Template(Template) {
llvm::copy(FriendTypeTPLists, getTrailingObjects());
}
@@ -2504,6 +2506,12 @@ class FriendTemplateDecl final
ArrayRef<TemplateParameterList *> FriendTypeTPLists = {},
SourceLocation EllipsisLoc = {});
+ static FriendTemplateDecl *
+ Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc,
+ TemplateName Template, SourceLocation FriendLoc,
+ ArrayRef<TemplateParameterList *> FriendTypeTPLists = {},
+ SourceLocation EllipsisLoc = {});
+
static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID,
unsigned FriendTypeNumTPLists);
@@ -2520,9 +2528,13 @@ class FriendTemplateDecl final
/// a member function of a templated type), return that type;
/// otherwise return null.
NamedDecl *getFriendDecl() const {
- return Friend.dyn_cast<NamedDecl*>();
+ if (TemplateDecl *TD = Template.getAsTemplateDecl())
+ return TD;
+ return Friend.dyn_cast<NamedDecl *>();
}
+ TemplateName getFriendTemplateName() const { return Template; }
+
ArrayRef<TemplateParameterList *>
getFriendTypeTemplateParameterLists() const {
return ArrayRef(getTrailingObjects(), NumTPLists);
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 75c985f9d173b..dd3d0ec5841c6 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1733,10 +1733,15 @@ DEF_TRAVERSE_DECL(FriendDecl, {
})
DEF_TRAVERSE_DECL(FriendTemplateDecl, {
- if (D->getFriendType())
- TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
- else
- TRY_TO(TraverseDecl(D->getFriendDecl()));
+ TemplateName Template = D->getFriendTemplateName();
+ if (Template.isNull()) {
+ if (D->getFriendType())
+ TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc()));
+ else
+ TRY_TO(TraverseDecl(D->getFriendDecl()));
+ } else {
+ TRY_TO(TraverseTemplateName(Template));
+ }
for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists()) {
for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end();
ITPL != ETPL; ++ITPL) {
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 5533cacac129c..045e4b088c4b6 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4687,16 +4687,25 @@ ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
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;
+ TemplateName ToTemplate;
+ TemplateName FromTemplate = D->getFriendTemplateName();
+ if (FromTemplate.isNull()) {
+ 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();
+ }
} else {
- if (auto TSIOrErr = import(D->getFriendType()))
- ToFU = *TSIOrErr;
+ if (auto TemplateOrErr = import(FromTemplate))
+ ToTemplate = *TemplateOrErr;
else
- return TSIOrErr.takeError();
+ return TemplateOrErr.takeError();
}
ArrayRef<TemplateParameterList *> TPLs =
@@ -4722,10 +4731,17 @@ ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
return EllipsisLocOrErr.takeError();
FriendTemplateDecl *FTD;
- if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
- *LocationOrErr, ToFU, *FriendLocOrErr, ToParams,
- *EllipsisLocOrErr))
- return FTD;
+ if (ToTemplate.isNull()) {
+ if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+ *LocationOrErr, ToFU, *FriendLocOrErr, ToParams,
+ *EllipsisLocOrErr))
+ return FTD;
+ } else {
+ if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC,
+ *LocationOrErr, ToTemplate, *FriendLocOrErr,
+ ToParams, *EllipsisLocOrErr))
+ return FTD;
+ }
FTD->setAccess(D->getAccess());
FTD->setLexicalDeclContext(LexicalDC);
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index aa09ea7efcf92..82baed3e31cc0 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2445,19 +2445,28 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
if (!Context.IsEquivalent(TPL1[I], TPL2[I]))
return false;
- if ((FTD1->getFriendType() && FTD2->getFriendDecl()) ||
- (FTD1->getFriendDecl() && FTD2->getFriendType()))
+ TemplateName TN1 = FTD1->getFriendTemplateName();
+ TemplateName TN2 = FTD2->getFriendTemplateName();
+ if (TN1.isNull() != TN2.isNull())
return false;
- if (FTD1->getFriendDecl() && FTD2->getFriendDecl())
- return IsStructurallyEquivalent(Context, FTD1->getFriendDecl(),
- FTD2->getFriendDecl());
+ if (TN1.isNull()) {
+ if ((FTD1->getFriendType() && FTD2->getFriendDecl()) ||
+ (FTD1->getFriendDecl() && FTD2->getFriendType()))
+ return false;
- if (FTD1->getFriendType() && FTD2->getFriendType())
- return IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
- FTD2->getFriendType()->getType());
+ if (FTD1->getFriendDecl() && FTD2->getFriendDecl())
+ return IsStructurallyEquivalent(Context, FTD1->getFriendDecl(),
+ FTD2->getFriendDecl());
- return false;
+ if (FTD1->getFriendType() && FTD2->getFriendType())
+ return IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(),
+ FTD2->getFriendType()->getType());
+
+ return false;
+ }
+
+ return IsStructurallyEquivalent(Context, TN1, TN2);
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 5bc2d6c6dbd6c..8aa1479520dec 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1250,6 +1250,22 @@ FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
return FTD;
}
+FriendTemplateDecl *
+FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC,
+ SourceLocation Loc, TemplateName Template,
+ 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, FriendUnion(), FriendLoc, EllipsisLoc,
+ FriendTypeTPLists, Template);
+ cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD);
+ return FTD;
+}
+
FriendTemplateDecl *
FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
unsigned NumFriendTypeTPLists) {
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..a22ab006640f3 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -164,7 +164,9 @@ void ODRHash::AddTemplateName(TemplateName Name) {
case TemplateName::AssumedTemplate:
case TemplateName::SubstTemplateTemplateParm:
case TemplateName::SubstTemplateTemplateParmPack:
+ break;
case TemplateName::UsingTemplate:
+ AddDecl(Name.getAsUsingShadowDecl()->getTargetDecl());
break;
case TemplateName::DeducedTemplate:
llvm_unreachable("Unexpected DeducedTemplate");
@@ -473,6 +475,16 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
Hash.AddBoolean(D->isPackExpansion());
}
+ void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ TemplateName TN = D->getFriendTemplateName();
+ Hash.AddBoolean(TN.isNull());
+ if (TN.isNull()) {
+ VisitFriendDecl(D);
+ } else {
+ Hash.AddTemplateName(TN);
+ }
+ }
+
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
// Only care about default arguments as part of the definition.
const bool hasDefaultArgument =
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 78ab1cdb6cc60..3e4af456c8f67 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -735,10 +735,11 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FriendTD,
ClassTemplateDecl *FriendCTD,
+ NestedNameSpecifier Qualifier,
TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
- const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
- S, FriendCTD->getTemplatedDecl()->getQualifier());
+ const auto *FriendTST =
+ GetCanonicalQualifierTemplateSpecializationType(S, Qualifier);
if (!FriendTST)
return MatchesFriend(S, EC, FriendCTD);
@@ -778,6 +779,26 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
return OnFailure;
}
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ TemplateName Template,
+ ClassTemplateDecl *FriendCTD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ NestedNameSpecifier Qualifier = Template.getQualifier();
+ if (Template.getAsUsingShadowDecl())
+ Qualifier = FriendCTD->getTemplatedDecl()->getQualifier();
+ return MatchesFriend(S, EC, FriendTD, FriendCTD, Qualifier, FailedTSC);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ ClassTemplateDecl *FriendCTD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ return MatchesFriend(S, EC, FriendTD, FriendCTD,
+ FriendCTD->getTemplatedDecl()->getQualifier(),
+ FailedTSC);
+}
+
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FriendTD,
FunctionTemplateDecl *FriendFTD,
@@ -867,6 +888,11 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FriendTemplateDecl *FTD, NamedDecl *ND,
TemplateSpecCandidateSet *FailedTSC) {
+ TemplateName Template = FTD->getFriendTemplateName();
+ if (auto *CTD =
+ dyn_cast_if_present<ClassTemplateDecl>(Template.getAsTemplateDecl()))
+ return MatchesFriend(S, EC, FTD, Template, CTD, FailedTSC);
+
if (auto *CTD = dyn_cast<ClassTemplateDecl>(ND))
return MatchesFriend(S, EC, FTD, CTD, FailedTSC);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 32de665297205..314d4fd951329 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4735,7 +4735,7 @@ Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
if (SubstTemplateParameterLists(TPLists, TPL))
return nullptr;
- ClassTemplateDecl *CTD = nullptr;
+ TemplateName Template;
if (auto DNT = FT->getTypeLoc().getAs<DependentNameTypeLoc>()) {
NestedNameSpecifierLoc QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(
DNT.getQualifierLoc(), TemplateArgs);
@@ -4745,23 +4745,42 @@ Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
DeclContext *DC =
SemaRef.computeDeclContext(SS, /*EnteringContext=*/true);
+ if (DC && !DC->isDependentContext() &&
+ SemaRef.RequireCompleteDeclContext(SS, DC))
+ DC = nullptr;
if (DC) {
LookupResult Result(SemaRef, DNT.getTypePtr()->getIdentifier(),
DNT.getNameLoc(), Sema::LookupOrdinaryName,
SemaRef.forRedeclarationInCurContext());
SemaRef.LookupQualifiedName(Result, DC);
- CTD = Result.getAsSingle<ClassTemplateDecl>();
+ if (Result.getResultKind() == LookupResultKind::Found) {
+ NamedDecl *FoundD = *Result.begin();
+ UsingShadowDecl *FoundUsingShadow =
+ dyn_cast<UsingShadowDecl>(FoundD);
+ NamedDecl *Underlying =
+ FoundUsingShadow ? FoundUsingShadow->getTargetDecl() : FoundD;
+ if (auto *CTD = dyn_cast<ClassTemplateDecl>(Underlying)) {
+ Template = FoundUsingShadow ? TemplateName(FoundUsingShadow)
+ : TemplateName(CTD);
+ Template = SemaRef.Context.getQualifiedTemplateName(
+ QualifierLoc.getNestedNameSpecifier(),
+ /*TemplateKeyword=*/false, Template);
+ }
+ }
}
}
}
- if (CTD) {
- FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
- CTD, D->getFriendLoc(), TPL);
- } else if (TypeSourceInfo *InstTy = SemaRef.SubstType(
- FT, TemplateArgs, D->getLocation(), DeclarationName())) {
- FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ if (Template.isNull()) {
+ if (TypeSourceInfo *InstTy = SemaRef.SubstType(
+ FT, TemplateArgs, D->getLocation(), DeclarationName())) {
+ FTD =
+ FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
InstTy, D->getFriendLoc(), TPL);
+ }
+ } else {
+ FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ Template, D->getFriendLoc(), TPL);
}
} else {
SmallVector<TemplateParameterList *, 1> TPL;
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index cbcd92c9cc810..d10d67708dca1 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2407,10 +2407,17 @@ void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
VisitDecl(D);
for (unsigned i = 0; i != D->NumTPLists; ++i)
D->getTrailingObjects()[i] = Record.readTemplateParameterList();
- if (Record.readInt()) // HasFriendDecl
- D->Friend = readDeclAs<NamedDecl>();
- else
+ switch (Record.readInt()) {
+ case 0:
D->Friend = readTypeSourceInfo();
+ break;
+ case 1:
+ D->Friend = readDeclAs<NamedDecl>();
+ break;
+ case 2:
+ D->Template = Record.readTemplateName();
+ break;
+ }
D->NextFriend = readDeclID().getRawValue();
D->FriendLoc = readSourceLocation();
D->EllipsisLoc = readSourceLocation();
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 6e96fc6021696..8201af47d504f 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1842,11 +1842,18 @@ void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
VisitDecl(D);
for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
Record.AddTemplateParameterList(TPL);
- Record.push_back(D->getFriendDecl() != nullptr);
- if (D->getFriendDecl())
- Record.AddDeclRef(D->getFriendDecl());
- else
- Record.AddTypeSourceInfo(D->getFriendType());
+ if (D->Template.isNull()) {
+ if (D->getFriendDecl()) {
+ Record.push_back(1);
+ Record.AddDeclRef(D->getFriendDecl());
+ } else {
+ Record.push_back(0);
+ Record.AddTypeSourceInfo(D->getFriendType());
+ }
+ } else {
+ Record.push_back(2);
+ Record.AddTemplateName(D->Template);
+ }
Record.AddDeclRef(D->getNextFriend());
Record.AddSourceLocation(D->FriendLoc);
Record.AddSourceLocation(D->EllipsisLoc);
>From bf05d27e16ac83824af6d30ff7fd95af827cd39b Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 14 May 2026 23:38:54 +0300
Subject: [PATCH 27/38] update args comments
---
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 ++-
clang/lib/Serialization/ASTReaderDecl.cpp | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 314d4fd951329..643d3dc88a0d1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5264,7 +5264,8 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
}
ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
- OldParam, TemplateArgs, /*indexAdjustment=*/0, std::nullopt,
+ OldParam, TemplateArgs, /*indexAdjustment=*/0,
+ /*NumExpansions=*/std::nullopt,
/*ExpectParameterPack=*/false, EvaluateConstraints);
if (!Parm)
return nullptr;
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index d10d67708dca1..293c3557f1a5d 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -4077,7 +4077,8 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
D = FriendDecl::CreateDeserialized(Context, ID);
break;
case DECL_FRIEND_TEMPLATE:
- D = FriendTemplateDecl::CreateDeserialized(Context, ID, Record.readInt());
+ D = FriendTemplateDecl::CreateDeserialized(Context, ID,
+ /*NumTPLists=*/Record.readInt());
break;
case DECL_CLASS_TEMPLATE:
D = ClassTemplateDecl::CreateDeserialized(Context, ID);
>From 6078de60d6d7efe9db0283b6260d5429678b6820 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 14 May 2026 23:43:12 +0300
Subject: [PATCH 28/38] add local instantiation scope
---
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 643d3dc88a0d1..0f811a7db0ccf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4940,6 +4940,7 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
bool TemplateDeclInstantiator::SubstTemplateParameterLists(
ArrayRef<TemplateParameterList *> TPL,
SmallVectorImpl<TemplateParameterList *> &InstTPL) {
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
for (TemplateParameterList *L : TPL) {
TemplateParameterList *InstParams = SubstTemplateParams(L);
if (!InstParams)
>From eb4f5290934ddb22714ef0f73e84dd331598f949 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 14 May 2026 23:48:26 +0300
Subject: [PATCH 29/38] refactor CheckDependentFriend
---
clang/lib/Sema/SemaDeclCXX.cpp | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 30f8f12016f6f..a3a6de6e872c2 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18062,30 +18062,26 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
TemplateParameterList *FPL) {
- if (!NNS || !FPL || FPL->size() == 0)
+ if (!NNS.isDependent() || !FPL || FPL->size() == 0)
return false;
- if (NNS.isDependent()) {
- if (NNS.getKind() == NestedNameSpecifier::Kind::Type) {
- QualType T(NNS.getCanonical().getAsType(), 0);
+ if (NNS.getKind() == NestedNameSpecifier::Kind::Type) {
+ QualType T(NNS.getCanonical().getAsType(), 0);
- if (const auto *PIT = dyn_cast<PackIndexingType>(T))
- T = PIT->getPattern();
+ if (const auto *PIT = dyn_cast<PackIndexingType>(T))
+ T = PIT->getPattern();
- if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
- if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()))
- return false;
- }
-
- if (isa<InjectedClassNameType>(T))
+ if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+ if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()))
return false;
}
- Diag(Loc, diag::err_dependent_friend_not_member);
- return true;
+ if (isa<InjectedClassNameType>(T))
+ return false;
}
- return false;
+ Diag(Loc, diag::err_dependent_friend_not_member);
+ return true;
}
DeclResult Sema::ActOnTemplatedFriendTag(
>From 2c7a2ff0d17482416ded63a19ffa5bca88abad22 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Thu, 14 May 2026 23:57:55 +0300
Subject: [PATCH 30/38] fix dependent friend check for dependent template names
---
clang/lib/Sema/SemaDeclCXX.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a3a6de6e872c2..e4232f9221a1c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18072,7 +18072,8 @@ bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
T = PIT->getPattern();
if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
- if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()))
+ if (isa_and_nonnull<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
return false;
}
>From 68adfc1af674289d5960319fbd24b6d64226fe79 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 15 May 2026 00:16:14 +0300
Subject: [PATCH 31/38] refine friend template qualifier handling
---
clang/lib/Sema/SemaAccess.cpp | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 3e4af456c8f67..6a39e7416f2d1 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -360,8 +360,7 @@ GetCanonicalFunctionProto(Sema &S, const FunctionDecl *FD) {
}
static const TemplateSpecializationType *
-GetCanonicalQualifierTemplateSpecializationType(Sema &S,
- NestedNameSpecifier NNS) {
+GetQualifierClassTemplateSpecializationType(Sema &S, NestedNameSpecifier NNS) {
if (!NNS)
return nullptr;
@@ -372,7 +371,15 @@ GetCanonicalQualifierTemplateSpecializationType(Sema &S,
if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(S.Context);
- return Ty->getAs<TemplateSpecializationType>();
+ const auto *TST = Ty->getAsNonAliasTemplateSpecializationType();
+ if (!TST)
+ return nullptr;
+
+ if (!isa_and_nonnull<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
+ return nullptr;
+
+ return TST;
}
static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
@@ -739,7 +746,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
const auto *FriendTST =
- GetCanonicalQualifierTemplateSpecializationType(S, Qualifier);
+ GetQualifierClassTemplateSpecializationType(S, Qualifier);
if (!FriendTST)
return MatchesFriend(S, EC, FriendCTD);
@@ -804,7 +811,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FunctionTemplateDecl *FriendFTD,
TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
- const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
+ const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
S, FriendFTD->getTemplatedDecl()->getQualifier());
if (!FriendTST)
return OnFailure;
@@ -844,8 +851,8 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
FunctionDecl *FriendFD,
TemplateSpecCandidateSet *FailedTSC) {
AccessResult OnFailure = AR_inaccessible;
- const auto *FriendTST = GetCanonicalQualifierTemplateSpecializationType(
- S, FriendFD->getQualifier());
+ const auto *FriendTST =
+ GetQualifierClassTemplateSpecializationType(S, FriendFD->getQualifier());
if (!FriendTST)
return OnFailure;
@@ -921,7 +928,7 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!NNS)
return OnFailure;
- const auto *TST = GetCanonicalQualifierTemplateSpecializationType(S, NNS);
+ const auto *TST = GetQualifierClassTemplateSpecializationType(S, NNS);
if (!TST)
return OnFailure;
>From 94f24a570262d5c9e8b031b943794ff487813a92 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 15 May 2026 00:49:53 +0300
Subject: [PATCH 32/38] improve diagnostics
---
.../clang/Basic/DiagnosticSemaKinds.td | 5 ++++
clang/include/clang/Sema/TemplateDeduction.h | 17 ++++++++++---
clang/lib/Sema/SemaAccess.cpp | 4 +++-
clang/lib/Sema/SemaOverload.cpp | 24 ++++++++++++-------
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 2 +-
clang/test/SemaTemplate/friend-template.cpp | 2 +-
6 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f5c93412abf76..a7a875b08588a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5264,11 +5264,16 @@ def note_ovl_candidate_deduced_mismatch : Note<
"adjusted type of %select{|element of }4argument}1,2%3">;
def note_ovl_candidate_non_deduced_mismatch : Note<
"candidate template ignored: could not match %diff{$ against $|types}0,1">;
+def note_friend_template_non_deduced_mismatch : Note<
+ "candidate friend template ignored: could not match "
+ "%diff{$ against $|types}0,1">;
// This note is needed because the above note would sometimes print two
// different types with the same name. Remove this note when the above note
// can handle that case properly.
def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
"candidate template ignored: could not match %q0 against %q1">;
+def note_friend_template_non_deduced_mismatch_qualified : Note<
+ "candidate friend template ignored: could not match %q0 against %q1">;
// Note that we don't treat templates differently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 39c909d73f565..dd9fe46c9bf8a 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -311,6 +311,11 @@ struct DeductionFailureInfo {
}
};
+enum class TemplateSpecCandidateSetKind {
+ Normal,
+ FriendTemplate,
+};
+
/// TemplateSpecCandidate - This is a generalization of OverloadCandidate
/// which keeps track of template argument deduction failure info, when
/// handling explicit specializations (and instantiations) of templates
@@ -337,7 +342,8 @@ struct TemplateSpecCandidate {
}
/// Diagnose a template argument deduction failure.
- void NoteDeductionFailure(Sema &S, bool ForTakingAddress);
+ void NoteDeductionFailure(Sema &S, bool ForTakingAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind);
};
/// TemplateSpecCandidateSet - A set of generalized overload candidates,
@@ -353,11 +359,16 @@ class TemplateSpecCandidateSet {
// attribute on parameters.
bool ForTakingAddress;
+ TemplateSpecCandidateSetKind CandidateSetKind;
+
void destroyCandidates();
public:
- TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false)
- : Loc(Loc), ForTakingAddress(ForTakingAddress) {}
+ TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false,
+ TemplateSpecCandidateSetKind CandidateSetKind =
+ TemplateSpecCandidateSetKind::Normal)
+ : Loc(Loc), ForTakingAddress(ForTakingAddress),
+ CandidateSetKind(CandidateSetKind) {}
TemplateSpecCandidateSet(const TemplateSpecCandidateSet &) = delete;
TemplateSpecCandidateSet &
operator=(const TemplateSpecCandidateSet &) = delete;
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 6a39e7416f2d1..ad6aac1c956ed 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -1895,7 +1895,9 @@ static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
if (Entity.isQuiet())
return CheckEffectiveAccess(S, EC, Loc, Entity, /*FailedTSC=*/nullptr);
- TemplateSpecCandidateSet FailedTSC(Loc);
+ TemplateSpecCandidateSet FailedTSC(
+ Loc, /*ForTakingAddress=*/false,
+ TemplateSpecCandidateSetKind::FriendTemplate);
return CheckEffectiveAccess(S, EC, Loc, Entity, &FailedTSC);
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 96c4ce489fe04..097807b0e6602 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12371,8 +12371,9 @@ static TemplateDecl *getDescribedTemplate(Decl *Templated) {
/// Diagnose a failed template-argument deduction.
static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
DeductionFailureInfo &DeductionFailure,
- unsigned NumArgs,
- bool TakingCandidateAddress) {
+ unsigned NumArgs, bool TakingCandidateAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind =
+ TemplateSpecCandidateSetKind::Normal) {
TemplateParameter Param = DeductionFailure.getTemplateParameter();
NamedDecl *ParamD;
(ParamD = Param.dyn_cast<TemplateTypeParmDecl*>()) ||
@@ -12620,7 +12621,10 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
// name for types, not decls.
// Ideally, this should folded into the diagnostic printer.
S.Diag(Templated->getLocation(),
- diag::note_ovl_candidate_non_deduced_mismatch_qualified)
+ CandidateSetKind ==
+ TemplateSpecCandidateSetKind::FriendTemplate
+ ? diag::note_friend_template_non_deduced_mismatch_qualified
+ : diag::note_ovl_candidate_non_deduced_mismatch_qualified)
<< FirstTN.getAsTemplateDecl() << SecondTN.getAsTemplateDecl();
return;
}
@@ -12636,7 +12640,9 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
// diagnostic that mentions 'auto' and lambda in addition to
// (or instead of?) the canonical template type parameters.
S.Diag(Templated->getLocation(),
- diag::note_ovl_candidate_non_deduced_mismatch)
+ CandidateSetKind == TemplateSpecCandidateSetKind::FriendTemplate
+ ? diag::note_friend_template_non_deduced_mismatch
+ : diag::note_ovl_candidate_non_deduced_mismatch)
<< FirstTA << SecondTA;
return;
}
@@ -13565,10 +13571,12 @@ struct CompareTemplateSpecCandidatesForDisplay {
/// Diagnose a template argument deduction failure.
/// We are treating these failures as overload failures due to bad
/// deductions.
-void TemplateSpecCandidate::NoteDeductionFailure(Sema &S,
- bool ForTakingAddress) {
+void TemplateSpecCandidate::NoteDeductionFailure(
+ Sema &S, bool ForTakingAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind) {
DiagnoseBadDeduction(S, FoundDecl, Specialization, // pattern
- DeductionFailure, /*NumArgs=*/0, ForTakingAddress);
+ DeductionFailure, /*NumArgs=*/0, ForTakingAddress,
+ CandidateSetKind);
}
void TemplateSpecCandidateSet::destroyCandidates() {
@@ -13620,7 +13628,7 @@ void TemplateSpecCandidateSet::NoteCandidates(Sema &S, SourceLocation Loc) {
assert(Cand->Specialization &&
"Non-matching built-in candidates are not added to Cands.");
- Cand->NoteDeductionFailure(S, ForTakingAddress);
+ Cand->NoteDeductionFailure(S, ForTakingAddress, CandidateSetKind);
}
if (I != E)
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index 46f8139d4451f..684d03ab82ced 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -167,7 +167,7 @@ namespace test8 {
c.n = 0;
// expected-error at -1 {{'n' is a private member of 'test8::C'}}
// expected-note@#test8-C-n {{implicitly declared private here}}
- // expected-note@#test8-A {{candidate template ignored: could not match 'T *' against 'int'}}
+ // expected-note@#test8-A {{candidate friend template ignored: could not match 'T *' against 'int'}}
return 0;
}
diff --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 3555bb2eb8cb3..4a8f23a56dd22 100644
--- a/clang/test/SemaTemplate/friend-template.cpp
+++ b/clang/test/SemaTemplate/friend-template.cpp
@@ -354,7 +354,7 @@ template <class U> struct A<T>::B {
A<int>::f();
// expected-error at -1 {{'f' is a private member of 'GH104057::A<int>'}}
// expected-note@#GH104057-A-f {{declared private here}}
- // expected-note@#GH104057-A {{candidate template ignored: could not match 'U *' against 'double'}}
+ // expected-note@#GH104057-A {{candidate friend template ignored: could not match 'U *' against 'double'}}
}
};
>From df7ec0f4780f6286a9baa4fdc947a2e0eda69ecf Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 15 May 2026 01:00:46 +0300
Subject: [PATCH 33/38] add additional tests
---
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
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 684d03ab82ced..7baebae666f11 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -237,3 +237,55 @@ namespace test10 {
}
};
}
+
+namespace test11 {
+ template <class> struct C;
+ template <class T> struct A {
+ template <class> struct B;
+ };
+ template <class T> struct D : A<T> {
+ using A<T>::B;
+ };
+
+ template <class T> struct C {
+ int n;
+ template <class U> friend struct D<T>::B;
+ };
+
+ template <> template <class U> struct A<int>::B {
+ static int f(C<int> &c) {
+ c.n = 0;
+ return 0;
+ }
+ };
+
+ int x = A<int>::B<void>::f(*new C<int>);
+}
+
+namespace test12 {
+ template <class T> struct A {
+ template <T> struct B {
+ static int f();
+ };
+ };
+
+ template <class T> struct C {
+ int n;
+ template <class U> template <U V> friend struct A<U>::B;
+ };
+
+ template <class T> template <T V> int A<T>::B<V>::f() {
+ C<T> c;
+ c.n = 0;
+ return 0;
+ }
+
+ int x = A<int>::B<0>::f();
+}
+
+namespace test13 {
+template <typename T> struct S {
+ template <typename> friend class T::template X<int>::Y;
+ // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
+};
+}
>From a93ba613195a6b12a5f0dade538119d9f6d91b97 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 15 May 2026 09:10:52 +0300
Subject: [PATCH 34/38] cleanup
---
clang/lib/Sema/SemaAccess.cpp | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index ad6aac1c956ed..02414dae5938d 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -372,14 +372,11 @@ GetQualifierClassTemplateSpecializationType(Sema &S, NestedNameSpecifier NNS) {
Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(S.Context);
const auto *TST = Ty->getAsNonAliasTemplateSpecializationType();
- if (!TST)
- return nullptr;
+ if (TST && isa_and_nonnull<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
+ return TST;
- if (!isa_and_nonnull<ClassTemplateDecl>(
- TST->getTemplateName().getAsTemplateDecl()))
- return nullptr;
-
- return TST;
+ return nullptr;
}
static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
>From 40fba25564766fb47dd5088a4529988a0375b036 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 15 May 2026 09:34:46 +0300
Subject: [PATCH 35/38] simplify friend template lookup handling
---
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 21 +++++++------------
1 file changed, 8 insertions(+), 13 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 04a7804182240..714711342d76f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4755,19 +4755,14 @@ Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
DNT.getNameLoc(), Sema::LookupOrdinaryName,
SemaRef.forRedeclarationInCurContext());
SemaRef.LookupQualifiedName(Result, DC);
- if (Result.getResultKind() == LookupResultKind::Found) {
- NamedDecl *FoundD = *Result.begin();
- UsingShadowDecl *FoundUsingShadow =
- dyn_cast<UsingShadowDecl>(FoundD);
- NamedDecl *Underlying =
- FoundUsingShadow ? FoundUsingShadow->getTargetDecl() : FoundD;
- if (auto *CTD = dyn_cast<ClassTemplateDecl>(Underlying)) {
- Template = FoundUsingShadow ? TemplateName(FoundUsingShadow)
- : TemplateName(CTD);
- Template = SemaRef.Context.getQualifiedTemplateName(
- QualifierLoc.getNestedNameSpecifier(),
- /*TemplateKeyword=*/false, Template);
- }
+ if (auto *CTD = Result.getAsSingle<ClassTemplateDecl>()) {
+ auto *FoundUsingShadow =
+ dyn_cast<UsingShadowDecl>(Result.getRepresentativeDecl());
+ Template = FoundUsingShadow ? TemplateName(FoundUsingShadow)
+ : TemplateName(CTD);
+ Template = SemaRef.Context.getQualifiedTemplateName(
+ QualifierLoc.getNestedNameSpecifier(),
+ /*TemplateKeyword=*/false, Template);
}
}
}
>From 644fc60cca0a467415bb983c0e3d9f332279db45 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 15 May 2026 10:56:41 +0300
Subject: [PATCH 36/38] handle qualified friend template access
---
clang/lib/Sema/SemaAccess.cpp | 47 +++++++++++++++++--
.../CXX/temp/temp.decls/temp.friend/p5.cpp | 22 +++++++--
2 files changed, 60 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 02414dae5938d..a261c84aa6f0f 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -426,6 +426,41 @@ static bool MatchesFriendContext(Sema &S, DeclContext *DC,
ContextArgs, Loc, FailedTSC);
}
+static bool MatchesFriendContext(Sema &S, DeclContext *DC,
+ ClassTemplateDecl *FriendCTD,
+ DeclContext *FriendDC,
+ ArrayRef<TemplateArgument> FriendArgs,
+ TemplateParameterList *FriendTPL,
+ SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC) {
+ auto GetClassTemplateContext =
+ [](const DeclContext *DC,
+ ClassTemplateDecl *Template) -> const CXXRecordDecl * {
+ const auto *RD = dyn_cast<CXXRecordDecl>(DC);
+ if (RD) {
+ ClassTemplateDecl *CTD = RD->getDescribedClassTemplate();
+ if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ CTD = CTSD->getSpecializedTemplate();
+
+ if (CTD && CTD->getCanonicalDecl() == Template->getCanonicalDecl())
+ return RD;
+ }
+ return nullptr;
+ };
+
+ if (const auto *FriendRecord = dyn_cast<CXXRecordDecl>(FriendDC)) {
+ const CXXRecordDecl *FriendContext =
+ GetClassTemplateContext(FriendRecord->getDeclContext(), FriendCTD);
+ const CXXRecordDecl *Context = GetClassTemplateContext(DC, FriendCTD);
+ if (FriendContext && Context &&
+ FriendContext->getCanonicalDecl() == Context->getCanonicalDecl())
+ return true;
+ }
+
+ return MatchesFriendContext(S, DC, FriendCTD, FriendArgs, FriendTPL, Loc,
+ FailedTSC);
+}
+
/// Checks whether one class might instantiate to the other.
static bool MightInstantiateTo(const CXXRecordDecl *From,
const CXXRecordDecl *To) {
@@ -772,11 +807,13 @@ static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
if (!ContextCTD)
continue;
- if (MightInstantiateTo(ContextCTD->getTemplatedDecl(),
- FriendCTD->getTemplatedDecl()) &&
- MatchesFriendContext(S, RD->getDeclContext(), FriendContextCTD,
- FriendArgs, FriendTPL, FriendTD->getLocation(),
- FailedTSC))
+ if (!MightInstantiateTo(ContextCTD->getTemplatedDecl(),
+ FriendCTD->getTemplatedDecl()))
+ continue;
+
+ if (MatchesFriendContext(S, RD->getDeclContext(), FriendContextCTD,
+ FriendTD->getDeclContext(), FriendArgs, FriendTPL,
+ FriendTD->getLocation(), FailedTSC))
return AR_accessible;
}
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 7baebae666f11..ec766b909d0f7 100644
--- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
@@ -284,8 +284,22 @@ namespace test12 {
}
namespace test13 {
-template <typename T> struct S {
- template <typename> friend class T::template X<int>::Y;
- // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
-};
+ template <typename T> struct S {
+ template <typename> friend class T::template X<int>::Y;
+ // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
+ };
+}
+
+namespace test14 {
+ template <class T> struct A {
+ template <bool V> struct B {
+ static int f(B<false> &x) { return x.n; }
+
+ private:
+ int n;
+ template <bool> friend struct A<T>::B;
+ };
+ };
+
+ int x = A<int>::B<true>::f(*new A<int>::B<false>);
}
>From 91f7995fb753ea0d393fe77d1fb2d0e70a55e104 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 26 May 2026 11:56:56 +0300
Subject: [PATCH 37/38] reuse structural equivalence for templated friends
---
clang/lib/AST/ASTImporter.cpp | 39 ++--------------------
clang/lib/AST/ASTStructuralEquivalence.cpp | 15 +++++----
2 files changed, 10 insertions(+), 44 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index ba2ccb4ce3b77..01e3f7ece5baf 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4531,13 +4531,9 @@ struct FriendCountAndPosition {
static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
FriendDecl *FD2) {
- if ((!FD1->getFriendType()) != (!FD2->getFriendType()))
+ if (FD1->getKind() != FD2->getKind())
return false;
- if (const TypeSourceInfo *TSI = FD1->getFriendType())
- return Importer.IsStructurallyEquivalent(
- TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false);
-
ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
StructuralEquivalenceContext Ctx(
Importer.getToContext().getLangOpts(), FD1->getASTContext(),
@@ -4547,36 +4543,6 @@ static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
return Ctx.IsEquivalent(FD1, FD2);
}
-static bool IsEquivalentFriend(ASTImporter &Importer, FriendTemplateDecl *FTD1,
- FriendTemplateDecl *FTD2) {
- ArrayRef<TemplateParameterList *> TPL1 =
- FTD1->getFriendTypeTemplateParameterLists();
- ArrayRef<TemplateParameterList *> TPL2 =
- FTD2->getFriendTypeTemplateParameterLists();
- if (TPL1.size() != TPL2.size())
- 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 = TPL1.size(); I != N; ++I)
- if (!Ctx.IsEquivalent(TPL1[I], TPL2[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;
@@ -4607,11 +4573,10 @@ 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()) {
+ for (FriendDecl *ImportedFriend : RD->friends())
if (ImportedFriend->getKind() == Decl::Friend &&
IsEquivalentFriend(Importer, D, ImportedFriend))
ImportedEquivalentFriends.push_back(ImportedFriend);
- }
FriendCountAndPosition CountAndPosition =
getFriendCountAndPosition(Importer, D);
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 82baed3e31cc0..db0ed0e3662a6 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2442,7 +2442,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
for (unsigned I = 0, N = TPL1.size(); I != N; ++I)
- if (!Context.IsEquivalent(TPL1[I], TPL2[I]))
+ if (!IsStructurallyEquivalent(Context, TPL1[I], TPL2[I]))
return false;
TemplateName TN1 = FTD1->getFriendTemplateName();
@@ -2810,18 +2810,19 @@ bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
bool StructuralEquivalenceContext::IsEquivalent(TemplateParameterList *TPL1,
TemplateParameterList *TPL2) {
+ assert(DeclsToCheck.empty());
+ assert(VisitedDecls.empty());
+
if (TPL1 == TPL2)
return true;
- if (!TPL1 || !TPL2 || TPL1->size() != TPL2->size())
+ if (!TPL1 || !TPL2)
return false;
- for (unsigned I = 0, N = TPL1->size(); I != N; ++I) {
- if (!IsEquivalent(TPL1->getParam(I), TPL2->getParam(I)))
- return false;
- }
+ if (!::IsStructurallyEquivalent(*this, TPL1, TPL2))
+ return false;
- return IsEquivalent(TPL1->getRequiresClause(), TPL2->getRequiresClause());
+ return !Finish();
}
bool StructuralEquivalenceContext::CheckKindSpecificEquivalence(
>From 62144c3597a28508a42a4239902693df7427b07f Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 26 May 2026 11:57:48 +0300
Subject: [PATCH 38/38] add templated friend visitor to decl printer
---
clang/lib/AST/DeclPrinter.cpp | 19 +++++++++++++++++++
clang/lib/AST/ODRHash.cpp | 3 +++
2 files changed, 22 insertions(+)
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index b84745382a976..7980dd38211d8 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -70,6 +70,7 @@ namespace {
void VisitEmptyDecl(EmptyDecl *D);
void VisitFunctionDecl(FunctionDecl *D);
void VisitFriendDecl(FriendDecl *D);
+ void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitFieldDecl(FieldDecl *D);
void VisitVarDecl(VarDecl *D);
void VisitLabelDecl(LabelDecl *D);
@@ -907,6 +908,24 @@ void DeclPrinter::VisitFriendDecl(FriendDecl *D) {
Out << "...";
}
+void DeclPrinter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+ printTemplateParameters(TPL);
+
+ TemplateName TN = D->getFriendTemplateName();
+ if (TN.isNull()) {
+ VisitFriendDecl(D);
+ } else {
+ Out << "friend ";
+ TN.print(Out, Policy,
+ Policy.SuppressScope ? TemplateName::Qualified::None
+ : TemplateName::Qualified::AsWritten);
+
+ if (D->isPackExpansion())
+ Out << "...";
+ }
+}
+
void DeclPrinter::VisitFieldDecl(FieldDecl *D) {
prettyPrintPragmas(D);
// FIXME: add printing of pragma attributes if required.
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index a22ab006640f3..b85204083cca9 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -476,6 +476,9 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
}
void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+ Hash.AddTemplateParameterList(TPL);
+
TemplateName TN = D->getFriendTemplateName();
Hash.AddBoolean(TN.isNull());
if (TN.isNull()) {
More information about the cfe-commits
mailing list