[clang] dcf1b8f - [Clang] support friend declarations with a dependent nested-name-specifier (#191268)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 8 02:52:58 PDT 2026
Author: Oleksandr Tarasiuk
Date: 2026-07-08T12:52:47+03:00
New Revision: dcf1b8f2c00b38777f71297afc7b575cd2d9300a
URL: https://github.com/llvm/llvm-project/commit/dcf1b8f2c00b38777f71297afc7b575cd2d9300a
DIFF: https://github.com/llvm/llvm-project/commit/dcf1b8f2c00b38777f71297afc7b575cd2d9300a.diff
LOG: [Clang] support friend declarations with a dependent nested-name-specifier (#191268)
Fixes #104057
---
This patch adds support for friend declarations with a dependent NNS
Added:
clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
Modified:
clang-tools-extra/clang-doc/Serialize.cpp
clang/docs/ReleaseNotes.md
clang/include/clang/AST/ASTStructuralEquivalence.h
clang/include/clang/AST/DeclFriend.h
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Basic/DeclNodes.td
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/Template.h
clang/include/clang/Sema/TemplateDeduction.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/ASTStructuralEquivalence.cpp
clang/lib/AST/DeclFriend.cpp
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/AST/ODRHash.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaAccess.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp
clang/test/CXX/drs/cwg18xx.cpp
clang/test/CXX/drs/cwg19xx.cpp
clang/test/CXX/drs/cwg28xx.cpp
clang/test/CXX/drs/cwg6xx.cpp
clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
clang/test/Parser/cxx2c-variadic-friends.cpp
clang/test/SemaCXX/many-template-parameter-lists.cpp
clang/test/SemaTemplate/GH71595.cpp
clang/test/SemaTemplate/concepts-friends.cpp
clang/test/SemaTemplate/ctad.cpp
clang/test/SemaTemplate/friend-template.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 50118e0472075..5fa23416949c4 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -1029,9 +1029,6 @@ void Serializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
llvm::SmallVector<FriendInfo, 4> LocalFriends;
for (const FriendDecl *FD : D->friends()) {
- if (FD->isUnsupportedFriend())
- continue;
-
FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
const auto *ActualDecl = FD->getFriendDecl();
if (!ActualDecl) {
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 4d7d9f2909096..772cd64dd93f4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -243,6 +243,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- `__is_trivially_equality_comparable` no longer returns false for all enum types. (#GH132672)
- `auto` parameters are now available in all C++ language modes as an extension.
+- Clang now supports friend declarations with a dependent nested name specifier. (#GH104057)
#### C++2d Feature Support
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..c68028d22bd54 100644
--- a/clang/include/clang/AST/DeclFriend.h
+++ b/clang/include/clang/AST/DeclFriend.h
@@ -15,20 +15,13 @@
#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 +42,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 +52,28 @@ class FriendDecl final
friend class CXXRecordDecl;
friend class CXXRecordDecl::friend_iterator;
+ // Location of the '...', if present.
+ SourceLocation EllipsisLoc;
+
+ SourceLocation FriendLoc;
+
+protected:
// The declaration that's a friend of this class.
FriendUnion Friend;
- // A pointer to the next friend in the sequence.
LazyDeclPtr NextFriend;
- // Location of the 'friend' specifier.
- SourceLocation FriendLoc;
-
- // Location of the '...', if present.
- SourceLocation EllipsisLoc;
-
- /// True if this 'friend' declaration is unsupported. Eventually we
- /// will support every possible friend declaration, but for now we
- /// silently ignore some and set this flag to authorize all access.
- LLVM_PREFERRED_TYPE(bool)
- unsigned UnsupportedFriend : 1;
-
- // The number of "outer" template parameter lists in non-templatic
- // (currently unsupported) friend type declarations, such as
- // template <class T> friend class A<T>::B;
- unsigned NumTPLists : 31;
-
- FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
- SourceLocation FriendL, SourceLocation EllipsisLoc,
- ArrayRef<TemplateParameterList *> FriendTypeTPLists)
- : Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
- EllipsisLoc(EllipsisLoc), UnsupportedFriend(false),
- NumTPLists(FriendTypeTPLists.size()) {
- llvm::copy(FriendTypeTPLists, getTrailingObjects());
- }
+ FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend,
+ SourceLocation FriendL, SourceLocation EllipsisLoc = {})
+ : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FriendL),
+ Friend(Friend), NextFriend() {}
- FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
- : Decl(Decl::Friend, Empty), UnsupportedFriend(false),
- NumTPLists(NumFriendTypeTPLists) {}
+ FriendDecl(Kind K, EmptyShell Empty) : Decl(K, Empty) {}
FriendDecl *getNextFriend() {
- if (!NextFriend.isOffset())
- return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
- return getNextFriendSlowCase();
+ if (NextFriend.isOffset())
+ return getNextFriendSlowCase();
+ return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
}
FriendDecl *getNextFriendSlowCase();
@@ -109,14 +82,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,74 +96,27 @@ 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; }
- /// Determines if this friend kind is unsupported.
- bool isUnsupportedFriend() const {
- return UnsupportedFriend;
- }
- void setUnsupportedFriend(bool Unsupported) {
- UnsupportedFriend = Unsupported;
- }
+ SourceRange getSourceRange() const override LLVM_READONLY;
bool isPackExpansion() const { return EllipsisLoc.isValid(); }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
- static bool classofKind(Kind K) { return K == Decl::Friend; }
+ static bool classofKind(Kind K) {
+ return K >= firstFriend && K <= lastFriend;
+ }
};
-
/// An iterator over the friend declarations of a class.
class CXXRecordDecl::friend_iterator {
friend class CXXRecordDecl;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 4f5a4e1b7b8a6..95d5ab1b11bf2 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"
@@ -2461,45 +2462,51 @@ 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 : public Decl {
- virtual void anchor();
-
-public:
- using FriendUnion = llvm::PointerUnion<NamedDecl *,TypeSourceInfo *>;
+class FriendTemplateDecl final
+ : public FriendDecl,
+ private llvm::TrailingObjects<FriendTemplateDecl,
+ TemplateParameterList *> {
+ void anchor() override;
private:
- // The number of template parameters; always non-zero.
- unsigned NumParams = 0;
+ unsigned NumTPLists = 0;
+ TemplateName Template;
- // The parameter list.
- TemplateParameterList **Params = nullptr;
-
- // The declaration that's a friend of this class.
- FriendUnion Friend;
-
- // Location of the 'friend' specifier.
- SourceLocation FriendLoc;
-
- FriendTemplateDecl(DeclContext *DC, SourceLocation Loc,
- TemplateParameterList **Params, unsigned NumParams,
- FriendUnion Friend, SourceLocation FriendLoc)
- : Decl(Decl::FriendTemplate, DC, Loc), NumParams(NumParams),
- Params(Params), Friend(Friend), FriendLoc(FriendLoc) {}
+ FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, FriendUnion Friend,
+ SourceLocation FriendLoc, SourceLocation EllipsisLoc,
+ ArrayRef<TemplateParameterList *> FriendTypeTPLists,
+ TemplateName Template = {})
+ : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc,
+ EllipsisLoc),
+ NumTPLists(FriendTypeTPLists.size()), Template(Template) {
+ 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 *
+ 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);
- static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
+ 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
@@ -2512,21 +2519,16 @@ class FriendTemplateDecl : public Decl {
/// 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 *>();
}
- /// Retrieves the location of the 'friend' keyword.
- SourceLocation getFriendLoc() const {
- return FriendLoc;
- }
+ TemplateName getFriendTemplateName() const { return Template; }
- TemplateParameterList *getTemplateParameterList(unsigned i) const {
- assert(i <= NumParams);
- return Params[i];
- }
-
- unsigned getNumTemplateParameters() const {
- return NumParams;
+ ArrayRef<TemplateParameterList *>
+ getFriendTypeTemplateParameterLists() const {
+ return ArrayRef(getTrailingObjects(), NumTPLists);
}
// Implement isa/cast/dyncast/etc.
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index b77443f1fa0ab..4552437d3db9a 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1733,12 +1733,16 @@ DEF_TRAVERSE_DECL(FriendDecl, {
})
DEF_TRAVERSE_DECL(FriendTemplateDecl, {
- if (D->getFriendType())
- 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);
+ 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) {
TRY_TO(TraverseDecl(*ITPL));
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/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 73fb97aeeb302..16ee72c80dbc8 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1198,7 +1198,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 38d9e4046d3a5..3fa5bce9fb04f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1916,16 +1916,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">,
@@ -1935,6 +1925,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 "
@@ -5290,11 +5282,16 @@ def note_ovl_candidate_deduced_mismatch : Note<
"adjusted type of %select{|element of }4argument}1,2%3">;
def note_ovl_candidate_non_deduced_mismatch : Note<
"candidate template ignored: could not match %
diff {$ against $|types}0,1">;
+def note_friend_template_non_deduced_mismatch : Note<
+ "candidate friend template ignored: could not match "
+ "%
diff {$ against $|types}0,1">;
// This note is needed because the above note would sometimes print two
//
diff erent types with the same name. Remove this note when the above note
// can handle that case properly.
def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
"candidate template ignored: could not match %q0 against %q1">;
+def note_friend_template_non_deduced_mismatch_qualified : Note<
+ "candidate friend template ignored: could not match %q0 against %q1">;
// Note that we don't treat templates
diff erently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f7d0d493e7081..e658f57968b99 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12018,6 +12018,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,
@@ -12751,6 +12754,15 @@ 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,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ sema::TemplateDeductionInfo &Info, bool CopyDeducedArgs);
+
/// Perform template argument deduction from a function call
/// (C++ [temp.deduct.call]).
///
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index b0170c21feb1a..42febace0a671 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);
@@ -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/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 39c909d73f565..dd9fe46c9bf8a 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -311,6 +311,11 @@ struct DeductionFailureInfo {
}
};
+enum class TemplateSpecCandidateSetKind {
+ Normal,
+ FriendTemplate,
+};
+
/// TemplateSpecCandidate - This is a generalization of OverloadCandidate
/// which keeps track of template argument deduction failure info, when
/// handling explicit specializations (and instantiations) of templates
@@ -337,7 +342,8 @@ struct TemplateSpecCandidate {
}
/// Diagnose a template argument deduction failure.
- void NoteDeductionFailure(Sema &S, bool ForTakingAddress);
+ void NoteDeductionFailure(Sema &S, bool ForTakingAddress,
+ TemplateSpecCandidateSetKind CandidateSetKind);
};
/// TemplateSpecCandidateSet - A set of generalized overload candidates,
@@ -353,11 +359,16 @@ class TemplateSpecCandidateSet {
// attribute on parameters.
bool ForTakingAddress;
+ TemplateSpecCandidateSetKind CandidateSetKind;
+
void destroyCandidates();
public:
- TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false)
- : Loc(Loc), ForTakingAddress(ForTakingAddress) {}
+ TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false,
+ TemplateSpecCandidateSetKind CandidateSetKind =
+ TemplateSpecCandidateSetKind::Normal)
+ : Loc(Loc), ForTakingAddress(ForTakingAddress),
+ CandidateSetKind(CandidateSetKind) {}
TemplateSpecCandidateSet(const TemplateSpecCandidateSet &) = delete;
TemplateSpecCandidateSet &
operator=(const TemplateSpecCandidateSet &) = delete;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 279380de2f7fe..b581a70a073b5 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2109,6 +2109,13 @@ enum CtorInitializerType {
CTOR_INITIALIZER_INDIRECT_MEMBER
};
+/// Kinds of friend payloads owned by FriendTemplateDecl.
+enum FriendTemplateDeclKind {
+ FTDK_Type = 0,
+ FTDK_Decl = 1,
+ FTDK_Template = 2,
+};
+
/// Kinds of cleanup objects owned by ExprWithCleanups.
enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 567d2d07298a3..da1b6c2e908c6 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -538,6 +538,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);
@@ -4560,19 +4561,15 @@ struct FriendCountAndPosition {
static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1,
FriendDecl *FD2) {
- if ((!FD1->getFriendType()) != (!FD2->getFriendType()))
+ if (FD1->getKind() != FD2->getKind())
return false;
- if (const TypeSourceInfo *TSI = FD1->getFriendType())
- return Importer.IsStructurallyEquivalent(
- TSI->getType(), FD2->getFriendType()->getType(), /*Complain=*/false);
-
ASTImporter::NonEquivalentDeclSet NonEquivalentDecls;
StructuralEquivalenceContext Ctx(
Importer.getToContext().getLangOpts(), FD1->getASTContext(),
FD2->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default,
- /* StrictTypeSpelling = */ false, /* Complain = */ false);
+ /*StrictTypeSpelling=*/false, /*Complain=*/false);
return Ctx.IsEquivalent(FD1, FD2);
}
@@ -4592,7 +4589,6 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer,
}
assert(FriendPosition && "Friend decl not found in own parent.");
-
return {FriendCount, *FriendPosition};
}
@@ -4608,7 +4604,8 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
const auto *RD = cast<CXXRecordDecl>(DC);
SmallVector<FriendDecl *, 2> ImportedEquivalentFriends;
for (FriendDecl *ImportedFriend : RD->friends())
- if (IsEquivalentFriend(Importer, D, ImportedFriend))
+ if (ImportedFriend->getKind() == Decl::Friend &&
+ IsEquivalentFriend(Importer, D, ImportedFriend))
ImportedEquivalentFriends.push_back(ImportedFriend);
FriendCountAndPosition CountAndPosition =
@@ -4640,15 +4637,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();
@@ -4662,7 +4650,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());
@@ -4671,6 +4659,92 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) {
return FrD;
}
+ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ DeclContext *DC, *LexicalDC;
+ if (Error Err = ImportDeclContext(D, DC, LexicalDC))
+ return std::move(Err);
+
+ const auto *RD = cast<CXXRecordDecl>(DC);
+ SmallVector<FriendTemplateDecl *, 2> ImportedEquivalentFriends;
+ for (FriendDecl *ImportedFriend : RD->friends()) {
+ auto *ImportedFriendTemplate = dyn_cast<FriendTemplateDecl>(ImportedFriend);
+ if (ImportedFriendTemplate &&
+ IsEquivalentFriend(Importer, D, ImportedFriendTemplate))
+ ImportedEquivalentFriends.push_back(ImportedFriendTemplate);
+ }
+
+ FriendCountAndPosition CountAndPosition =
+ getFriendCountAndPosition(Importer, D);
+ assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount &&
+ "Class with non-matching friends is imported, ODR check wrong?");
+
+ if (ImportedEquivalentFriends.size() == CountAndPosition.TotalCount)
+ return Importer.MapImported(
+ D, ImportedEquivalentFriends[CountAndPosition.IndexOfDecl]);
+
+ FriendTemplateDecl::FriendUnion ToFU;
+ 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 TemplateOrErr = import(FromTemplate))
+ ToTemplate = *TemplateOrErr;
+ else
+ return TemplateOrErr.takeError();
+ }
+
+ 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();
+ }
+
+ 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 (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);
+ 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 e0b62591a6a73..d3b47ebe5520c 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2290,7 +2290,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
- return true;
+ return IsStructurallyEquivalent(Context, Params1->getRequiresClause(),
+ Params2->getRequiresClause());
}
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
@@ -2441,6 +2442,45 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
+static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
+ FriendTemplateDecl *FTD1,
+ FriendTemplateDecl *FTD2) {
+ ArrayRef<TemplateParameterList *> TPL1 =
+ FTD1->getFriendTypeTemplateParameterLists();
+ ArrayRef<TemplateParameterList *> TPL2 =
+ FTD2->getFriendTypeTemplateParameterLists();
+ bool EquivalentTemplateParameterLists = llvm::equal(
+ TPL1, TPL2,
+ [&Context](TemplateParameterList *LHS, TemplateParameterList *RHS) {
+ return IsStructurallyEquivalent(Context, LHS, RHS);
+ });
+ if (!EquivalentTemplateParameterLists)
+ return false;
+
+ TemplateName TN1 = FTD1->getFriendTemplateName();
+ TemplateName TN2 = FTD2->getFriendTemplateName();
+ if (TN1.isNull() != TN2.isNull())
+ return false;
+
+ if (TN1.isNull()) {
+ 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;
+ }
+
+ return IsStructurallyEquivalent(Context, TN1, TN2);
+}
+
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
TypedefNameDecl *D1, TypedefNameDecl *D2) {
if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier()))
@@ -2780,6 +2820,23 @@ bool StructuralEquivalenceContext::CheckCommonEquivalence(Decl *D1, Decl *D2) {
return true;
}
+bool StructuralEquivalenceContext::IsEquivalent(TemplateParameterList *TPL1,
+ TemplateParameterList *TPL2) {
+ assert(DeclsToCheck.empty());
+ assert(VisitedDecls.empty());
+
+ if (TPL1 == TPL2)
+ return true;
+
+ if (!TPL1 || !TPL2)
+ return false;
+
+ if (!::IsStructurallyEquivalent(*this, TPL1, TPL2))
+ return false;
+
+ return !Finish();
+}
+
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 95591d3a09431..71546c5b43070 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);
@@ -888,24 +889,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);
}
@@ -914,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/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 275fe364e306d..124ebcc50223b 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1236,21 +1236,50 @@ 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::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) {
+ std::size_t Extra =
+ FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>(
+ NumFriendTypeTPLists);
+ return new (C, ID, Extra)
+ FriendTemplateDecl(EmptyShell(), NumFriendTypeTPLists);
+}
+
+SourceRange FriendTemplateDecl::getSourceRange() const {
+ SourceLocation Begin =
+ getFriendTypeTemplateParameterLists().front()->getTemplateLoc();
+ SourceLocation End = FriendDecl::getSourceRange().getEnd();
+ return SourceRange(Begin, End);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..b85204083cca9 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,19 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
Hash.AddBoolean(D->isPackExpansion());
}
+ void VisitFriendTemplateDecl(const FriendTemplateDecl *D) {
+ for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+ Hash.AddTemplateParameterList(TPL);
+
+ 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/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 78fbc9e31842d..ed3ff4bf277bc 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..582f2f779d761 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -19,8 +19,11 @@
#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"
+#include "clang/Sema/TemplateDeduction.h"
using namespace clang;
using namespace sema;
@@ -274,6 +277,243 @@ struct AccessTarget : public AccessedEntity {
}
+static void AddFriendTemplateDeductionCandidate(
+ Sema &S, TemplateDecl *TD, Decl *Declaration, TemplateDeductionInfo &Info,
+ TemplateDeductionResult Result, TemplateSpecCandidateSet *FailedTSC) {
+ if (!FailedTSC)
+ return;
+
+ for (TemplateSpecCandidate &Candidate : *FailedTSC) {
+ if (!Candidate.Specialization)
+ continue;
+
+ if (declaresSameEntity(Candidate.Specialization, Declaration))
+ return;
+ }
+
+ FailedTSC->addCandidate().set(
+ DeclAccessPair::make(TD, AS_public), Declaration,
+ MakeDeductionFailureInfo(S.Context, Result, Info));
+}
+
+static bool
+AreTemplateArgumentsStructurallyEqual(ArrayRef<TemplateArgument> LHS,
+ ArrayRef<TemplateArgument> RHS) {
+ return llvm::equal(
+ LHS, RHS, [](const TemplateArgument &LHS, const TemplateArgument &RHS) {
+ return LHS.structurallyEquals(RHS);
+ });
+}
+
+static bool DeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+ TemplateDecl *TD,
+ ArrayRef<TemplateArgument> PatternArgs,
+ ArrayRef<TemplateArgument> Args,
+ TemplateDeductionInfo &Info,
+ TemplateSpecCandidateSet *FailedTSC,
+ bool CopyDeducedArgs) {
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap Trap(S, Info);
+ LocalInstantiationScope InstantiationScope(S);
+ SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size());
+ 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> InstArgs(Deduced.begin(), Deduced.end());
+ Sema::InstantiatingTemplate Inst(S, Info.getLocation(), TD, InstArgs);
+ 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=*/CopyDeducedArgs);
+ });
+
+ 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 bool
+DeduceTemplateArguments(Sema &S, TemplateParameterList *TPL, TemplateDecl *TD,
+ ArrayRef<TemplateArgument> PatternArgs,
+ ArrayRef<TemplateArgument> Args, SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC,
+ SmallVectorImpl<TemplateArgument> &DeducedArgs) {
+ if (AreTemplateArgumentsStructurallyEqual(PatternArgs, Args)) {
+ DeducedArgs.assign(Args.begin(), Args.end());
+ return true;
+ }
+
+ TemplateDeductionInfo Info(FailedTSC ? FailedTSC->getLocation() : Loc);
+ if (!DeduceTemplateArguments(S, TPL, TD, PatternArgs, Args, Info, FailedTSC,
+ /*CopyDeducedArgs=*/true))
+ return false;
+
+ TemplateArgumentList *CanonicalDeducedArgs = Info.takeCanonical();
+ DeducedArgs.assign(CanonicalDeducedArgs->asArray().begin(),
+ CanonicalDeducedArgs->asArray().end());
+
+ return true;
+}
+
+static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL,
+ TemplateDecl *TD,
+ ArrayRef<TemplateArgument> PatternArgs,
+ ArrayRef<TemplateArgument> Args,
+ SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC) {
+ if (AreTemplateArgumentsStructurallyEqual(PatternArgs, Args))
+ return true;
+
+ TemplateDeductionInfo Info(FailedTSC ? FailedTSC->getLocation() : Loc);
+ return DeduceTemplateArguments(S, TPL, TD, PatternArgs, Args, Info, FailedTSC,
+ /*CopyDeducedArgs=*/false);
+}
+
+static CanQual<FunctionProtoType> GetCanonicalFunctionProto(ASTContext &Context,
+ QualType Ty) {
+ return Context.getCanonicalType(Ty)->getAs<FunctionProtoType>();
+}
+
+static CanQual<FunctionProtoType>
+GetCanonicalFunctionProto(ASTContext &Context, const FunctionDecl *FD) {
+ return GetCanonicalFunctionProto(Context, FD->getType());
+}
+
+static const TemplateSpecializationType *
+GetQualifierClassTemplateSpecializationType(ASTContext &Context,
+ NestedNameSpecifier NNS) {
+ if (!NNS)
+ return nullptr;
+
+ QualType Ty(NNS.getAsType(), 0);
+ if (Ty.isNull())
+ return nullptr;
+
+ if (const auto *ICNT = Ty->getAs<InjectedClassNameType>())
+ Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(Context);
+
+ const auto *TST = Ty->getAsNonAliasTemplateSpecializationType();
+ if (TST && isa_and_nonnull<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
+ return TST;
+
+ return nullptr;
+}
+
+static FunctionTemplateDecl *TryGetFunctionTemplateDecl(FunctionDecl *FD) {
+ if (auto *FTD = FD->getPrimaryTemplate())
+ return FTD->getCanonicalDecl();
+
+ if (auto *FTD = FD->getDescribedFunctionTemplate())
+ return FTD->getCanonicalDecl();
+
+ if (FunctionDecl *Pattern =
+ FD->getTemplateInstantiationPattern(/*ForDefinition=*/false)) {
+ if (auto *FTD = Pattern->getDescribedFunctionTemplate())
+ return FTD->getCanonicalDecl();
+ if (auto *FTD = Pattern->getPrimaryTemplate())
+ return FTD->getCanonicalDecl();
+ }
+
+ return nullptr;
+}
+
+static bool DeduceFriendContextTemplateArguments(
+ Sema &S, DeclContext *DC, ClassTemplateDecl *FriendCTD,
+ ArrayRef<TemplateArgument> FriendArgs, TemplateParameterList *FriendTPL,
+ SourceLocation Loc, TemplateSpecCandidateSet *FailedTSC,
+ SmallVectorImpl<TemplateArgument> &DeducedArgs) {
+ const auto *RD = dyn_cast<CXXRecordDecl>(DC);
+ 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 (!declaresSameEntity(ContextCTD, FriendCTD))
+ return false;
+
+ return DeduceTemplateArguments(S, FriendTPL, FriendCTD, FriendArgs,
+ ContextArgs, Loc, FailedTSC, DeducedArgs);
+}
+
+static bool MatchesFriendContext(Sema &S, DeclContext *DC,
+ ClassTemplateDecl *FriendCTD,
+ ArrayRef<TemplateArgument> FriendArgs,
+ TemplateParameterList *FriendTPL,
+ SourceLocation Loc,
+ TemplateSpecCandidateSet *FailedTSC) {
+ SmallVector<TemplateArgument, 4> DeducedArgs;
+ return DeduceFriendContextTemplateArguments(
+ S, DC, FriendCTD, FriendArgs, FriendTPL, Loc, FailedTSC, DeducedArgs);
+}
+
+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 (declaresSameEntity(CTD, Template))
+ 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 (declaresSameEntity(FriendContext, Context))
+ 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) {
@@ -283,8 +523,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,9 +586,7 @@ static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived,
return OnFailure;
}
-
-static bool MightInstantiateTo(Sema &S, DeclContext *Context,
- DeclContext *Friend) {
+static bool MightInstantiateTo(DeclContext *Context, DeclContext *Friend) {
if (Friend == Context)
return true;
@@ -363,7 +605,7 @@ static bool MightInstantiateTo(Sema &S, DeclContext *Context,
// Asks whether the type in 'context' can ever instantiate to the type
// in 'friend'.
-static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
+static bool MightInstantiateTo(CanQualType Context, CanQualType Friend) {
if (Friend == Context)
return true;
@@ -374,49 +616,66 @@ static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend)
return true;
}
-static bool MightInstantiateTo(Sema &S,
- FunctionDecl *Context,
- FunctionDecl *Friend) {
- if (Context->getDeclName() != Friend->getDeclName())
+static bool MightInstantiateTo(CanQual<FunctionProtoType> Context,
+ CanQual<FunctionProtoType> Friend) {
+ if (Friend.getQualifiers() != Context.getQualifiers())
+ return false;
+
+ if (Friend->getNumParams() != Context->getNumParams())
return false;
- if (!MightInstantiateTo(S,
- Context->getDeclContext(),
- Friend->getDeclContext()))
+ if (!MightInstantiateTo(Context->getReturnType(), Friend->getReturnType()))
return false;
- CanQual<FunctionProtoType> FriendTy
- = S.Context.getCanonicalType(Friend->getType())
- ->getAs<FunctionProtoType>();
- CanQual<FunctionProtoType> ContextTy
- = S.Context.getCanonicalType(Context->getType())
- ->getAs<FunctionProtoType>();
+ for (unsigned I = 0, E = Friend->getNumParams(); I != E; ++I)
+ if (!MightInstantiateTo(Context->getParamType(I), Friend->getParamType(I)))
+ return false;
+
+ return true;
+}
+
+static bool MightInstantiateTo(ASTContext &Ctx, DeclarationName Context,
+ DeclarationName Friend) {
+ if (Context == Friend)
+ return true;
- // There isn't any way that I know of to add qualifiers
- // during instantiation.
- if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
+ if (Context.getNameKind() != Friend.getNameKind())
return false;
- if (FriendTy->getNumParams() != ContextTy->getNumParams())
+ switch (Context.getNameKind()) {
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ return MightInstantiateTo(Ctx.getCanonicalType(Context.getCXXNameType()),
+ Ctx.getCanonicalType(Friend.getCXXNameType()));
+
+ default:
return false;
+ }
+}
- if (!MightInstantiateTo(S, ContextTy->getReturnType(),
- FriendTy->getReturnType()))
+static bool MightInstantiateTo(ASTContext &Ctx, FunctionDecl *Context,
+ FunctionDecl *Friend) {
+ if (!MightInstantiateTo(Ctx, Context->getDeclName(), Friend->getDeclName()))
return false;
- for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I)
- if (!MightInstantiateTo(S, ContextTy->getParamType(I),
- FriendTy->getParamType(I)))
- return false;
+ DeclContext *ContextDC = Context->getDeclContext();
+ DeclContext *FriendDC = Friend->getDeclContext();
- return true;
+ if (!FriendDC->isDependentContext() &&
+ !MightInstantiateTo(ContextDC, FriendDC))
+ return false;
+
+ CanQual<FunctionProtoType> FriendTy = GetCanonicalFunctionProto(Ctx, Friend);
+ CanQual<FunctionProtoType> ContextTy =
+ GetCanonicalFunctionProto(Ctx, Context);
+
+ return MightInstantiateTo(ContextTy, FriendTy);
}
-static bool MightInstantiateTo(Sema &S,
- FunctionTemplateDecl *Context,
+static bool MightInstantiateTo(ASTContext &Ctx, FunctionTemplateDecl *Context,
FunctionTemplateDecl *Friend) {
- return MightInstantiateTo(S,
- Context->getTemplatedDecl(),
+ return MightInstantiateTo(Ctx, Context->getTemplatedDecl(),
Friend->getTemplatedDecl());
}
@@ -477,7 +736,7 @@ static AccessResult MatchesFriend(Sema &S,
}
// It's a match.
- if (Friend == CTD->getCanonicalDecl())
+ if (declaresSameEntity(Friend, CTD))
return AR_accessible;
// If the context isn't dependent, it can't be a dependent match.
@@ -491,8 +750,7 @@ static AccessResult MatchesFriend(Sema &S,
// If the class's context can't instantiate to the friend's
// context, it can't be a dependent match.
- if (!MightInstantiateTo(S, CTD->getDeclContext(),
- Friend->getDeclContext()))
+ if (!MightInstantiateTo(CTD->getDeclContext(), Friend->getDeclContext()))
continue;
// Otherwise, it's a dependent match.
@@ -514,7 +772,7 @@ static AccessResult MatchesFriend(Sema &S,
if (Friend == *I)
return AR_accessible;
- if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
+ if (EC.isDependent() && MightInstantiateTo(S.Context, *I, Friend))
OnFailure = AR_dependent;
}
@@ -533,63 +791,330 @@ static AccessResult MatchesFriend(Sema &S,
for (SmallVectorImpl<FunctionDecl*>::const_iterator
I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
- FunctionTemplateDecl *FTD = (*I)->getPrimaryTemplate();
- if (!FTD)
- FTD = (*I)->getDescribedFunctionTemplate();
+ FunctionTemplateDecl *FTD = TryGetFunctionTemplateDecl(*I);
if (!FTD)
continue;
- FTD = FTD->getCanonicalDecl();
-
if (Friend == FTD)
return AR_accessible;
- if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
+ if (EC.isDependent() && MightInstantiateTo(S.Context, FTD, Friend))
OnFailure = AR_dependent;
}
return OnFailure;
}
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ NamedDecl *ND) {
+ ND = cast<NamedDecl>(ND->getCanonicalDecl());
+ if (ClassTemplateDecl *CTD = dyn_cast<ClassTemplateDecl>(ND))
+ return MatchesFriend(S, EC, CTD);
+
+ if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+ return MatchesFriend(S, EC, FTD);
+
+ if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND))
+ return MatchesFriend(S, EC, RD);
+
+ assert(isa<FunctionDecl>(ND) && "unknown friend decl kind");
+ return MatchesFriend(S, EC, cast<FunctionDecl>(ND));
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ ClassTemplateDecl *FriendCTD,
+ NestedNameSpecifier Qualifier,
+ TemplateSpecCandidateSet *FailedTSC) {
+ AccessResult OnFailure = AR_inaccessible;
+ const auto *FriendTST =
+ GetQualifierClassTemplateSpecializationType(S.Context, Qualifier);
+ 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()))
+ continue;
+
+ if (MatchesFriendContext(S, RD->getDeclContext(), FriendContextCTD,
+ FriendTD->getDeclContext(), FriendArgs, FriendTPL,
+ FriendTD->getLocation(), FailedTSC))
+ return AR_accessible;
+ }
+
+ 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,
+ TemplateSpecCandidateSet *FailedTSC) {
+ AccessResult OnFailure = AR_inaccessible;
+ const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+ S.Context, FriendFTD->getTemplatedDecl()->getQualifier());
+ if (!FriendTST)
+ return OnFailure;
+
+ auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+ FriendTST->getTemplateName().getAsTemplateDecl());
+ if (!FriendCTD)
+ 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();
+ SourceLocation FriendLoc = FriendTD->getLocation();
+
+ for (FunctionDecl *FD : EC.Functions) {
+ if (!MatchesFriendContext(S, FD->getDeclContext(), FriendCTD, FriendArgs,
+ FriendTPL, FriendLoc, FailedTSC))
+ continue;
+
+ FunctionTemplateDecl *ContextFTD = TryGetFunctionTemplateDecl(FD);
+ if (ContextFTD && MightInstantiateTo(S.Context, FriendFTD, ContextFTD))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FriendTD,
+ FunctionDecl *FriendFD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ AccessResult OnFailure = AR_inaccessible;
+ const auto *FriendTST = GetQualifierClassTemplateSpecializationType(
+ S.Context, FriendFD->getQualifier());
+ if (!FriendTST)
+ return OnFailure;
+
+ auto *FriendCTD = dyn_cast<ClassTemplateDecl>(
+ FriendTST->getTemplateName().getAsTemplateDecl());
+ if (!FriendCTD)
+ 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();
+ SourceLocation FriendLoc = FriendTD->getLocation();
+
+ for (FunctionDecl *FD : EC.Functions) {
+ if (!MightInstantiateTo(S.Context, FD->getDeclName(),
+ FriendFD->getDeclName()))
+ continue;
+
+ SmallVector<TemplateArgument, 4> DeducedArgs;
+ if (!DeduceFriendContextTemplateArguments(
+ S, FD->getDeclContext(), FriendCTD, FriendArgs, FriendTPL,
+ FriendLoc, FailedTSC, DeducedArgs))
+ continue;
+
+ CanQual<FunctionProtoType> ContextProto =
+ GetCanonicalFunctionProto(S.Context, FD);
+ Sema::InstantiatingTemplate Inst(S, FriendFD->getLocation(), FriendCTD,
+ DeducedArgs);
+ if (Inst.isInvalid())
+ continue;
+
+ TemplateDeductionInfo Info(FriendFD->getLocation());
+ Sema::SFINAETrap Trap(S, Info);
+ MultiLevelTemplateArgumentList Args(FriendCTD, DeducedArgs, /*Final=*/true);
+ QualType FriendType =
+ S.SubstType(FriendFD->getType(), Args, FriendFD->getLocation(),
+ FriendFD->getDeclName());
+ if (FriendType.isNull() || Trap.hasErrorOccurred())
+ continue;
+
+ CanQual<FunctionProtoType> FriendProto =
+ GetCanonicalFunctionProto(S.Context, FriendType);
+ if (MightInstantiateTo(ContextProto, FriendProto))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
+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);
+
+ if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND))
+ return MatchesFriend(S, EC, FTD, TD, FailedTSC);
+
+ if (auto *FD = dyn_cast<FunctionDecl>(ND))
+ return MatchesFriend(S, EC, FTD, FD, FailedTSC);
+
+ return MatchesFriend(S, EC, ND);
+}
+
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD, TypeSourceInfo *TSI,
+ TemplateSpecCandidateSet *FailedTSC) {
+ 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 *TST = GetQualifierClassTemplateSpecializationType(S.Context, NNS);
+ if (!TST)
+ return OnFailure;
+
+ auto *CTD =
+ dyn_cast<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl());
+ if (!CTD)
+ return OnFailure;
+
+ ArrayRef<TemplateParameterList *> FriendTPLists =
+ FTD->getFriendTypeTemplateParameterLists();
+ if (FriendTPLists.empty())
+ return OnFailure;
+
+ TemplateParameterList *TPL = FriendTPLists.front();
+ 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 (!declaresSameEntity(CTSD->getSpecializedTemplate(), CTD))
+ continue;
+
+ if (CanDeduceTemplateArguments(S, TPL, CTD, TST->template_arguments(),
+ CTSD->getTemplateArgs().asArray(),
+ FTD->getLocation(), FailedTSC))
+ return AR_accessible;
+ }
+
+ return OnFailure;
+}
+
/// Determines whether the given friend declaration matches anything
/// in the effective context.
static AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
FriendDecl *FriendD) {
- // Whitelist accesses if there's an invalid or unsupported friend
- // declaration.
- if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend())
+ // Whitelist accesses if there's an invalid friend declaration.
+ if (FriendD->isInvalidDecl())
return AR_accessible;
+ if (NamedDecl *Friend = FriendD->getFriendDecl())
+ return MatchesFriend(S, EC, Friend);
+
if (TypeSourceInfo *T = FriendD->getFriendType())
return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified());
- NamedDecl *Friend
- = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
-
- // FIXME: declarations with dependent or templated scope.
+ return AR_inaccessible;
+}
- if (isa<ClassTemplateDecl>(Friend))
- return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC,
+ FriendTemplateDecl *FTD,
+ TemplateSpecCandidateSet *FailedTSC) {
+ if (FTD->isInvalidDecl())
+ 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, FailedTSC);
- if (isa<CXXRecordDecl>(Friend))
- return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+ if (TypeSourceInfo *TSI = FTD->getFriendType())
+ return MatchesFriend(S, EC, FTD, TSI, FailedTSC);
- assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
- return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
+ 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 (auto *Friend : Class->friends()) {
- switch (MatchesFriend(S, EC, Friend)) {
+ for (FriendDecl *Friend : Class->friends()) {
+ AccessResult AR;
+ if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend))
+ AR = MatchesFriend(S, EC, FTD, FailedTSC);
+ else
+ AR = MatchesFriend(S, EC, Friend);
+
+ switch (AR) {
case AR_accessible:
return AR_accessible;
@@ -613,6 +1138,7 @@ namespace {
struct ProtectedFriendContext {
Sema &S;
const EffectiveContext &EC;
+ TemplateSpecCandidateSet *FailedTSC;
const CXXRecordDecl *NamingClass;
bool CheckDependent;
bool EverDependent;
@@ -622,18 +1148,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;
@@ -720,9 +1247,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);
@@ -730,19 +1257,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");
@@ -862,7 +1390,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;
@@ -870,7 +1399,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;
@@ -983,7 +1512,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;
@@ -1179,7 +1709,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:
@@ -1222,7 +1753,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;
@@ -1326,9 +1858,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();
@@ -1340,7 +1872,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
@@ -1369,7 +1902,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
@@ -1421,25 +1954,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;
@@ -1449,6 +1987,20 @@ static AccessResult CheckEffectiveAccess(Sema &S,
llvm_unreachable("invalid access result");
}
+static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC,
+ SourceLocation Loc,
+ AccessTarget &Entity) {
+ assert(Entity.getAccess() != AS_public && "called for public access!");
+
+ if (Entity.isQuiet())
+ return CheckEffectiveAccess(S, EC, Loc, Entity, /*FailedTSC=*/nullptr);
+
+ TemplateSpecCandidateSet FailedTSC(
+ Loc, /*ForTakingAddress=*/false,
+ TemplateSpecCandidateSetKind::FriendTemplate);
+ return CheckEffectiveAccess(S, EC, Loc, Entity, &FailedTSC);
+}
+
static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
AccessTarget &Entity) {
// If the access path is public, it's accessible everywhere.
@@ -1933,7 +2485,8 @@ bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass,
AccessTarget Entity(Context, AccessedEntity::Member, NamingClass,
DeclAccessPair::make(Target, AS_none), BaseType);
EffectiveContext EC(CurContext);
- return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible;
+ return ::IsAccessible(*this, EC, Entity, /*FailedTSC=*/nullptr) !=
+ ::AR_inaccessible;
}
if (ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(Target)) {
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 65b46562b166e..407dacefe76a6 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18083,6 +18083,31 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
return Decl;
}
+bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS,
+ TemplateParameterList *FPL) {
+ if (!NNS.isDependent() || !FPL || FPL->size() == 0)
+ return false;
+
+ assert(NNS.getKind() == NestedNameSpecifier::Kind::Type &&
+ "dependent nested-name-specifier must be a type");
+ QualType T(NNS.getCanonical().getAsType(), 0);
+
+ if (const auto *PIT = dyn_cast<PackIndexingType>(T))
+ T = PIT->getPattern();
+
+ if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+ if (isa_and_nonnull<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl()))
+ return false;
+ }
+
+ if (isa<InjectedClassNameType>(T))
+ return false;
+
+ Diag(Loc, diag::err_dependent_friend_not_member);
+ return true;
+}
+
DeclResult Sema::ActOnTemplatedFriendTag(
Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc,
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
@@ -18098,16 +18123,17 @@ 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(), IsMemberSpecialization)
- .get();
+ if (SS.isEmpty() || !SS.getScopeRep().isDependent()) {
+ DeclResult Result = CheckClassTemplate(
+ S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr,
+ TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(),
+ FriendLoc, TempParamLists.size() - 1, TempParamLists.data(),
+ IsMemberSpecialization);
+ return Result.get();
+ }
} else {
// The "template<>" header is extraneous.
Diag(TemplateParams->getTemplateLoc(), diag::err_template_tag_noparams)
@@ -18152,9 +18178,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;
@@ -18180,25 +18205,35 @@ 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);
+ FriendDecl *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);
+ }
+
Friend->setAccess(AS_public);
- Friend->setUnsupportedFriend(true);
CurContext->addDecl(Friend);
+
return Friend;
}
@@ -18299,11 +18334,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);
@@ -18380,7 +18418,7 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
Kind == NestedNameSpecifier::Kind::Namespace;
if (IsNamespaceOrGlobal) {
Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def)
- << SS.getScopeRep() << FixItHint::CreateRemoval(SS.getRange());
+ << SS.getScopeRep();
SS.clear();
}
}
@@ -18501,6 +18539,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()) {
@@ -18569,81 +18612,79 @@ 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);
- // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
- // default argument expression, that declaration shall be a definition
- // and shall be the only declaration of the function or function
- // template in the translation unit.
- if (functionDeclHasDefaultArgument(FD)) {
- // We can't look at FD->getPreviousDecl() because it may not have been set
- // if we're in a dependent context. If the function is known to be a
- // redeclaration, we will have narrowed Previous down to the right decl.
- if (D.isRedeclaration()) {
- Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_redeclared);
- Diag(Previous.getRepresentativeDecl()->getLocation(),
- diag::note_previous_declaration);
- } else if (!D.isFunctionDefinition())
- Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
- }
+ 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()) {
+ SemaDiagnosticBuilder DB =
+ Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def);
+
+ DB << SS.getScopeRep();
+
+ // Friend function defined in a local class.
+ } else if (FunctionContainingLocalClass) {
+ Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class);
+
+ // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have
+ // a template-id, the function name is not unqualified because these is
+ // no name. While the wording requires some reading in-between the
+ // lines, GCC, MSVC, and EDG all consider a friend function
+ // specialization definitions to be de facto explicit specialization
+ // and diagnose them as such.
+ } else if (isTemplateId) {
+ Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def);
+ }
+ }
+
+ // C++11 [dcl.fct.default]p4: If a friend declaration specifies a
+ // default argument expression, that declaration shall be a definition
+ // and shall be the only declaration of the function or function
+ // template in the translation unit.
+ if (functionDeclHasDefaultArgument(FD)) {
+ // We can't look at FD->getPreviousDecl() because it may not have been set
+ // if we're in a dependent context. If the function is known to be a
+ // redeclaration, we will have narrowed Previous down to the right decl.
+ if (D.isRedeclaration()) {
+ Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_redeclared);
+ Diag(Previous.getRepresentativeDecl()->getLocation(),
+ diag::note_previous_declaration);
+ } else if (!D.isFunctionDefinition())
+ Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def);
+ }
- // Mark templated-scope function declarations as unsupported.
- if (!FD->getTemplateParameterLists().empty() && SS.isValid()) {
- Diag(FD->getLocation(), diag::warn_template_qualified_friend_unsupported)
- << SS.getScopeRep() << SS.getRange()
- << cast<CXXRecordDecl>(CurContext);
- FrD->setUnsupportedFriend(true);
- }
+ ArrayRef<TemplateParameterList *> TPL = FD->getTemplateParameterLists();
+ FriendDecl *Friend;
+ if (TPL.size() > 0 && SS.isValid()) {
+ if (CheckTemplateDeclScope(S, TPL.back()))
+ return nullptr;
+
+ Friend =
+ FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(),
+ ND, DS.getFriendSpecLoc(), TPL);
+ } else {
+ Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND,
+ DS.getFriendSpecLoc());
}
- warnOnReservedIdentifier(ND);
+ Friend->setAccess(AS_public);
+ CurContext->addDecl(Friend);
return ND;
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c8b05a9f94bd0..19141890ca230 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12394,8 +12394,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*>()) ||
@@ -12642,7 +12643,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;
}
@@ -12658,7 +12662,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;
}
@@ -13609,10 +13615,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() {
@@ -13664,7 +13672,7 @@ void TemplateSpecCandidateSet::NoteCandidates(Sema &S, SourceLocation Loc) {
assert(Cand->Specialization &&
"Non-matching built-in candidates are not added to Cands.");
- Cand->NoteDeductionFailure(S, ForTakingAddress);
+ Cand->NoteDeductionFailure(S, ForTakingAddress, CandidateSetKind);
}
if (I != E)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index ad0e653e254dd..b06bf2d25ad6a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1958,14 +1958,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))
@@ -11407,6 +11402,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/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index d93b528facbcc..adabbe69903db 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4112,6 +4112,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 c56203f10ac3c..8311ed5349639 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1989,68 +1989,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;
}
@@ -2069,7 +2070,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;
}
@@ -4711,12 +4711,75 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D)
}
Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
- // FIXME: We need to be able to instantiate FriendTemplateDecls.
- unsigned DiagID = SemaRef.getDiagnostics().getCustomDiagID(
- DiagnosticsEngine::Error,
- "cannot instantiate %0 yet");
- SemaRef.Diag(D->getLocation(), DiagID)
- << D->getDeclKindName();
+ ArrayRef<TemplateParameterList *> TPLists =
+ D->getFriendTypeTemplateParameterLists();
+
+ Decl *FTD = nullptr;
+ if (TypeSourceInfo *FT = D->getFriendType()) {
+ if (D->isPackExpansion() && InstantiateFriendPackExpansion(D, FT, TPLists))
+ return nullptr;
+
+ SmallVector<TemplateParameterList *, 1> TPL;
+ if (SubstTemplateParameterLists(TPLists, TPL))
+ return nullptr;
+
+ TemplateName Template;
+ 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 && !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);
+ 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);
+ }
+ }
+ }
+ }
+
+ 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;
+ if (SubstTemplateParameterLists(TPLists, TPL))
+ return nullptr;
+
+ if (auto *InstND = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
+ D->getLocation(), D->getFriendDecl(), TemplateArgs)))
+ FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ InstND, D->getFriendLoc(), TPL);
+ }
+
+ if (FTD) {
+ FTD->setAccess(AS_public);
+ Owner->addDecl(FTD);
+ }
return nullptr;
}
@@ -4856,6 +4919,20 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
return InstL;
}
+bool TemplateDeclInstantiator::SubstTemplateParameterLists(
+ ArrayRef<TemplateParameterList *> TPL,
+ SmallVectorImpl<TemplateParameterList *> &InstTPL) {
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ 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,
@@ -5169,8 +5246,10 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
continue;
}
- ParmVarDecl *Parm =
- cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam));
+ ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
+ OldParam, TemplateArgs, /*indexAdjustment=*/0,
+ /*NumExpansions=*/std::nullopt,
+ /*ExpectParameterPack=*/false, EvaluateConstraints);
if (!Parm)
return nullptr;
Params.push_back(Parm);
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 370a6970d531a..ee359268a0e3c 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2404,26 +2404,29 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) {
D->Friend = readDeclAs<NamedDecl>();
else
D->Friend = readTypeSourceInfo();
- for (unsigned i = 0; i != D->NumTPLists; ++i)
- D->getTrailingObjects()[i] = Record.readTemplateParameterList();
D->NextFriend = readDeclID().getRawValue();
- D->UnsupportedFriend = (Record.readInt() != 0);
D->FriendLoc = readSourceLocation();
D->EllipsisLoc = readSourceLocation();
}
void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
VisitDecl(D);
- unsigned NumParams = Record.readInt();
- D->NumParams = NumParams;
- D->Params = new (Reader.getContext()) TemplateParameterList *[NumParams];
- for (unsigned i = 0; i != NumParams; ++i)
- D->Params[i] = Record.readTemplateParameterList();
- if (Record.readInt()) // HasFriendDecl
- D->Friend = readDeclAs<NamedDecl>();
- else
+ for (unsigned i = 0; i != D->NumTPLists; ++i)
+ D->getTrailingObjects()[i] = Record.readTemplateParameterList();
+ switch (Record.readInt()) {
+ case FTDK_Type:
D->Friend = readTypeSourceInfo();
+ break;
+ case FTDK_Decl:
+ D->Friend = readDeclAs<NamedDecl>();
+ break;
+ case FTDK_Template:
+ D->Template = Record.readTemplateName();
+ break;
+ }
+ D->NextFriend = readDeclID().getRawValue();
D->FriendLoc = readSourceLocation();
+ D->EllipsisLoc = readSourceLocation();
}
void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
@@ -4078,10 +4081,11 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
D = AccessSpecDecl::CreateDeserialized(Context, ID);
break;
case DECL_FRIEND:
- D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt());
+ D = FriendDecl::CreateDeserialized(Context, ID);
break;
case DECL_FRIEND_TEMPLATE:
- D = FriendTemplateDecl::CreateDeserialized(Context, ID);
+ D = FriendTemplateDecl::CreateDeserialized(Context, ID,
+ /*NumTPLists=*/Record.readInt());
break;
case DECL_CLASS_TEMPLATE:
D = ClassTemplateDecl::CreateDeserialized(Context, ID);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index f271769d8edf6..3b192590fe7f8 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1828,9 +1828,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);
@@ -1838,26 +1835,34 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) {
Record.AddDeclRef(D->getFriendDecl());
else
Record.AddTypeSourceInfo(D->getFriendType());
- for (unsigned i = 0; i < D->NumTPLists; ++i)
- Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i));
Record.AddDeclRef(D->getNextFriend());
- Record.push_back(D->UnsupportedFriend);
Record.AddSourceLocation(D->FriendLoc);
Record.AddSourceLocation(D->EllipsisLoc);
Code = serialization::DECL_FRIEND;
}
void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) {
+ // Record the number of friend type template parameter lists here
+ // so as to simplify memory allocation during deserialization.
+ Record.push_back(D->NumTPLists);
VisitDecl(D);
- Record.push_back(D->getNumTemplateParameters());
- for (unsigned i = 0, e = D->getNumTemplateParameters(); i != e; ++i)
- Record.AddTemplateParameterList(D->getTemplateParameterList(i));
- Record.push_back(D->getFriendDecl() != nullptr);
- if (D->getFriendDecl())
- Record.AddDeclRef(D->getFriendDecl());
- else
- Record.AddTypeSourceInfo(D->getFriendType());
- Record.AddSourceLocation(D->getFriendLoc());
+ for (TemplateParameterList *TPL : D->getFriendTypeTemplateParameterLists())
+ Record.AddTemplateParameterList(TPL);
+ if (D->Template.isNull()) {
+ if (D->getFriendDecl()) {
+ Record.push_back(FTDK_Decl);
+ Record.AddDeclRef(D->getFriendDecl());
+ } else {
+ Record.push_back(FTDK_Type);
+ Record.AddTypeSourceInfo(D->getFriendType());
+ }
+ } else {
+ Record.push_back(FTDK_Template);
+ Record.AddTemplateName(D->Template);
+ }
+ Record.AddDeclRef(D->getNextFriend());
+ 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 f0424a1021abc..3891bcc5b1975 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -416,29 +416,24 @@ struct A<float*> {
};
class C {
- int private_int;
+ int private_int; // #cwg1862-C-private_int
template<class T>
friend struct A<T>::B;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
template<class T>
friend void A<T>::f();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}}
- // FIXME: this is ill-formed, because A<T>::D does not end with a simple-template-id
template<class T>
friend void A<T>::D::g();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::D' for friend class declaration is not supported; turning off access control for 'C'}}
+ // expected-error at -1 {{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,16 @@ void A<int>::B::e() { (void)c.private_int; }
template<class T>
void A<T>::f() { (void)c.private_int; }
int A<int>::f() { (void)c.private_int; return 0; }
+// expected-error at -1 {{'private_int' is a private member of 'cwg1862::C'}}
+// expected-note@#cwg1862-C-private_int {{implicitly declared private here}}
-// FIXME: both definition of 'D::g' are not friends, so they don't have access to 'private_int'
+// FIXME: the primary template definition of 'D::g' is not a friend either,
+// so it should not 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@#cwg1862-C-private_int {{implicitly declared private here}}
template<class T>
T A<T>::h() { (void)c.private_int; }
diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp
index 8162f9caa8f15..a3ab88e82bed3 100644
--- a/clang/test/CXX/drs/cwg19xx.cpp
+++ b/clang/test/CXX/drs/cwg19xx.cpp
@@ -101,19 +101,19 @@ 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
+ static int x; // #cwg1918-X-x
template <typename T>
friend class A<T>::B::C;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
+ // expected-error at -1 {{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@#cwg1918-X-x {{implicitly declared private here}}
};
};
} // namespace cwg1918
@@ -167,10 +167,9 @@ template<typename T> struct A {
};
class X {
static int x;
- // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id
template <typename T>
friend class A<T>::B::C;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}}
+ // expected-error at -1 {{friend declaration does not name a member of a class template specialization}}
};
} // namespace cwg1945
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 02c8f30249683..e574a5f48c507 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -183,12 +183,13 @@ 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.
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/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp
index 3ba2b372cb715..68ac7e2346727 100644
--- a/clang/test/CXX/drs/cwg6xx.cpp
+++ b/clang/test/CXX/drs/cwg6xx.cpp
@@ -407,26 +407,29 @@ namespace cwg638 { // cwg638: no
};
class X {
- typedef int type;
+ typedef int type; // #cwg638-X-type
template<class T> friend struct A<T>::B;
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
template<class T> friend void A<T>::f();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
template<class T> friend void A<T>::g();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'X'}}
template<class T> friend void A<T>::C::h();
- // expected-warning at -1 {{dependent nested name specifier 'A<T>::C' for friend class declaration is not supported; turning off access control for 'X'}}
+ // expected-error at -1 {{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@#cwg638-X-type {{implicitly declared private here}}
struct B {
X::type b; // ok
};
- int f() { X::type c; } // FIXME: private
+ int f() { X::type c; }
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
void g() { X::type d; } // ok
struct D {
- void h() { X::type e; } // FIXME: private
+ void h() { X::type e; }
+ // expected-error at -1 {{'type' is a private member of 'cwg638::X'}}
+ // expected-note@#cwg638-X-type {{implicitly declared private here}}
};
};
} // namespace cwg638
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp
index a292d0de97a39..ae754c5990972 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,30 @@ namespace test1 {
};
}
-// FIXME: these should fail!
namespace test2 {
template <class T> struct A;
class C {
- static void foo();
- template <class T> friend void A<T>::g(); // expected-warning {{not supported}}
+ static void foo(); // #test2-C-foo
+ template <class T> friend void A<T>::g();
};
template <class T> struct A {
void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
template <class T> struct A<T*> {
void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
template <> struct A<char> {
void f() { C::foo(); }
+ // expected-error at -1 {{'foo' is a private member of 'test2::C'}}
+ // expected-note@#test2-C-foo {{implicitly declared private here}}
};
}
@@ -66,7 +71,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 +86,264 @@ 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 at -1 {{friend declaration does not name a member of a class template specialization}}
+ };
+}
+
+namespace test8 {
+ template <class T> struct A { // #test8-A
+ T h();
+ };
+
+ template <> struct A<int> {
+ int h();
+ };
+
+ template <> struct A<float *> {
+ int *h();
+ };
+
+ class C {
+ int n; // #test8-C-n
+ template <class T> friend int *A<T *>::h();
+ };
+
+ template <class T> T A<T>::h() {
+ return T();
+ }
+
+ int A<int>::h() {
+ C c;
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test8::C'}}
+ // expected-note@#test8-C-n {{implicitly declared private here}}
+ // expected-note@#test8-A {{candidate friend template ignored: could not match 'T *' against 'int'}}
+ return 0;
+ }
+
+ template <> int *A<int *>::h() {
+ C c;
+ c.n = 0;
+ return nullptr;
+ }
+
+ int *A<float *>::h() {
+ C c;
+ c.n = 0;
+ return nullptr;
+ }
+
+ int *t1 = A<int *>().h();
+ int *t2 = A<float *>().h();
+ int t3 = A<int>().h();
+}
+
+namespace test9 {
+ template <class T> struct A {
+ template <T U> T i();
+ };
+
+ template <> struct A<int> {
+ template <int U> int i();
+ };
+
+ struct C {
+ int n;
+ template <class T> template <T U> friend T A<T>::i();
+ };
+
+ template <class T> template <T U> T A<T>::i() {
+ C c;
+ c.n = 0;
+ return U;
+ }
+
+ template <int U> int A<int>::i() {
+ C c;
+ c.n = 0;
+ return U;
+ }
+
+ int x = A<int>().i<1>();
+}
+
+namespace test10 {
+ template <class T> struct A;
+ class C {
+ static void foo(); // #test10-C-foo
+ template <class T> friend void A<T>::f();
+ };
+
+ template <class T> struct A {
+ void f() { C::foo(); }
+ };
+
+ template <> struct A<int> {
+ int f() {
+ C::foo();
+ // expected-error at -1 {{'foo' is a private member of 'test10::C'}}
+ // expected-note@#test10-C-foo {{implicitly declared private here}}
+ return 0;
+ }
+ };
+}
+
+namespace test11 {
+ template <class> struct C;
+ template <class T> struct A {
+ template <class> struct B;
+ };
+ template <class T> struct D : A<T> {
+ using A<T>::B;
+ };
+
+ template <class T> struct C {
+ int n;
+ template <class U> friend struct D<T>::B;
+ };
+
+ template <> template <class U> struct A<int>::B {
+ static int f(C<int> &c) {
+ c.n = 0;
+ return 0;
+ }
+ };
+
+ int x = A<int>::B<void>::f(*new C<int>);
+}
+
+namespace test12 {
+ template <class T> struct A {
+ template <T> struct B {
+ static int f();
+ };
+ };
+
+ template <class T> struct C {
+ int n;
+ template <class U> template <U V> friend struct A<U>::B;
+ };
+
+ template <class T> template <T V> int A<T>::B<V>::f() {
+ C<T> c;
+ c.n = 0;
+ return 0;
+ }
+
+ int x = A<int>::B<0>::f();
+}
+
+namespace test13 {
+ template <typename T> struct S {
+ template <typename> friend class T::template 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>);
+}
+
+namespace test15 {
+ template <class T> struct A {
+ T f();
+ };
+
+ template <> struct A<int> {
+ void f();
+ };
+
+ class C {
+ int n; // #test15-C-n
+ template <class T> friend T A<T>::f();
+ };
+
+ void A<int>::f() {
+ C c;
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test15::C'}}
+ // expected-note@#test15-C-n {{implicitly declared private here}}
+ }
+}
+
+namespace test16 {
+ template <class T> struct A {
+ template <T U> T i();
+ };
+
+ template <> struct A<int> {
+ template <int U> void i();
+ };
+
+ class C {
+ int n; // #test16-C-n
+ template <class T> template <T U> friend T A<T>::i();
+ };
+
+ template <int U> void A<int>::i() {
+ C c;
+ c.n = 0;
+ // expected-error at -1 {{'n' is a private member of 'test16::C'}}
+ // expected-note@#test16-C-n {{implicitly declared private here}}
+ }
+}
diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
new file mode 100644
index 0000000000000..f60f3dd42445c
--- /dev/null
+++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template <class T> struct A;
+template <class T> struct B {
+ void f();
+};
+
+void t1() {
+ struct S {
+ template <class T> friend void f();
+ // expected-error at -1 {{templates can only be declared in namespace or class scope}}
+ };
+}
+
+void t2() {
+ struct S {
+ template <class T> friend struct A;
+ // expected-error at -1 {{templates cannot be declared inside of a local class}}
+ };
+}
+
+void t3() {
+ struct S {
+ template <class T> friend void B<T>::f();
+ // expected-error at -1 {{templates cannot be declared inside of a local class}}
+ };
+}
diff --git a/clang/test/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/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp
index f98005c7e6fb5..ad6de9ee5b25e 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,9 +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-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); // #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 daec9410e547a..c5c5d13036233 100644
--- a/clang/test/SemaTemplate/GH71595.cpp
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -18,17 +18,21 @@ void f() {
template<class A>
class temp {
template<C<temp> T>
- friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
+ friend void g();
+ // expected-error at -1 {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
- temp();
+ temp(); // #temp-ctor
};
template<C<temp<int>> T>
void g() {
auto v = temp<T>();
+ // expected-error at -1 {{calling a private constructor of class 'temp<int>'}}
+ // expected-note@#temp-ctor {{implicitly declared private here}}
}
void h() {
f<int>();
g<int>();
+ // expected-note at -1 {{in instantiation of function template specialization 'g<int>' requested here}}
}
diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
index 11287aa773b1b..747ebd3246b47 100644
--- a/clang/test/SemaTemplate/concepts-friends.cpp
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -566,3 +566,61 @@ 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> struct E {
+ template <X U> // #DependentFriends_E_TPL
+ static void f() // #DependentFriends_E_f
+ requires X<T>;
+};
+
+class F {
+ static int n;
+ template <X T> template <X U>
+ friend void E<T>::f()
+ requires X<T>;
+};
+
+template <class T>
+void C<T>::f() requires X<T> {
+ D::n = 0;
+}
+
+template <class T>
+template <X U>
+void E<T>::f() requires X<T> {
+ F::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'}}
+
+ E<A>::f<A>();
+ E<A>::f<B>();
+ // expected-error at -1 {{no matching function for call to 'f'}}
+ // expected-note@#DependentFriends_E_f {{candidate template ignored: constraints not satisfied}}
+ // expected-note@#DependentFriends_E_TPL {{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'}}
+}
+}
diff --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp
index 7a8a72a62e992..2681ee2a6081e 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -19,27 +19,31 @@ namespace pr41427 {
namespace Access {
struct B {
protected:
- struct type {};
+ struct type {}; // #Access-B-type
};
- template<typename T> struct D : B { // expected-note {{not viable}} \
- expected-note {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
- D(T, typename T::type); // expected-note {{private member}} \
- // expected-note {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
+ template<typename T> struct D : B { // #Access-D
+ D(T, typename T::type); // #Access-D-ctor
+ // expected-error at -1 {{'type' is a private member of 'Access::Y'}}
+ // expected-note@#Access-Y-type {{implicitly declared private here}}
+ // expected-note@#Access-D-ctor {{implicit deduction guide declared as 'template <typename T> D(T, typename T::type) -> Access::D<T>'}}
};
D b = {B(), {}};
class X {
using type = int;
};
- D x = {X(), {}}; // expected-error {{no viable constructor or deduction guide}}
+ D x = {X(), {}};
+ // expected-error at -1 {{no viable constructor or deduction guide}}
+ // expected-note@#Access-D {{implicit deduction guide declared as 'template <typename T> D(Access::D<T>) -> Access::D<T>'}}
+ // expected-note@#Access-D {{candidate function template not viable: requires 1 argument, but 2 were provided}}
+ // expected-note@#Access-D-ctor {{candidate template ignored: substitution failure [with T = X]: 'type' is a private member of 'Access::X'}}
- // Once we implement proper support for dependent nested name specifiers in
- // friends, this should still work.
class Y {
- template <typename T> friend D<T>::D(T, typename T::type); // expected-warning {{dependent nested name specifier}}
- struct type {};
+ template <typename T> friend D<T>::D(T, typename T::type);
+ struct type {}; // #Access-Y-type
};
D y = {Y(), {}};
+ // expected-note at -1 {{in instantiation of template class 'Access::D<Access::Y>' requested here}}
class Z {
template <typename T> friend class D;
diff --git a/clang/test/SemaTemplate/friend-template.cpp b/clang/test/SemaTemplate/friend-template.cpp
index 2b5a226c3b33c..4a8f23a56dd22 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 {
@@ -338,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 friend template ignored: could not match 'U *' against 'double'}}
+ }
+};
+
+void test() {
+ A<double>::B<int>::g(); // expected-note {{in instantiation of member function 'GH104057::A<double>::B<int>::g' requested here}}
+}
+}
More information about the cfe-commits
mailing list