https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/191268
>From 8d30639cfaf8910e7a4052b8dfc2bbf38e686332 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Thu, 9 Apr 2026 18:04:29 +0300 Subject: [PATCH] [Clang] support friend declarations with a dependent nested-name-specifier --- clang/include/clang/AST/DeclFriend.h | 117 ++----- clang/include/clang/AST/DeclTemplate.h | 67 ++-- clang/include/clang/AST/RecursiveASTVisitor.h | 5 +- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/Sema.h | 29 +- clang/include/clang/Sema/Template.h | 3 +- clang/lib/AST/ASTImporter.cpp | 105 +++++- clang/lib/AST/ASTStructuralEquivalence.cpp | 18 + clang/lib/AST/DeclFriend.cpp | 64 ++-- clang/lib/AST/DeclPrinter.cpp | 17 +- clang/lib/AST/DeclTemplate.cpp | 39 ++- clang/lib/Sema/Sema.cpp | 5 +- clang/lib/Sema/SemaAccess.cpp | 331 +++++++++++++++--- clang/lib/Sema/SemaDeclCXX.cpp | 209 +++++++---- clang/lib/Sema/SemaTemplate.cpp | 24 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 4 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 99 ++++-- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 115 +++--- clang/lib/Serialization/ASTReaderDecl.cpp | 14 +- clang/lib/Serialization/ASTWriterDecl.cpp | 12 +- .../class.access/class.friend/p3-cxx0x.cpp | 4 +- clang/test/CXX/drs/cwg18xx.cpp | 11 +- clang/test/CXX/drs/cwg19xx.cpp | 8 +- clang/test/CXX/drs/cwg6xx.cpp | 17 +- .../CXX/temp/temp.decls/temp.friend/p5.cpp | 151 +++++++- .../CXX/temp/temp.decls/temp.friend/p6.cpp | 24 ++ .../SemaCXX/many-template-parameter-lists.cpp | 6 +- clang/test/SemaTemplate/GH71595.cpp | 6 +- clang/test/SemaTemplate/ctad.cpp | 2 +- 29 files changed, 1044 insertions(+), 464 deletions(-) create mode 100644 clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp diff --git a/clang/include/clang/AST/DeclFriend.h b/clang/include/clang/AST/DeclFriend.h index 1f8c210263677..2cd5a1af17fd8 100644 --- a/clang/include/clang/AST/DeclFriend.h +++ b/clang/include/clang/AST/DeclFriend.h @@ -15,20 +15,14 @@ #define LLVM_CLANG_AST_DECLFRIEND_H #include "clang/AST/Decl.h" -#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/ExternalASTSource.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" -#include "llvm/Support/TrailingObjects.h" #include <cassert> -#include <iterator> namespace clang { @@ -49,9 +43,7 @@ class ASTContext; /// @endcode /// /// The semantic context of a friend decl is its declaring class. -class FriendDecl final - : public Decl, - private llvm::TrailingObjects<FriendDecl, TemplateParameterList *> { +class FriendDecl : public Decl { LLVM_DECLARE_VIRTUAL_ANCHOR_FUNCTION(); public: @@ -61,46 +53,35 @@ class FriendDecl final friend class CXXRecordDecl; friend class CXXRecordDecl::friend_iterator; - // The declaration that's a friend of this class. - FriendUnion Friend; - - // A pointer to the next friend in the sequence. - LazyDeclPtr NextFriend; - - // Location of the 'friend' specifier. - SourceLocation FriendLoc; - // Location of the '...', if present. SourceLocation EllipsisLoc; + SourceLocation FriendLoc; + /// True if this 'friend' declaration is unsupported. Eventually we /// will support every possible friend declaration, but for now we /// silently ignore some and set this flag to authorize all access. LLVM_PREFERRED_TYPE(bool) unsigned UnsupportedFriend : 1; - // The number of "outer" template parameter lists in non-templatic - // (currently unsupported) friend type declarations, such as - // template <class T> friend class A<T>::B; - unsigned NumTPLists : 31; - - FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend, - SourceLocation FriendL, SourceLocation EllipsisLoc, - ArrayRef<TemplateParameterList *> FriendTypeTPLists) - : Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL), - EllipsisLoc(EllipsisLoc), UnsupportedFriend(false), - NumTPLists(FriendTypeTPLists.size()) { - llvm::copy(FriendTypeTPLists, getTrailingObjects()); - } +protected: + // The declaration that's a friend of this class. + FriendUnion Friend; - FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists) - : Decl(Decl::Friend, Empty), UnsupportedFriend(false), - NumTPLists(NumFriendTypeTPLists) {} + LazyDeclPtr NextFriend; + + FriendDecl(Kind K, DeclContext *DC, SourceLocation L, FriendUnion Friend, + SourceLocation FriendL, SourceLocation EllipsisLoc = {}) + : Decl(K, DC, L), EllipsisLoc(EllipsisLoc), FriendLoc(FriendL), + UnsupportedFriend(false), Friend(Friend), NextFriend() {} + + FriendDecl(Kind K, EmptyShell Empty) + : Decl(K, Empty), UnsupportedFriend(false) {} FriendDecl *getNextFriend() { - if (!NextFriend.isOffset()) - return cast_or_null<FriendDecl>(NextFriend.get(nullptr)); - return getNextFriendSlowCase(); + if (NextFriend.isOffset()) + return getNextFriendSlowCase(); + return cast_or_null<FriendDecl>(NextFriend.get(nullptr)); } FriendDecl *getNextFriendSlowCase(); @@ -109,14 +90,11 @@ class FriendDecl final friend class ASTDeclReader; friend class ASTDeclWriter; friend class ASTNodeImporter; - friend TrailingObjects; - static FriendDecl * - Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend_, - SourceLocation FriendL, SourceLocation EllipsisLoc = {}, - ArrayRef<TemplateParameterList *> FriendTypeTPLists = {}); - static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID, - unsigned FriendTypeNumTPLists); + static FriendDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, + FriendUnion Friend_, SourceLocation FriendL, + SourceLocation EllipsisLoc = {}); + static FriendDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); /// If this friend declaration names an (untemplated but possibly /// dependent) type, return the type; otherwise return null. This @@ -126,58 +104,18 @@ class FriendDecl final return Friend.dyn_cast<TypeSourceInfo*>(); } - unsigned getFriendTypeNumTemplateParameterLists() const { - return NumTPLists; - } - - TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const { - return getTrailingObjects(NumTPLists)[N]; - } - /// If this friend declaration doesn't name a type, return the inner /// declaration. NamedDecl *getFriendDecl() const { return Friend.dyn_cast<NamedDecl *>(); } - /// Retrieves the location of the 'friend' keyword. - SourceLocation getFriendLoc() const { - return FriendLoc; - } - /// Retrieves the location of the '...', if present. SourceLocation getEllipsisLoc() const { return EllipsisLoc; } - /// Retrieves the source range for the friend declaration. - SourceRange getSourceRange() const override LLVM_READONLY { - if (TypeSourceInfo *TInfo = getFriendType()) { - SourceLocation StartL = (NumTPLists == 0) - ? getFriendLoc() - : getTrailingObjects()[0]->getTemplateLoc(); - SourceLocation EndL = isPackExpansion() ? getEllipsisLoc() - : TInfo->getTypeLoc().getEndLoc(); - return SourceRange(StartL, EndL); - } - - if (isPackExpansion()) - return SourceRange(getFriendLoc(), getEllipsisLoc()); - - if (NamedDecl *ND = getFriendDecl()) { - if (const auto *FD = dyn_cast<FunctionDecl>(ND)) - return FD->getSourceRange(); - if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND)) - return FTD->getSourceRange(); - if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND)) - return CTD->getSourceRange(); - if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) { - if (DD->getOuterLocStart() != DD->getInnerLocStart()) - return DD->getSourceRange(); - } - return SourceRange(getFriendLoc(), ND->getEndLoc()); - } - - return SourceRange(getFriendLoc(), getLocation()); - } + SourceLocation getFriendLoc() const { return FriendLoc; } + + SourceRange getSourceRange() const override LLVM_READONLY; /// Determines if this friend kind is unsupported. bool isUnsupportedFriend() const { @@ -191,9 +129,10 @@ class FriendDecl final // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } - static bool classofKind(Kind K) { return K == Decl::Friend; } + static bool classofKind(Kind K) { + return K == Decl::Friend || K == Decl::FriendTemplate; + } }; - /// An iterator over the friend declarations of a class. class CXXRecordDecl::friend_iterator { friend class CXXRecordDecl; diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index a4a1bb9c13c79..1653c26910b8b 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -19,6 +19,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclFriend.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Redeclarable.h" #include "clang/AST/TemplateBase.h" @@ -2473,42 +2474,43 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl { /// /// \note This class is not currently in use. All of the above /// will yield a FriendDecl, not a FriendTemplateDecl. -class FriendTemplateDecl : public Decl { - virtual void anchor(); - -public: - using FriendUnion = llvm::PointerUnion<NamedDecl *,TypeSourceInfo *>; +class FriendTemplateDecl final + : public FriendDecl, + private llvm::TrailingObjects<FriendTemplateDecl, + TemplateParameterList *> { + void anchor() override; private: - // The number of template parameters; always non-zero. - unsigned NumParams = 0; + unsigned NumTPLists : 31; - // The parameter list. - TemplateParameterList **Params = nullptr; - - // The declaration that's a friend of this class. - FriendUnion Friend; - - // Location of the 'friend' specifier. - SourceLocation FriendLoc; - - FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, - TemplateParameterList **Params, unsigned NumParams, - FriendUnion Friend, SourceLocation FriendLoc) - : Decl(Decl::FriendTemplate, DC, Loc), NumParams(NumParams), - Params(Params), Friend(Friend), FriendLoc(FriendLoc) {} + FriendTemplateDecl(DeclContext *DC, SourceLocation Loc, FriendUnion Friend, + SourceLocation FriendLoc, SourceLocation EllipsisLoc, + ArrayRef<TemplateParameterList *> FriendTypeTPLists) + : FriendDecl(Decl::FriendTemplate, DC, Loc, Friend, FriendLoc, + EllipsisLoc), + NumTPLists(FriendTypeTPLists.size()) { + llvm::copy(FriendTypeTPLists, getTrailingObjects()); + } - FriendTemplateDecl(EmptyShell Empty) : Decl(Decl::FriendTemplate, Empty) {} + FriendTemplateDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists) + : FriendDecl(Decl::FriendTemplate, Empty), + NumTPLists(NumFriendTypeTPLists) {} public: friend class ASTDeclReader; + friend class ASTDeclWriter; + friend TrailingObjects; static FriendTemplateDecl * Create(ASTContext &Context, DeclContext *DC, SourceLocation Loc, - MutableArrayRef<TemplateParameterList *> Params, FriendUnion Friend, - SourceLocation FriendLoc); + FriendUnion Friend, SourceLocation FriendLoc, + ArrayRef<TemplateParameterList *> FriendTypeTPLists = {}, + SourceLocation EllipsisLoc = {}); - static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); + static FriendTemplateDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID, + unsigned FriendTypeNumTPLists); + + SourceRange getSourceRange() const override LLVM_READONLY; /// If this friend declaration names a templated type (or /// a dependent member type of a templated type), return that @@ -2524,19 +2526,12 @@ class FriendTemplateDecl : public Decl { return Friend.dyn_cast<NamedDecl*>(); } - /// Retrieves the location of the 'friend' keyword. - SourceLocation getFriendLoc() const { - return FriendLoc; + TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const { + assert(N < NumTPLists); + return getTrailingObjects()[N]; } - TemplateParameterList *getTemplateParameterList(unsigned i) const { - assert(i <= NumParams); - return Params[i]; - } - - unsigned getNumTemplateParameters() const { - return NumParams; - } + unsigned getFriendTypeNumTemplateParameterLists() const { return NumTPLists; } // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index ce6ad723191e0..c06ceec13b4dd 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1727,8 +1727,9 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, { TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc())); else TRY_TO(TraverseDecl(D->getFriendDecl())); - for (unsigned I = 0, E = D->getNumTemplateParameters(); I < E; ++I) { - TemplateParameterList *TPL = D->getTemplateParameterList(I); + for (unsigned I = 0, E = D->getFriendTypeNumTemplateParameterLists(); I < E; + ++I) { + TemplateParameterList *TPL = D->getFriendTypeTemplateParameterList(I); for (TemplateParameterList::iterator ITPL = TPL->begin(), ETPL = TPL->end(); ITPL != ETPL; ++ITPL) { TRY_TO(TraverseDecl(*ITPL)); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6d2fae551566f..9835c66c71816 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1921,6 +1921,8 @@ def err_friend_template_decl_multiple_specifiers: Error< "a friend declaration that befriends a template must contain exactly one type-specifier">; def friend_template_decl_malformed_pack_expansion : Error< "friend declaration expands pack %0 that is declared it its own template parameter list">; +def err_dependent_friend_not_member : Error< + "friend declaration does not name a member of a class template specialization">; def err_invalid_base_in_interface : Error< "interface type cannot inherit from " diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 760555d9c8b9b..7fee5c399fed2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1772,7 +1772,7 @@ class Sema final : public SemaBase { bool ForceCheck = false, bool ForceUnprivileged = false); /// Checks access to all the declarations in the given result set. - void CheckLookupAccess(const LookupResult &R); + void CheckLookupAccess(const LookupResult &R, DeclContext *DC = nullptr); /// Checks access to Target from the given class. The check will take access /// specifiers into account, but no member access expressions and such. @@ -12012,6 +12012,9 @@ class Sema final : public SemaBase { bool CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous); void CompleteMemberSpecialization(NamedDecl *Member, LookupResult &Previous); + bool CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS, + TemplateParameterList *FPL); + // Explicit instantiation of a class template specialization DeclResult ActOnExplicitInstantiation( Scope *S, SourceLocation ExternLoc, SourceLocation TemplateLoc, @@ -12388,7 +12391,8 @@ class Sema final : public SemaBase { SourceLocation KeywordLoc, NestedNameSpecifierLoc QualifierLoc, const IdentifierInfo &II, SourceLocation IILoc, - bool DeducedTSTContext = true); + bool DeducedTSTContext = true, + DeclContext *DC = nullptr); /// Rebuilds a type within the context of the current instantiation. /// @@ -13823,15 +13827,27 @@ class Sema final : public SemaBase { const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity, bool AllowDeducedTST = false); + TypeSourceInfo *SubstType(TypeSourceInfo *T, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation Loc, DeclarationName Entity, + DeclContext *DC, bool AllowDeducedTST = false); QualType SubstType(QualType T, const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity, bool *IsIncompleteSubstitution = nullptr); + QualType SubstType(QualType T, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation Loc, DeclarationName Entity, + DeclContext *DC, bool *IsIncompleteSubstitution = nullptr); TypeSourceInfo *SubstType(TypeLoc TL, const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity); + TypeSourceInfo *SubstType(TypeLoc TL, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation Loc, DeclarationName Entity, + DeclContext *DC); /// A form of SubstType intended specifically for instantiating the /// type of a FunctionDecl. Its purpose is solely to force the @@ -13840,7 +13856,8 @@ class Sema final : public SemaBase { TypeSourceInfo *SubstFunctionDeclType( TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext, - Qualifiers ThisTypeQuals, bool EvaluateConstraints = true); + Qualifiers ThisTypeQuals, DeclContext *DC = nullptr, + bool EvaluateConstraints = true); void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, const MultiLevelTemplateArgumentList &Args); bool SubstExceptionSpec(SourceLocation Loc, @@ -13851,7 +13868,8 @@ class Sema final : public SemaBase { SubstParmVarDecl(ParmVarDecl *D, const MultiLevelTemplateArgumentList &TemplateArgs, int indexAdjustment, UnsignedOrNone NumExpansions, - bool ExpectParameterPack, bool EvaluateConstraints = true); + bool ExpectParameterPack, DeclContext *DC = nullptr, + bool EvaluateConstraints = true); /// Substitute the given template arguments into the given set of /// parameters, producing the set of parameter types that would be generated @@ -13861,7 +13879,8 @@ class Sema final : public SemaBase { const MultiLevelTemplateArgumentList &TemplateArgs, SmallVectorImpl<QualType> &ParamTypes, SmallVectorImpl<ParmVarDecl *> *OutParams, - ExtParameterInfoBuilder &ParamInfos); + ExtParameterInfoBuilder &ParamInfos, + DeclContext *DC = nullptr); /// Substitute the given template arguments into the default argument. bool SubstDefaultArgument(SourceLocation Loc, ParmVarDecl *Param, diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index b0170c21feb1a..26268f1e04f07 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -715,7 +715,8 @@ enum class TemplateSubstitutionKind : char { // Helper functions for instantiating methods. TypeSourceInfo *SubstFunctionType(FunctionDecl *D, - SmallVectorImpl<ParmVarDecl *> &Params); + SmallVectorImpl<ParmVarDecl *> &Params, + DeclContext *DC = nullptr); bool InitFunctionInstantiation(FunctionDecl *New, FunctionDecl *Tmpl); bool InitMethodInstantiation(CXXMethodDecl *New, CXXMethodDecl *Tmpl); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 41ba98c53247d..7fc5b830638cc 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -537,6 +537,7 @@ namespace clang { ExpectedDecl VisitFieldDecl(FieldDecl *D); ExpectedDecl VisitIndirectFieldDecl(IndirectFieldDecl *D); ExpectedDecl VisitFriendDecl(FriendDecl *D); + ExpectedDecl VisitFriendTemplateDecl(FriendTemplateDecl *D); ExpectedDecl VisitObjCIvarDecl(ObjCIvarDecl *D); ExpectedDecl VisitVarDecl(VarDecl *D); ExpectedDecl VisitImplicitParamDecl(ImplicitParamDecl *D); @@ -4545,6 +4546,24 @@ static bool IsEquivalentFriend(ASTImporter &Importer, FriendDecl *FD1, return Ctx.IsEquivalent(FD1, FD2); } +static bool IsEquivalentFriend(ASTImporter &Importer, FriendTemplateDecl *FTD1, + FriendTemplateDecl *FTD2) { + if ((!FTD1->getFriendType()) != (!FTD2->getFriendType())) + return false; + + if (const TypeSourceInfo *TSI = FTD1->getFriendType()) + return Importer.IsStructurallyEquivalent( + TSI->getType(), FTD2->getFriendType()->getType(), /*Complain=*/false); + + ASTImporter::NonEquivalentDeclSet NonEquivalentDecls; + StructuralEquivalenceContext Ctx( + Importer.getToContext().getLangOpts(), FTD1->getASTContext(), + FTD2->getASTContext(), NonEquivalentDecls, + StructuralEquivalenceKind::Default, /* StrictTypeSpelling = */ false, + /* Complain = */ false); + return Ctx.IsEquivalent(FTD1, FTD2); +} + static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer, FriendDecl *FD) { unsigned int FriendCount = 0; @@ -4561,7 +4580,6 @@ static FriendCountAndPosition getFriendCountAndPosition(ASTImporter &Importer, } assert(FriendPosition && "Friend decl not found in own parent."); - return {FriendCount, *FriendPosition}; } @@ -4576,9 +4594,11 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { // We try to maintain order and count of redundant friend declarations. const auto *RD = cast<CXXRecordDecl>(DC); SmallVector<FriendDecl *, 2> ImportedEquivalentFriends; - for (FriendDecl *ImportedFriend : RD->friends()) - if (IsEquivalentFriend(Importer, D, ImportedFriend)) - ImportedEquivalentFriends.push_back(ImportedFriend); + for (FriendDecl *ImportedFriend : RD->friends()) { + if (ImportedFriend->getKind() == Decl::Friend && + IsEquivalentFriend(Importer, D, cast<FriendDecl>(ImportedFriend))) + ImportedEquivalentFriends.push_back(cast<FriendDecl>(ImportedFriend)); + } FriendCountAndPosition CountAndPosition = getFriendCountAndPosition(Importer, D); @@ -4609,15 +4629,6 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { return TSIOrErr.takeError(); } - SmallVector<TemplateParameterList *, 1> ToTPLists(D->NumTPLists); - auto **FromTPLists = D->getTrailingObjects(); - for (unsigned I = 0; I < D->NumTPLists; I++) { - if (auto ListOrErr = import(FromTPLists[I])) - ToTPLists[I] = *ListOrErr; - else - return ListOrErr.takeError(); - } - auto LocationOrErr = import(D->getLocation()); if (!LocationOrErr) return LocationOrErr.takeError(); @@ -4631,7 +4642,7 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { FriendDecl *FrD; if (GetImportedOrCreateDecl(FrD, D, Importer.getToContext(), DC, *LocationOrErr, ToFU, *FriendLocOrErr, - *EllipsisLocOrErr, ToTPLists)) + *EllipsisLocOrErr)) return FrD; FrD->setAccess(D->getAccess()); @@ -4640,6 +4651,72 @@ ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { return FrD; } +ExpectedDecl ASTNodeImporter::VisitFriendTemplateDecl(FriendTemplateDecl *D) { + DeclContext *DC, *LexicalDC; + if (Error Err = ImportDeclContext(D, DC, LexicalDC)) + return std::move(Err); + + const auto *RD = cast<CXXRecordDecl>(DC); + SmallVector<FriendTemplateDecl *, 2> ImportedEquivalentFriends; + for (FriendDecl *ImportedFriend : RD->friends()) { + if (isa<FriendTemplateDecl>(ImportedFriend) && + IsEquivalentFriend(Importer, D, + cast<FriendTemplateDecl>(ImportedFriend))) + ImportedEquivalentFriends.push_back( + cast<FriendTemplateDecl>(ImportedFriend)); + } + + FriendCountAndPosition CountAndPosition = + getFriendCountAndPosition(Importer, D); + assert(ImportedEquivalentFriends.size() <= CountAndPosition.TotalCount && + "Class with non-matching friends is imported, ODR check wrong?"); + + if (ImportedEquivalentFriends.size() == CountAndPosition.TotalCount) + return Importer.MapImported( + D, ImportedEquivalentFriends[CountAndPosition.IndexOfDecl]); + + FriendTemplateDecl::FriendUnion ToFU; + if (NamedDecl *FriendD = D->getFriendDecl()) { + NamedDecl *ToFriendD; + if (Error Err = importInto(ToFriendD, FriendD)) + return std::move(Err); + ToFU = ToFriendD; + } else { + if (auto TSIOrErr = import(D->getFriendType())) + ToFU = *TSIOrErr; + else + return TSIOrErr.takeError(); + } + + SmallVector<TemplateParameterList *, 1> ToParams( + D->getFriendTypeNumTemplateParameterLists()); + for (unsigned I = 0, N = D->getFriendTypeNumTemplateParameterLists(); I != N; + ++I) { + if (auto ParamsOrErr = import(D->getFriendTypeTemplateParameterList(I))) + ToParams[I] = *ParamsOrErr; + else + return ParamsOrErr.takeError(); + } + + auto LocationOrErr = import(D->getLocation()); + if (!LocationOrErr) + return LocationOrErr.takeError(); + + auto FriendLocOrErr = import(D->getFriendLoc()); + if (!FriendLocOrErr) + return FriendLocOrErr.takeError(); + + FriendTemplateDecl *FTD; + if (GetImportedOrCreateDecl(FTD, D, Importer.getToContext(), DC, + *LocationOrErr, ToFU, *FriendLocOrErr, ToParams)) + 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 9d970651a9e65..9c7198e5c42ec 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -2430,6 +2430,24 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; } +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + FriendTemplateDecl *FTD1, + FriendTemplateDecl *FTD2) { + if ((FTD1->getFriendType() && FTD2->getFriendDecl()) || + (FTD1->getFriendDecl() && FTD2->getFriendType())) + return false; + + if (FTD1->getFriendDecl() && FTD2->getFriendDecl()) + return IsStructurallyEquivalent(Context, FTD1->getFriendDecl(), + FTD2->getFriendDecl()); + + if (FTD1->getFriendType() && FTD2->getFriendType()) + return IsStructurallyEquivalent(Context, FTD1->getFriendType()->getType(), + FTD2->getFriendType()->getType()); + + return false; +} + static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, TypedefNameDecl *D1, TypedefNameDecl *D2) { if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier())) diff --git a/clang/lib/AST/DeclFriend.cpp b/clang/lib/AST/DeclFriend.cpp index 6bfc2eb62b284..aea0600441d25 100644 --- a/clang/lib/AST/DeclFriend.cpp +++ b/clang/lib/AST/DeclFriend.cpp @@ -13,11 +13,9 @@ #include "clang/AST/DeclFriend.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" -#include "clang/Basic/LLVM.h" +#include "clang/AST/ExternalASTSource.h" #include <cassert> #include <cstddef> @@ -25,16 +23,9 @@ using namespace clang; void FriendDecl::anchor() {} -FriendDecl *FriendDecl::getNextFriendSlowCase() { - return cast_or_null<FriendDecl>( - NextFriend.get(getASTContext().getExternalSource())); -} - -FriendDecl * -FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, - FriendUnion Friend, SourceLocation FriendL, - SourceLocation EllipsisLoc, - ArrayRef<TemplateParameterList *> FriendTypeTPLists) { +FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, + FriendUnion Friend, SourceLocation FriendL, + SourceLocation EllipsisLoc) { #ifndef NDEBUG if (const auto *D = dyn_cast<NamedDecl *>(Friend)) { assert(isa<FunctionDecl>(D) || @@ -46,25 +37,22 @@ FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, // to the original declaration when instantiating members. assert(D->getFriendObjectKind() || (cast<CXXRecordDecl>(DC)->getTemplateSpecializationKind())); - // These template parameters are for friend types only. - assert(FriendTypeTPLists.empty()); } #endif - std::size_t Extra = - FriendDecl::additionalSizeToAlloc<TemplateParameterList *>( - FriendTypeTPLists.size()); - auto *FD = new (C, DC, Extra) - FriendDecl(DC, L, Friend, FriendL, EllipsisLoc, FriendTypeTPLists); + auto *FD = + new (C, DC) FriendDecl(Decl::Friend, DC, L, Friend, FriendL, EllipsisLoc); cast<CXXRecordDecl>(DC)->pushFriendDecl(FD); return FD; } -FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID, - unsigned FriendTypeNumTPLists) { - std::size_t Extra = - additionalSizeToAlloc<TemplateParameterList *>(FriendTypeNumTPLists); - return new (C, ID, Extra) FriendDecl(EmptyShell(), FriendTypeNumTPLists); +FriendDecl *FriendDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { + return new (C, ID) FriendDecl(Decl::Friend, EmptyShell()); +} + +FriendDecl *FriendDecl::getNextFriendSlowCase() { + return cast_or_null<FriendDecl>( + NextFriend.get(getASTContext().getExternalSource())); } FriendDecl *CXXRecordDecl::getFirstFriend() const { @@ -72,3 +60,29 @@ FriendDecl *CXXRecordDecl::getFirstFriend() const { Decl *First = data().FirstFriend.get(Source); return First ? cast<FriendDecl>(First) : nullptr; } + +SourceRange FriendDecl::getSourceRange() const { + if (TypeSourceInfo *TInfo = getFriendType()) { + SourceLocation EndL = + isPackExpansion() ? getEllipsisLoc() : TInfo->getTypeLoc().getEndLoc(); + return SourceRange(getFriendLoc(), EndL); + } + + if (isPackExpansion()) + return SourceRange(getFriendLoc(), getEllipsisLoc()); + + if (NamedDecl *ND = getFriendDecl()) { + if (const auto *FD = dyn_cast<FunctionDecl>(ND)) + return FD->getSourceRange(); + if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND)) + return FTD->getSourceRange(); + if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND)) + return CTD->getSourceRange(); + if (const auto *DD = dyn_cast<DeclaratorDecl>(ND)) { + if (DD->getOuterLocStart() != DD->getInnerLocStart()) + return DD->getSourceRange(); + } + return SourceRange(getFriendLoc(), ND->getEndLoc()); + } + return SourceRange(getFriendLoc(), getLocation()); +} diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 5e377a6c0c247..755f93e1ab22f 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -888,24 +888,17 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { void DeclPrinter::VisitFriendDecl(FriendDecl *D) { if (TypeSourceInfo *TSI = D->getFriendType()) { - unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists(); - for (unsigned i = 0; i < NumTPLists; ++i) - printTemplateParameters(D->getFriendTypeTemplateParameterList(i)); Out << "friend "; Out << TSI->getType().getAsString(Policy); - } - else if (FunctionDecl *FD = - dyn_cast<FunctionDecl>(D->getFriendDecl())) { + } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D->getFriendDecl())) { Out << "friend "; VisitFunctionDecl(FD); - } - else if (FunctionTemplateDecl *FTD = - dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) { + } else if (FunctionTemplateDecl *FTD = + dyn_cast<FunctionTemplateDecl>(D->getFriendDecl())) { Out << "friend "; VisitFunctionTemplateDecl(FTD); - } - else if (ClassTemplateDecl *CTD = - dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) { + } else if (ClassTemplateDecl *CTD = + dyn_cast<ClassTemplateDecl>(D->getFriendDecl())) { Out << "friend "; VisitRedeclarableTemplateDecl(CTD); } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 99d02fdc99e92..6c19c347d87fc 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1231,21 +1231,34 @@ void FriendTemplateDecl::anchor() {} FriendTemplateDecl * FriendTemplateDecl::Create(ASTContext &Context, DeclContext *DC, - SourceLocation L, - MutableArrayRef<TemplateParameterList *> Params, - FriendUnion Friend, SourceLocation FLoc) { - TemplateParameterList **TPL = nullptr; - if (!Params.empty()) { - TPL = new (Context) TemplateParameterList *[Params.size()]; - llvm::copy(Params, TPL); - } - return new (Context, DC) - FriendTemplateDecl(DC, L, TPL, Params.size(), Friend, FLoc); + SourceLocation Loc, FriendUnion Friend, + SourceLocation FriendLoc, + ArrayRef<TemplateParameterList *> FriendTypeTPLists, + SourceLocation EllipsisLoc) { + std::size_t Extra = + FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>( + FriendTypeTPLists.size()); + auto *FTD = new (Context, DC, Extra) FriendTemplateDecl( + DC, Loc, Friend, FriendLoc, EllipsisLoc, FriendTypeTPLists); + cast<CXXRecordDecl>(DC)->pushFriendDecl(FTD); + return FTD; } -FriendTemplateDecl *FriendTemplateDecl::CreateDeserialized(ASTContext &C, - GlobalDeclID ID) { - return new (C, ID) FriendTemplateDecl(EmptyShell()); +FriendTemplateDecl * +FriendTemplateDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID, + unsigned NumFriendTypeTPLists) { + std::size_t Extra = + FriendTemplateDecl::additionalSizeToAlloc<TemplateParameterList *>( + NumFriendTypeTPLists); + return new (C, ID, Extra) + FriendTemplateDecl(EmptyShell(), NumFriendTypeTPLists); +} + +SourceRange FriendTemplateDecl::getSourceRange() const { + SourceLocation Begin = + getFriendTypeTemplateParameterList(0)->getTemplateLoc(); + SourceLocation End = FriendDecl::getSourceRange().getEnd(); + return SourceRange(Begin, End); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index ef45d5842c795..27c99ef7691a6 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1163,8 +1163,9 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD, for (CXXRecordDecl::friend_iterator I = RD->friend_begin(), E = RD->friend_end(); I != E && Complete; ++I) { + FriendDecl *Friend = *I; // Check if friend classes and methods are complete. - if (TypeSourceInfo *TSI = (*I)->getFriendType()) { + if (TypeSourceInfo *TSI = Friend->getFriendType()) { // Friend classes are available as the TypeSourceInfo of the FriendDecl. if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl()) Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete); @@ -1173,7 +1174,7 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD, } else { // Friend functions are available through the NamedDecl of FriendDecl. if (const FunctionDecl *FD = - dyn_cast<FunctionDecl>((*I)->getFriendDecl())) + dyn_cast<FunctionDecl>(Friend->getFriendDecl())) Complete = FD->isDefined(); else // This is a template friend, give up. diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 17415b4185eff..1cd2710f79fe7 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -21,6 +21,7 @@ #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Template.h" using namespace clang; using namespace sema; @@ -274,6 +275,65 @@ struct AccessTarget : public AccessedEntity { } +static bool CanDeduceTemplateArguments(Sema &S, TemplateParameterList *TPL, + ArrayRef<TemplateArgument> PatternArgs, + ArrayRef<TemplateArgument> Args, + SourceLocation Loc) { + TemplateDeductionInfo Info(Loc); + SmallVector<DeducedTemplateArgument, 4> Deduced(TPL->size()); + S.DeduceTemplateArguments(TPL, PatternArgs, Args, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false); + + for (const DeducedTemplateArgument &Arg : Deduced) + if (Arg.isNull()) + return false; + + return true; +} + +static bool MatchesFriendContext(Sema &S, FunctionDecl *FD, + ClassTemplateDecl *FriendCTD, + ArrayRef<TemplateArgument> FriendArgs, + TemplateParameterList *FriendTPL, + SourceLocation Loc) { + const auto *RD = dyn_cast<CXXRecordDecl>(FD->getDeclContext()); + if (!RD) + return false; + + ClassTemplateDecl *ContextCTD = RD->getDescribedClassTemplate(); + ArrayRef<TemplateArgument> ContextArgs; + if (ContextCTD) { + ContextArgs = ContextCTD->getInjectedTemplateArgs(S.Context); + } else { + const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD); + if (!CTSD) + return false; + ContextCTD = CTSD->getSpecializedTemplate(); + ContextArgs = CTSD->getTemplateArgs().asArray(); + } + + if (ContextCTD->getCanonicalDecl() != FriendCTD->getCanonicalDecl()) + return false; + + return CanDeduceTemplateArguments(S, FriendTPL, FriendArgs, ContextArgs, Loc); +} + +static const TemplateSpecializationType * +TryGetTemplateSpecializationType(Sema &S, NestedNameSpecifier NNS) { + if (!NNS) + return nullptr; + + QualType Ty(NNS.getAsType(), 0); + if (Ty.isNull()) + return nullptr; + + Ty = S.Context.getCanonicalType(Ty); + if (const auto *ICNT = Ty->getAs<InjectedClassNameType>()) + Ty = ICNT->getDecl()->getCanonicalTemplateSpecializationType(S.Context); + + return Ty->getAs<TemplateSpecializationType>(); +} + /// Checks whether one class might instantiate to the other. static bool MightInstantiateTo(const CXXRecordDecl *From, const CXXRecordDecl *To) { @@ -283,8 +343,12 @@ static bool MightInstantiateTo(const CXXRecordDecl *From, const DeclContext *FromDC = From->getDeclContext()->getPrimaryContext(); const DeclContext *ToDC = To->getDeclContext()->getPrimaryContext(); - if (FromDC == ToDC) return true; - if (FromDC->isFileContext() || ToDC->isFileContext()) return false; + + if (FromDC == ToDC) + return true; + + if (FromDC->isFileContext() || ToDC->isFileContext()) + return false; // Be conservative. return true; @@ -342,7 +406,6 @@ static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived, return OnFailure; } - static bool MightInstantiateTo(Sema &S, DeclContext *Context, DeclContext *Friend) { if (Friend == Context) @@ -374,49 +437,49 @@ static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) return true; } -static bool MightInstantiateTo(Sema &S, - FunctionDecl *Context, - FunctionDecl *Friend) { - if (Context->getDeclName() != Friend->getDeclName()) +static bool MightInstantiateTo(Sema &S, CanQual<FunctionProtoType> Context, + CanQual<FunctionProtoType> Friend) { + if (Friend.getQualifiers() != Context.getQualifiers()) return false; - if (!MightInstantiateTo(S, - Context->getDeclContext(), - Friend->getDeclContext())) + if (Friend->getNumParams() != Context->getNumParams()) return false; - CanQual<FunctionProtoType> FriendTy - = S.Context.getCanonicalType(Friend->getType()) - ->getAs<FunctionProtoType>(); - CanQual<FunctionProtoType> ContextTy - = S.Context.getCanonicalType(Context->getType()) - ->getAs<FunctionProtoType>(); - - // There isn't any way that I know of to add qualifiers - // during instantiation. - if (FriendTy.getQualifiers() != ContextTy.getQualifiers()) + if (!MightInstantiateTo(S, Context->getReturnType(), Friend->getReturnType())) return false; - if (FriendTy->getNumParams() != ContextTy->getNumParams()) + for (unsigned I = 0, E = Friend->getNumParams(); I != E; ++I) + if (!MightInstantiateTo(S, Context->getParamType(I), + Friend->getParamType(I))) + return false; + + return true; +} + +static bool MightInstantiateTo(Sema &S, FunctionDecl *Context, + FunctionDecl *Friend) { + if (Context->getDeclName() != Friend->getDeclName()) return false; - if (!MightInstantiateTo(S, ContextTy->getReturnType(), - FriendTy->getReturnType())) + DeclContext *ContextDC = Context->getDeclContext(); + DeclContext *FriendDC = Friend->getDeclContext(); + + if (!FriendDC->isDependentContext() && + !MightInstantiateTo(S, ContextDC, FriendDC)) return false; - for (unsigned I = 0, E = FriendTy->getNumParams(); I != E; ++I) - if (!MightInstantiateTo(S, ContextTy->getParamType(I), - FriendTy->getParamType(I))) - return false; + CanQual<FunctionProtoType> FriendTy = + S.Context.getCanonicalType(Friend->getType())->getAs<FunctionProtoType>(); + CanQual<FunctionProtoType> ContextTy = + S.Context.getCanonicalType(Context->getType()) + ->getAs<FunctionProtoType>(); - return true; + return MightInstantiateTo(S, ContextTy, FriendTy); } -static bool MightInstantiateTo(Sema &S, - FunctionTemplateDecl *Context, +static bool MightInstantiateTo(Sema &S, FunctionTemplateDecl *Context, FunctionTemplateDecl *Friend) { - return MightInstantiateTo(S, - Context->getTemplatedDecl(), + return MightInstantiateTo(S, Context->getTemplatedDecl(), Friend->getTemplatedDecl()); } @@ -551,6 +614,165 @@ static AccessResult MatchesFriend(Sema &S, return OnFailure; } +static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, + NamedDecl *ND) { + ND = cast<NamedDecl>(ND->getCanonicalDecl()); + if (ClassTemplateDecl *CTD = dyn_cast<ClassTemplateDecl>(ND)) + return MatchesFriend(S, EC, CTD); + + if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND)) + return MatchesFriend(S, EC, FTD); + + if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND)) + return MatchesFriend(S, EC, RD); + + assert(isa<FunctionDecl>(ND) && "unknown friend decl kind"); + return MatchesFriend(S, EC, cast<FunctionDecl>(ND)); +} + +static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, + FriendTemplateDecl *FriendTD, + FunctionTemplateDecl *FriendFTD) { + AccessResult OnFailure = AR_inaccessible; + NestedNameSpecifier FriendNNS = FriendFTD->getTemplatedDecl()->getQualifier(); + const auto *FriendTST = TryGetTemplateSpecializationType(S, FriendNNS); + if (!FriendTST) + return OnFailure; + + auto *FriendCTD = dyn_cast<ClassTemplateDecl>( + FriendTST->getTemplateName().getAsTemplateDecl()); + if (!FriendCTD) + return OnFailure; + + TemplateParameterList *FriendTPL = + FriendTD->getFriendTypeTemplateParameterList(0); + + if (!FriendTPL) + return OnFailure; + + for (FunctionDecl *FD : EC.Functions) { + if (!MatchesFriendContext(S, FD, FriendCTD, FriendTST->template_arguments(), + FriendTPL, FriendTD->getLocation())) + continue; + + FunctionTemplateDecl *ContextFTD = FD->getPrimaryTemplate(); + if (!ContextFTD) + ContextFTD = FD->getDescribedFunctionTemplate(); + + if (ContextFTD && MightInstantiateTo(S, FriendFTD, ContextFTD)) + return AR_accessible; + } + + return OnFailure; +} + +static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, + FriendTemplateDecl *FriendTD, + FunctionDecl *FriendFD) { + AccessResult OnFailure = AR_inaccessible; + NestedNameSpecifier FriendNNS = FriendFD->getQualifier(); + const auto *FriendTST = TryGetTemplateSpecializationType(S, FriendNNS); + if (!FriendTST) + return OnFailure; + + auto *FriendCTD = dyn_cast<ClassTemplateDecl>( + FriendTST->getTemplateName().getAsTemplateDecl()); + if (!FriendCTD) + return OnFailure; + + TemplateParameterList *FriendTPL = + FriendTD->getFriendTypeTemplateParameterList(0); + if (!FriendTPL) + return OnFailure; + + CanQual<FunctionProtoType> FriendProto = + S.Context.getCanonicalType(FriendFD->getType()) + ->getAs<FunctionProtoType>(); + + for (FunctionDecl *FD : EC.Functions) { + if (FD->getDeclName() != FriendFD->getDeclName()) + continue; + + if (!MatchesFriendContext(S, FD, FriendCTD, FriendTST->template_arguments(), + FriendTPL, FriendTD->getLocation())) + continue; + + CanQual<FunctionProtoType> ContextProto = + S.Context.getCanonicalType(FD->getType())->getAs<FunctionProtoType>(); + if (MightInstantiateTo(S, ContextProto, FriendProto)) + return AR_accessible; + } + + return OnFailure; +} + +static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, + FriendTemplateDecl *FTD, NamedDecl *ND) { + if (auto *TD = dyn_cast<FunctionTemplateDecl>(ND)) + return MatchesFriend(S, EC, FTD, TD); + + if (auto *FD = dyn_cast<FunctionDecl>(ND)) + return MatchesFriend(S, EC, FTD, FD); + + return MatchesFriend(S, EC, ND); +} + +static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, + FriendTemplateDecl *FTD, + TypeSourceInfo *TSI) { + QualType TypeAsWritten = TSI->getType(); + if (!TypeAsWritten->isDependentType()) + return MatchesFriend(S, EC, S.Context.getCanonicalType(TypeAsWritten)); + + AccessResult OnFailure = EC.isDependent() ? AR_dependent : AR_inaccessible; + const auto *DNT = TypeAsWritten->getAs<DependentNameType>(); + if (!DNT) + return OnFailure; + + NestedNameSpecifier NNS = DNT->getQualifier(); + if (!NNS) + return OnFailure; + + const auto *T = NNS.getAsType(); + if (!T) + return OnFailure; + + const auto *TST = + S.Context.getCanonicalType(T)->getAsNonAliasTemplateSpecializationType(); + if (!TST) + return OnFailure; + + auto *CTD = + dyn_cast<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl()); + if (!CTD) + return OnFailure; + + TemplateParameterList *TPL = FTD->getFriendTypeTemplateParameterList(0); + if (!TPL) + return OnFailure; + + for (CXXRecordDecl *RD : EC.Records) { + if (RD->getDeclName() != DNT->getIdentifier()) + continue; + + const auto *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(RD->getDeclContext()); + if (!CTSD) + continue; + + if (CTSD->getSpecializedTemplate()->getCanonicalDecl() != + CTD->getCanonicalDecl()) + continue; + + if (CanDeduceTemplateArguments(S, TPL, TST->template_arguments(), + CTSD->getTemplateArgs().asArray(), + FTD->getLocation())) + return AR_accessible; + } + + return OnFailure; +} + /// Determines whether the given friend declaration matches anything /// in the effective context. static AccessResult MatchesFriend(Sema &S, @@ -561,25 +783,27 @@ static AccessResult MatchesFriend(Sema &S, if (FriendD->isInvalidDecl() || FriendD->isUnsupportedFriend()) return AR_accessible; + if (NamedDecl *Friend = FriendD->getFriendDecl()) + return MatchesFriend(S, EC, Friend); + if (TypeSourceInfo *T = FriendD->getFriendType()) return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified()); - NamedDecl *Friend - = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl()); - - // FIXME: declarations with dependent or templated scope. + return AR_inaccessible; +} - if (isa<ClassTemplateDecl>(Friend)) - return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend)); +static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, + FriendTemplateDecl *FTD) { + if (FTD->isInvalidDecl() || FTD->isUnsupportedFriend()) + return AR_accessible; - if (isa<FunctionTemplateDecl>(Friend)) - return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend)); + if (NamedDecl *ND = FTD->getFriendDecl()) + return MatchesFriend(S, EC, FTD, ND); - if (isa<CXXRecordDecl>(Friend)) - return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend)); + if (TypeSourceInfo *TSI = FTD->getFriendType()) + return MatchesFriend(S, EC, FTD, TSI); - assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind"); - return MatchesFriend(S, EC, cast<FunctionDecl>(Friend)); + return AR_inaccessible; } static AccessResult GetFriendKind(Sema &S, @@ -588,8 +812,14 @@ static AccessResult GetFriendKind(Sema &S, AccessResult OnFailure = AR_inaccessible; // Okay, check friends. - for (auto *Friend : Class->friends()) { - switch (MatchesFriend(S, EC, Friend)) { + for (FriendDecl *Friend : Class->friends()) { + AccessResult AR; + if (auto *FTD = dyn_cast<FriendTemplateDecl>(Friend)) + AR = MatchesFriend(S, EC, FTD); + else + AR = MatchesFriend(S, EC, cast<FriendDecl>(Friend)); + + switch (AR) { case AR_accessible: return AR_accessible; @@ -1450,7 +1680,8 @@ static AccessResult CheckEffectiveAccess(Sema &S, } static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, - AccessTarget &Entity) { + AccessTarget &Entity, + DeclContext *DC = nullptr) { // If the access path is public, it's accessible everywhere. if (Entity.getAccess() == AS_public) return Sema::AR_accessible; @@ -1491,7 +1722,7 @@ static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, } } - EffectiveContext EC(S.CurContext); + EffectiveContext EC(DC ? DC : S.CurContext); switch (CheckEffectiveAccess(S, EC, Loc, Entity)) { case AR_accessible: return Sema::AR_accessible; case AR_inaccessible: return Sema::AR_inaccessible; @@ -1904,7 +2135,7 @@ Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, ForceCheck, ForceUnprivileged); } -void Sema::CheckLookupAccess(const LookupResult &R) { +void Sema::CheckLookupAccess(const LookupResult &R, DeclContext *DC) { assert(getLangOpts().AccessControl && "performing access check without access control"); assert(R.getNamingClass() && "performing access check without naming class"); @@ -1915,7 +2146,7 @@ void Sema::CheckLookupAccess(const LookupResult &R) { R.getNamingClass(), I.getPair(), R.getBaseObjectType()); Entity.setDiag(diag::err_access); - CheckAccess(*this, R.getNameLoc(), Entity); + CheckAccess(*this, R.getNameLoc(), Entity, DC); } } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c1d3960e65ef6..6435dc250470d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -58,6 +58,7 @@ #include <set> using namespace clang; +using namespace sema; //===----------------------------------------------------------------------===// // CheckDefaultArgumentVisitor @@ -6211,8 +6212,8 @@ static void CheckAbstractClassUsage(AbstractUsageInfo &Info, if (D->isImplicit()) continue; // Step through friends to the befriended declaration. - if (auto *FD = dyn_cast<FriendDecl>(D)) { - D = FD->getFriendDecl(); + if (D->getKind() == Decl::Friend) { + D = cast<FriendDecl>(D)->getFriendDecl(); if (!D) continue; } @@ -7330,9 +7331,9 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { if (!isa<CXXDestructorDecl>(M)) CompleteMemberFunction(M); - } else if (auto *F = dyn_cast<FriendDecl>(D)) { + } else if (D->getKind() == Decl::Friend) { CheckForDefaultedFunction( - dyn_cast_or_null<FunctionDecl>(F->getFriendDecl())); + dyn_cast_or_null<FunctionDecl>(cast<FriendDecl>(D)->getFriendDecl())); } } @@ -18029,6 +18030,33 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, return Decl; } +bool Sema::CheckDependentFriend(SourceLocation Loc, NestedNameSpecifier NNS, + TemplateParameterList *FPL) { + if (!NNS || !FPL || FPL->size() == 0) + return false; + + if (NNS.isDependent()) { + if (NNS.getKind() == NestedNameSpecifier::Kind::Type) { + QualType T(NNS.getCanonical().getAsType(), 0); + if (isa<PackIndexingType>(T)) + return false; + + if (const auto *TST = dyn_cast<TemplateSpecializationType>(T)) { + if (isa<ClassTemplateDecl>(TST->getTemplateName().getAsTemplateDecl())) + return false; + } + + if (isa<InjectedClassNameType>(T)) + return false; + } + + Diag(Loc, diag::err_dependent_friend_not_member); + return true; + } + + return false; +} + DeclResult Sema::ActOnTemplatedFriendTag( Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc, CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, @@ -18099,9 +18127,8 @@ DeclResult Sema::ActOnTemplatedFriendTag( if (T.isNull()) return true; - FriendDecl *Friend = - FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc, - EllipsisLoc, TempParamLists); + FriendDecl *Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, + FriendLoc, EllipsisLoc); Friend->setAccess(AS_public); CurContext->addDecl(Friend); return Friend; @@ -18127,25 +18154,41 @@ DeclResult Sema::ActOnTemplatedFriendTag( } } - // Handle the case of a templated-scope friend class. e.g. - // template <class T> class A<T>::B; - // FIXME: we don't support these right now. - Diag(NameLoc, diag::warn_template_qualified_friend_unsupported) - << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext); + NestedNameSpecifier NNS = SS.getScopeRep(); + if (EllipsisLoc.isInvalid() && + CheckDependentFriend(TagLoc, NNS, TempParamLists.front())) + return true; + ElaboratedTypeKeyword ETK = TypeWithKeyword::getKeywordForTagTypeKind(Kind); - QualType T = Context.getDependentNameType(ETK, SS.getScopeRep(), Name); + QualType T = Context.getDependentNameType(ETK, NNS, Name); TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); + DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>(); TL.setElaboratedKeywordLoc(TagLoc); TL.setQualifierLoc(SS.getWithLocInContext(Context)); TL.setNameLoc(NameLoc); - FriendDecl *Friend = - FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc, - EllipsisLoc, TempParamLists); + Decl *Friend; + if (TempParamLists.empty()) + Friend = FriendDecl::Create(Context, CurContext, NameLoc, TSI, FriendLoc, + EllipsisLoc); + else { + if (CheckTemplateDeclScope(S, TempParamLists.back())) + return true; + + Friend = FriendTemplateDecl::Create(Context, CurContext, NameLoc, TSI, + FriendLoc, TempParamLists, EllipsisLoc); + } + + if (EllipsisLoc.isValid() && NNS.isDependent()) { + Diag(NameLoc, diag::warn_template_qualified_friend_unsupported) + << SS.getScopeRep() << SS.getRange() << cast<CXXRecordDecl>(CurContext); + cast<FriendDecl>(Friend)->setUnsupportedFriend(true); + } + Friend->setAccess(AS_public); - Friend->setUnsupportedFriend(true); CurContext->addDecl(Friend); + return Friend; } @@ -18246,11 +18289,14 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, // friend a member of an arbitrary specialization of your template). Decl *D; - if (!TempParams.empty()) + if (!TempParams.empty()) { + if (CheckTemplateDeclScope(S, TempParams.back())) + return nullptr; + // TODO: Support variadic friend template decls? - D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI, - FriendLoc); - else + D = FriendTemplateDecl::Create(Context, CurContext, Loc, TSI, FriendLoc, + TempParams, EllipsisLoc); + } else D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(), TSI, FriendLoc, EllipsisLoc); @@ -18437,6 +18483,11 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, assert(isa<CXXRecordDecl>(DC) && "friend declaration not in class?"); } + if (TemplateParams.size() && SS.isValid() && + CheckDependentFriend(NameInfo.getLoc(), SS.getScopeRep(), + TemplateParams.front())) + return nullptr; + if (!DC->isRecord()) { int DiagArg = -1; switch (D.getName().getKind()) { @@ -18500,54 +18551,51 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, PushOnScopeChains(ND, EnclosingScope, /*AddToContext=*/ false); } - FriendDecl *FrD = FriendDecl::Create(Context, CurContext, - D.getIdentifierLoc(), ND, - DS.getFriendSpecLoc()); - FrD->setAccess(AS_public); - CurContext->addDecl(FrD); + warnOnReservedIdentifier(ND); - if (ND->isInvalidDecl()) { - FrD->setInvalidDecl(); - } else { - if (DC->isRecord()) CheckFriendAccess(ND); + if (ND->isInvalidDecl()) + return ND; - FunctionDecl *FD; - if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND)) - FD = FTD->getTemplatedDecl(); - else - FD = cast<FunctionDecl>(ND); - - // C++ [class.friend]p6: - // A function may be defined in a friend declaration of a class if and - // only if the class is a non-local class, and the function name is - // unqualified. - if (D.isFunctionDefinition()) { - // Qualified friend function definition. - if (SS.isNotEmpty()) { - // FIXME: We should only do this if the scope specifier names the - // innermost enclosing namespace; otherwise the fixit changes the - // meaning of the code. - SemaDiagnosticBuilder DB = - Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def); - - DB << SS.getScopeRep(); - if (DC->isFileContext()) - DB << FixItHint::CreateRemoval(SS.getRange()); - - // Friend function defined in a local class. - } else if (FunctionContainingLocalClass) { - Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class); - - // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have - // a template-id, the function name is not unqualified because these is - // no name. While the wording requires some reading in-between the - // lines, GCC, MSVC, and EDG all consider a friend function - // specialization definitions to be de facto explicit specialization - // and diagnose them as such. - } else if (isTemplateId) { - Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def); - } + if (DC->isRecord()) + CheckFriendAccess(ND); + + FunctionDecl *FD; + if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND)) + FD = FTD->getTemplatedDecl(); + else + FD = cast<FunctionDecl>(ND); + + // C++ [class.friend]p6: + // A function may be defined in a friend declaration of a class if and + // only if the class is a non-local class, and the function name is + // unqualified. + if (D.isFunctionDefinition()) { + // Qualified friend function definition. + if (SS.isNotEmpty()) { + // FIXME: We should only do this if the scope specifier names the + // innermost enclosing namespace; otherwise the fixit changes the + // meaning of the code. + SemaDiagnosticBuilder DB = + Diag(SS.getRange().getBegin(), diag::err_qualified_friend_def); + + DB << SS.getScopeRep(); + if (DC->isFileContext()) + DB << FixItHint::CreateRemoval(SS.getRange()); + + // Friend function defined in a local class. + } else if (FunctionContainingLocalClass) { + Diag(NameInfo.getBeginLoc(), diag::err_friend_def_in_local_class); + + // Per [basic.pre]p4, a template-id is not a name. Therefore, if we have + // a template-id, the function name is not unqualified because these is + // no name. While the wording requires some reading in-between the + // lines, GCC, MSVC, and EDG all consider a friend function + // specialization definitions to be de facto explicit specialization + // and diagnose them as such. + } else if (isTemplateId) { + Diag(NameInfo.getBeginLoc(), diag::err_friend_specialization_def); } + } // C++11 [dcl.fct.default]p4: If a friend declaration specifies a // default argument expression, that declaration shall be a definition @@ -18565,18 +18613,27 @@ NamedDecl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, Diag(FD->getLocation(), diag::err_friend_decl_with_def_arg_must_be_def); } - // Mark templated-scope function declarations as unsupported. - if (FD->getNumTemplateParameterLists() && SS.isValid()) { - Diag(FD->getLocation(), diag::warn_template_qualified_friend_unsupported) - << SS.getScopeRep() << SS.getRange() - << cast<CXXRecordDecl>(CurContext); - FrD->setUnsupportedFriend(true); - } - } + unsigned NumTPLists = FD->getNumTemplateParameterLists(); + Decl *Friend; + if (NumTPLists && SS.isValid()) { + SmallVector<TemplateParameterList *, 1> TPL(NumTPLists); + for (unsigned I = 0, N = NumTPLists; I != N; ++I) + TPL[I] = FD->getTemplateParameterList(I); - warnOnReservedIdentifier(ND); + if (CheckTemplateDeclScope(S, TPL.back())) + return nullptr; - return ND; + Friend = + FriendTemplateDecl::Create(Context, CurContext, D.getIdentifierLoc(), + ND, DS.getFriendSpecLoc(), TPL); + } else { + Friend = FriendDecl::Create(Context, CurContext, D.getIdentifierLoc(), ND, + DS.getFriendSpecLoc()); + } + Friend->setAccess(AS_public); + CurContext->addDecl(Friend); + + return ND; } void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c436b7018a2bd..8938add183b0f 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -11266,14 +11266,11 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, return T; } -/// Build the type that describes a C++ typename specifier, -/// e.g., "typename T::type". -QualType -Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, - SourceLocation KeywordLoc, - NestedNameSpecifierLoc QualifierLoc, - const IdentifierInfo &II, - SourceLocation IILoc, bool DeducedTSTContext) { +QualType Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, + SourceLocation KeywordLoc, + NestedNameSpecifierLoc QualifierLoc, + const IdentifierInfo &II, SourceLocation IILoc, + bool DeducedTSTContext, DeclContext *DC) { assert((Keyword != ElaboratedTypeKeyword::None) == KeywordLoc.isValid()); CXXScopeSpec SS; @@ -11303,10 +11300,17 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, DeclarationName Name(&II); LookupResult Result(*this, Name, IILoc, LookupOrdinaryName); - if (Ctx) + if (Ctx) { LookupQualifiedName(Result, Ctx, SS); - else + if (getLangOpts().AccessControl && DC) { + Result.suppressAccessDiagnostics(); + if (Result.isClassLookup()) + CheckLookupAccess(Result, DC); + } + } 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 c71c40526ccdc..5733fca7d6abe 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3641,7 +3641,7 @@ TemplateDeductionResult Sema::SubstituteExplicitTemplateArguments( if (Proto->hasTrailingReturn()) { if (SubstParmTypes(Function->getLocation(), Function->parameters(), Proto->getExtParameterInfosOrNull(), MLTAL, ParamTypes, - /*params=*/nullptr, ExtParamInfos)) + /*params=*/nullptr, ExtParamInfos, Function)) return TemplateDeductionResult::SubstitutionFailure; } @@ -3683,7 +3683,7 @@ TemplateDeductionResult Sema::SubstituteExplicitTemplateArguments( if (!Proto->hasTrailingReturn() && SubstParmTypes(Function->getLocation(), Function->parameters(), Proto->getExtParameterInfosOrNull(), MLTAL, ParamTypes, - /*params*/ nullptr, ExtParamInfos)) + /*params*/ nullptr, ExtParamInfos, Function)) return TemplateDeductionResult::SubstitutionFailure; if (FunctionType) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 5381a5a6f110d..53d5497e21daf 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1321,6 +1321,7 @@ namespace { const MultiLevelTemplateArgumentList &TemplateArgs; SourceLocation Loc; DeclarationName Entity; + DeclContext *DC; // Whether to evaluate the C++20 constraints or simply substitute into them. bool EvaluateConstraints = true; // Whether Substitution was Incomplete, that is, we tried to substitute in @@ -1342,9 +1343,10 @@ namespace { TemplateInstantiator(Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity, + DeclContext *DC = nullptr, bool BailOutOnIncomplete = false) : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), - Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {} + Entity(Entity), DC(DC), BailOutOnIncomplete(BailOutOnIncomplete) {} void setEvaluateConstraints(bool B) { EvaluateConstraints = B; @@ -1359,7 +1361,7 @@ namespace { TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef, SourceLocation Loc, const MultiLevelTemplateArgumentList &TemplateArgs) - : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), + : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), DC(nullptr), BailOutOnIncomplete(false) { if (!SemaRef.CurrentCachedTemplateArgs) return; @@ -1369,6 +1371,29 @@ namespace { Arg.Profile(V, SemaRef.Context); } + QualType RebuildDependentNameType(ElaboratedTypeKeyword Keyword, + SourceLocation KeywordLoc, + NestedNameSpecifierLoc QualifierLoc, + const IdentifierInfo *Id, + SourceLocation IdLoc, + bool DeducedTSTContext) { + CXXScopeSpec SS; + SS.Adopt(QualifierLoc); + + NestedNameSpecifier NNS = QualifierLoc.getNestedNameSpecifier(); + if (NNS.isDependent()) { + if (!SemaRef.computeDeclContext(SS)) + return SemaRef.Context.getDependentNameType(Keyword, NNS, Id); + } + if (Keyword == ElaboratedTypeKeyword::None || + Keyword == ElaboratedTypeKeyword::Typename) { + return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, *Id, + IdLoc, DeducedTSTContext, DC); + } + return inherited::RebuildDependentNameType( + Keyword, KeywordLoc, QualifierLoc, Id, IdLoc, DeducedTSTContext); + } + /// Determine whether the given type \p T has already been /// transformed. /// @@ -2442,7 +2467,7 @@ ParmVarDecl *TemplateInstantiator::TransformFunctionTypeParam( bool ExpectParameterPack) { auto NewParm = SemaRef.SubstParmVarDecl( OldParm, TemplateArgs, indexAdjustment, NumExpansions, - ExpectParameterPack, EvaluateConstraints); + ExpectParameterPack, DC, EvaluateConstraints); if (NewParm && SemaRef.getLangOpts().OpenCL) SemaRef.deduceOpenCLAddressSpace(NewParm); return NewParm; @@ -2831,9 +2856,8 @@ TemplateInstantiator::TransformNestedRequirement( TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, const MultiLevelTemplateArgumentList &Args, - SourceLocation Loc, - DeclarationName Entity, - bool AllowDeducedTST) { + SourceLocation Loc, DeclarationName Entity, + DeclContext *DC, bool AllowDeducedTST) { assert(!CodeSynthesisContexts.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -2842,15 +2866,22 @@ TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, !T->getType()->isVariablyModifiedType()) return T; - TemplateInstantiator Instantiator(*this, Args, Loc, Entity); + TemplateInstantiator Instantiator(*this, Args, Loc, Entity, DC); return AllowDeducedTST ? Instantiator.TransformTypeWithDeducedTST(T) : Instantiator.TransformType(T); } +TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, + const MultiLevelTemplateArgumentList &Args, + SourceLocation Loc, DeclarationName Entity, + bool AllowDeducedTST) { + return SubstType(T, Args, Loc, Entity, /*DC=*/nullptr, AllowDeducedTST); +} + TypeSourceInfo *Sema::SubstType(TypeLoc TL, const MultiLevelTemplateArgumentList &Args, - SourceLocation Loc, - DeclarationName Entity) { + SourceLocation Loc, DeclarationName Entity, + DeclContext *DC) { assert(!CodeSynthesisContexts.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -2867,7 +2898,7 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL, return TLB.getTypeSourceInfo(Context, TL.getType()); } - TemplateInstantiator Instantiator(*this, Args, Loc, Entity); + TemplateInstantiator Instantiator(*this, Args, Loc, Entity, DC); TypeLocBuilder TLB; TLB.reserve(TL.getFullDataSize()); QualType Result = Instantiator.TransformType(TLB, TL); @@ -2877,11 +2908,25 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL, return TLB.getTypeSourceInfo(Context, Result); } +TypeSourceInfo *Sema::SubstType(TypeLoc TL, + const MultiLevelTemplateArgumentList &Args, + SourceLocation Loc, DeclarationName Entity) { + return SubstType(TL, Args, Loc, Entity, /*DC=*/nullptr); +} + /// Deprecated form of the above. QualType Sema::SubstType(QualType T, const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity, bool *IsIncompleteSubstitution) { + return SubstType(T, TemplateArgs, Loc, Entity, /*DC=*/nullptr, + IsIncompleteSubstitution); +} + +QualType Sema::SubstType(QualType T, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation Loc, DeclarationName Entity, + DeclContext *DC, bool *IsIncompleteSubstitution) { assert(!CodeSynthesisContexts.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -2892,7 +2937,7 @@ QualType Sema::SubstType(QualType T, return T; TemplateInstantiator Instantiator( - *this, TemplateArgs, Loc, Entity, + *this, TemplateArgs, Loc, Entity, DC, /*BailOutOnIncomplete=*/IsIncompleteSubstitution != nullptr); QualType QT = Instantiator.TransformType(T); if (IsIncompleteSubstitution && Instantiator.getIsIncomplete()) @@ -2922,13 +2967,10 @@ static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) { return false; } -TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T, - const MultiLevelTemplateArgumentList &Args, - SourceLocation Loc, - DeclarationName Entity, - CXXRecordDecl *ThisContext, - Qualifiers ThisTypeQuals, - bool EvaluateConstraints) { +TypeSourceInfo *Sema::SubstFunctionDeclType( + TypeSourceInfo *T, const MultiLevelTemplateArgumentList &Args, + SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext, + Qualifiers ThisTypeQuals, DeclContext *DC, bool EvaluateConstraints) { assert(!CodeSynthesisContexts.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -2936,7 +2978,7 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T, if (!NeedsInstantiationAsFunctionType(T)) return T; - TemplateInstantiator Instantiator(*this, Args, Loc, Entity); + TemplateInstantiator Instantiator(*this, Args, Loc, Entity, DC); Instantiator.setEvaluateConstraints(EvaluateConstraints); TypeLocBuilder TLB; @@ -3111,11 +3153,10 @@ bool Sema::SubstTypeConstraint( : SourceLocation()); } -ParmVarDecl * -Sema::SubstParmVarDecl(ParmVarDecl *OldParm, - const MultiLevelTemplateArgumentList &TemplateArgs, - int indexAdjustment, UnsignedOrNone NumExpansions, - bool ExpectParameterPack, bool EvaluateConstraint) { +ParmVarDecl *Sema::SubstParmVarDecl( + ParmVarDecl *OldParm, const MultiLevelTemplateArgumentList &TemplateArgs, + int indexAdjustment, UnsignedOrNone NumExpansions, bool ExpectParameterPack, + DeclContext *DC, bool EvaluateConstraint) { TypeSourceInfo *OldTSI = OldParm->getTypeSourceInfo(); TypeSourceInfo *NewTSI = nullptr; @@ -3125,7 +3166,7 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm, // We have a function parameter pack. Substitute into the pattern of the // expansion. NewTSI = SubstType(ExpansionTL.getPatternLoc(), TemplateArgs, - OldParm->getLocation(), OldParm->getDeclName()); + OldParm->getLocation(), OldParm->getDeclName(), DC); if (!NewTSI) return nullptr; @@ -3147,7 +3188,7 @@ Sema::SubstParmVarDecl(ParmVarDecl *OldParm, } } else { NewTSI = SubstType(OldTSI, TemplateArgs, OldParm->getLocation(), - OldParm->getDeclName()); + OldParm->getDeclName(), DC, /*AllowDeducedTST=*/false); } if (!NewTSI) @@ -3237,13 +3278,13 @@ bool Sema::SubstParmTypes( const MultiLevelTemplateArgumentList &TemplateArgs, SmallVectorImpl<QualType> &ParamTypes, SmallVectorImpl<ParmVarDecl *> *OutParams, - ExtParameterInfoBuilder &ParamInfos) { + ExtParameterInfoBuilder &ParamInfos, DeclContext *DC) { assert(!CodeSynthesisContexts.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); - TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, - DeclarationName()); + TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, DeclarationName(), + DC); return Instantiator.TransformFunctionTypeParams( Loc, Params, nullptr, ExtParamInfos, ParamTypes, OutParams, ParamInfos); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 09c2482168ab7..ef739c2a96983 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2766,7 +2766,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( } SmallVector<ParmVarDecl *, 4> Params; - TypeSourceInfo *TInfo = SubstFunctionType(D, Params); + TypeSourceInfo *TInfo = SubstFunctionType(D, Params, nullptr); if (!TInfo) return nullptr; QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo); @@ -3180,29 +3180,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( D->setTypeSourceInfo(TSI); } - SmallVector<ParmVarDecl *, 4> Params; - TypeSourceInfo *TInfo = SubstFunctionType(D, Params); - if (!TInfo) - return nullptr; - QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo); - - if (TemplateParams && TemplateParams->size()) { - auto *LastParam = - dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back()); - if (LastParam && LastParam->isImplicit() && - LastParam->hasTypeConstraint()) { - // In abbreviated templates, the type-constraints of invented template - // type parameters are instantiated with the function type, invalidating - // the TemplateParameterList which relied on the template type parameter - // not having a type constraint. Recreate the TemplateParameterList with - // the updated parameter list. - TemplateParams = TemplateParameterList::Create( - SemaRef.Context, TemplateParams->getTemplateLoc(), - TemplateParams->getLAngleLoc(), TemplateParams->asArray(), - TemplateParams->getRAngleLoc(), TemplateParams->getRequiresClause()); - } - } - NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc(); if (QualifierLoc) { QualifierLoc = SemaRef.SubstNestedNameSpecifierLoc(QualifierLoc, @@ -3233,14 +3210,40 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( DeclarationNameInfo NameInfo = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); + if (!NameInfo.getName()) + return nullptr; + + CXXMethodDecl *Method = nullptr; + SourceLocation StartLoc = D->getInnerLocStart(); + + SmallVector<ParmVarDecl *, 4> Params; + TypeSourceInfo *TInfo = + SubstFunctionType(D, Params, dyn_cast<CXXConstructorDecl>(D)); + if (!TInfo) + return nullptr; + + QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo); + + if (TemplateParams && TemplateParams->size()) { + auto *LastParam = + dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back()); + if (LastParam && LastParam->isImplicit() && + LastParam->hasTypeConstraint()) { + // In abbreviated templates, the type-constraints of invented template + // type parameters are instantiated with the function type, invalidating + // the TemplateParameterList which relied on the template type parameter + // not having a type constraint. Recreate the TemplateParameterList with + // the updated parameter list. + TemplateParams = TemplateParameterList::Create( + SemaRef.Context, TemplateParams->getTemplateLoc(), + TemplateParams->getLAngleLoc(), TemplateParams->asArray(), + TemplateParams->getRAngleLoc(), TemplateParams->getRequiresClause()); + } + } if (FunctionRewriteKind != RewriteKind::None) adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo); - // Build the instantiated method declaration. - CXXMethodDecl *Method = nullptr; - - SourceLocation StartLoc = D->getInnerLocStart(); if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { Method = CXXConstructorDecl::Create( SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo, @@ -4712,12 +4715,36 @@ Decl *TemplateDeclInstantiator::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D) } Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) { - // FIXME: We need to be able to instantiate FriendTemplateDecls. - unsigned DiagID = SemaRef.getDiagnostics().getCustomDiagID( - DiagnosticsEngine::Error, - "cannot instantiate %0 yet"); - SemaRef.Diag(D->getLocation(), DiagID) - << D->getDeclKindName(); + unsigned NumTPLists = D->getFriendTypeNumTemplateParameterLists(); + SmallVector<TemplateParameterList *, 1> TPL(NumTPLists); + for (unsigned I = 0, N = NumTPLists; I != N; ++I) { + TemplateParameterList *InstParams = + SubstTemplateParams(D->getFriendTypeTemplateParameterList(I)); + if (!InstParams) + return nullptr; + + TPL[I] = InstParams; + } + + Decl *FTD = nullptr; + if (TypeSourceInfo *FT = D->getFriendType()) { + TypeSourceInfo *TSI = SemaRef.SubstType(FT, TemplateArgs, D->getLocation(), + DeclarationName()); + if (TSI) + FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(), + TSI, D->getFriendLoc(), TPL); + } else { + NamedDecl *ND = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl( + D->getLocation(), D->getFriendDecl(), TemplateArgs)); + if (ND) + FTD = FriendTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(), + ND, D->getFriendLoc(), TPL); + } + + if (FTD) { + FTD->setAccess(AS_public); + Owner->addDecl(FTD); + } return nullptr; } @@ -5099,9 +5126,8 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization( return InstPartialSpec; } -TypeSourceInfo* -TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, - SmallVectorImpl<ParmVarDecl *> &Params) { +TypeSourceInfo *TemplateDeclInstantiator::SubstFunctionType( + FunctionDecl *D, SmallVectorImpl<ParmVarDecl *> &Params, DeclContext *DC) { TypeSourceInfo *OldTInfo = D->getTypeSourceInfo(); assert(OldTInfo && "substituting function without type source info"); assert(Params.empty() && "parameter vector is non-empty at start"); @@ -5115,7 +5141,7 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, TypeSourceInfo *NewTInfo = SemaRef.SubstFunctionDeclType( OldTInfo, TemplateArgs, D->getTypeSpecStartLoc(), D->getDeclName(), - ThisContext, ThisTypeQuals, EvaluateConstraints); + ThisContext, ThisTypeQuals, DC, EvaluateConstraints); if (!NewTInfo) return nullptr; @@ -5170,8 +5196,9 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, continue; } - ParmVarDecl *Parm = - cast_or_null<ParmVarDecl>(VisitParmVarDecl(OldParam)); + ParmVarDecl *Parm = SemaRef.SubstParmVarDecl( + OldParam, TemplateArgs, /*indexAdjustment=*/0, std::nullopt, + /*ExpectParameterPack=*/false, DC, EvaluateConstraints); if (!Parm) return nullptr; Params.push_back(Parm); @@ -5191,8 +5218,8 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, SmallVector<QualType, 4> ParamTypes; Sema::ExtParameterInfoBuilder ExtParamInfos; if (SemaRef.SubstParmTypes(D->getLocation(), D->parameters(), nullptr, - TemplateArgs, ParamTypes, &Params, - ExtParamInfos)) + TemplateArgs, ParamTypes, &Params, ExtParamInfos, + DC)) return nullptr; } @@ -5248,7 +5275,7 @@ bool Sema::addInstantiatedParametersToScope( if (!PatternDecl->getType()->isDependentType()) { QualType T = SubstType(PatternParam->getType(), TemplateArgs, FunctionParam->getLocation(), - FunctionParam->getDeclName()); + FunctionParam->getDeclName(), Function); if (T.isNull()) return true; FunctionParam->setType(T); @@ -5273,7 +5300,7 @@ bool Sema::addInstantiatedParametersToScope( Sema::ArgPackSubstIndexRAII SubstIndex(*this, Arg); QualType T = SubstType(PatternType, TemplateArgs, FunctionParam->getLocation(), - FunctionParam->getDeclName()); + FunctionParam->getDeclName(), Function); if (T.isNull()) return true; FunctionParam->setType(T); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9033ea55bc5e2..ed7ece7b9fdcc 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2397,8 +2397,6 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) { D->Friend = readDeclAs<NamedDecl>(); else D->Friend = readTypeSourceInfo(); - for (unsigned i = 0; i != D->NumTPLists; ++i) - D->getTrailingObjects()[i] = Record.readTemplateParameterList(); D->NextFriend = readDeclID().getRawValue(); D->UnsupportedFriend = (Record.readInt() != 0); D->FriendLoc = readSourceLocation(); @@ -2407,15 +2405,13 @@ void ASTDeclReader::VisitFriendDecl(FriendDecl *D) { void ASTDeclReader::VisitFriendTemplateDecl(FriendTemplateDecl *D) { VisitDecl(D); - unsigned NumParams = Record.readInt(); - D->NumParams = NumParams; - D->Params = new (Reader.getContext()) TemplateParameterList *[NumParams]; - for (unsigned i = 0; i != NumParams; ++i) - D->Params[i] = Record.readTemplateParameterList(); + for (unsigned i = 0; i != D->NumTPLists; ++i) + D->getTrailingObjects()[i] = Record.readTemplateParameterList(); if (Record.readInt()) // HasFriendDecl D->Friend = readDeclAs<NamedDecl>(); else D->Friend = readTypeSourceInfo(); + D->NextFriend = readDeclID().getRawValue(); D->FriendLoc = readSourceLocation(); } @@ -4044,10 +4040,10 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { D = AccessSpecDecl::CreateDeserialized(Context, ID); break; case DECL_FRIEND: - D = FriendDecl::CreateDeserialized(Context, ID, Record.readInt()); + D = FriendDecl::CreateDeserialized(Context, ID); break; case DECL_FRIEND_TEMPLATE: - D = FriendTemplateDecl::CreateDeserialized(Context, ID); + D = FriendTemplateDecl::CreateDeserialized(Context, ID, Record.readInt()); break; case DECL_CLASS_TEMPLATE: D = ClassTemplateDecl::CreateDeserialized(Context, ID); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index e415ac1e47862..9311715a6ddaa 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1819,7 +1819,6 @@ void ASTDeclWriter::VisitAccessSpecDecl(AccessSpecDecl *D) { void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) { // Record the number of friend type template parameter lists here // so as to simplify memory allocation during deserialization. - Record.push_back(D->NumTPLists); VisitDecl(D); bool hasFriendDecl = isa<NamedDecl *>(D->Friend); Record.push_back(hasFriendDecl); @@ -1827,8 +1826,6 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) { Record.AddDeclRef(D->getFriendDecl()); else Record.AddTypeSourceInfo(D->getFriendType()); - for (unsigned i = 0; i < D->NumTPLists; ++i) - Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i)); Record.AddDeclRef(D->getNextFriend()); Record.push_back(D->UnsupportedFriend); Record.AddSourceLocation(D->FriendLoc); @@ -1838,15 +1835,16 @@ void ASTDeclWriter::VisitFriendDecl(FriendDecl *D) { void ASTDeclWriter::VisitFriendTemplateDecl(FriendTemplateDecl *D) { VisitDecl(D); - Record.push_back(D->getNumTemplateParameters()); - for (unsigned i = 0, e = D->getNumTemplateParameters(); i != e; ++i) - Record.AddTemplateParameterList(D->getTemplateParameterList(i)); + Record.push_back(D->NumTPLists); + for (unsigned i = 0, e = D->NumTPLists; i != e; ++i) + Record.AddTemplateParameterList(D->getFriendTypeTemplateParameterList(i)); Record.push_back(D->getFriendDecl() != nullptr); if (D->getFriendDecl()) Record.AddDeclRef(D->getFriendDecl()); else Record.AddTypeSourceInfo(D->getFriendType()); - Record.AddSourceLocation(D->getFriendLoc()); + Record.AddDeclRef(D->getNextFriend()); + Record.AddSourceLocation(D->FriendLoc); Code = serialization::DECL_FRIEND_TEMPLATE; } diff --git a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp index f7216ea7eb7b0..6c55e81c58c18 100644 --- a/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp +++ b/clang/test/CXX/class.access/class.friend/p3-cxx0x.cpp @@ -36,7 +36,7 @@ class A { public: class foo {}; static int y; - template <typename S> friend class B<S>::ty; // expected-warning {{dependent nested name specifier 'B<S>' for friend class declaration is not supported}} + template <typename S> friend class B<S>::ty; }; template<typename T> class B { typedef int ty; }; @@ -74,7 +74,7 @@ struct { friend float; - template<typename T> friend class A<T>::foo; // expected-warning {{not supported}} + template<typename T> friend class A<T>::foo; } a; void testA() { (void)sizeof(A<int>); } diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp index ebee9bc4c3e16..236423067dcb5 100644 --- a/clang/test/CXX/drs/cwg18xx.cpp +++ b/clang/test/CXX/drs/cwg18xx.cpp @@ -420,25 +420,20 @@ class C { template<class T> friend struct A<T>::B; - // expected-warning@-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@-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@-1 {{dependent nested name specifier 'A<T>::D' for friend class declaration is not supported; turning off access control for 'C'}} + // expected-error@-1 {{friend declaration does not name a member of a class template specialization}} template<class T> friend int *A<T*>::h(); - // expected-warning@-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@-1 {{dependent nested name specifier 'A<T>' for friend class declaration is not supported; turning off access control for 'C'}} }; C c; @@ -450,11 +445,15 @@ void A<int>::B::e() { (void)c.private_int; } template<class T> void A<T>::f() { (void)c.private_int; } int A<int>::f() { (void)c.private_int; return 0; } +// expected-error@-1 {{'private_int' is a private member of 'cwg1862::C'}} +// expected-note@-30 {{implicitly declared private here}} // FIXME: both definition of 'D::g' are not friends, so they don't have access to 'private_int' template<class T> void A<T>::D::g() { (void)c.private_int; } void A<int>::D::g() { (void)c.private_int; } +// expected-error@-1 {{'private_int' is a private member of 'cwg1862::C'}} +// expected-note@-37 {{implicitly declared private here}} template<class T> T A<T>::h() { (void)c.private_int; } diff --git a/clang/test/CXX/drs/cwg19xx.cpp b/clang/test/CXX/drs/cwg19xx.cpp index 8162f9caa8f15..4f7031ddec602 100644 --- a/clang/test/CXX/drs/cwg19xx.cpp +++ b/clang/test/CXX/drs/cwg19xx.cpp @@ -102,18 +102,18 @@ template<typename T> struct A { }; class X { static int x; - // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id template <typename T> friend class A<T>::B::C; - // expected-warning@-1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-error@-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@-1 {{'x' is a private member of 'cwg1918::X'}} + // expected-note@-12 {{implicitly declared private here}} }; }; } // namespace cwg1918 @@ -170,7 +170,7 @@ class X { // FIXME: this is ill-formed, because A<T>::B::C does not end with a simple-template-id template <typename T> friend class A<T>::B::C; - // expected-warning@-1 {{dependent nested name specifier 'A<T>::B' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-error@-1 {{friend declaration does not name a member of a class template specialization}} }; } // namespace cwg1945 diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp index b7b2ebf700375..1fca7390c371d 100644 --- a/clang/test/CXX/drs/cwg6xx.cpp +++ b/clang/test/CXX/drs/cwg6xx.cpp @@ -409,24 +409,27 @@ namespace cwg638 { // cwg638: no class X { typedef int type; template<class T> friend struct A<T>::B; - // expected-warning@-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@-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@-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@-1 {{dependent nested name specifier 'A<T>::C' for friend class declaration is not supported; turning off access control for 'X'}} + // expected-error@-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@-1 {{'type' is a private member of 'cwg638::X'}} + // expected-note@-11 {{implicitly declared private here}} struct B { X::type b; // ok }; - int f() { X::type c; } // FIXME: private + int f() { X::type c; } + // expected-error@-1 {{'type' is a private member of 'cwg638::X'}} + // expected-note@-17 {{implicitly declared private here}} void g() { X::type d; } // ok struct D { - void h() { X::type e; } // FIXME: private + void h() { X::type e; } + // expected-error@-1 {{'type' is a private member of 'cwg638::X'}} + // expected-note@-22 {{implicitly declared private here}} }; }; } // namespace cwg638 diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp index a292d0de97a39..5b03aba342f8b 100644 --- a/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.friend/p5.cpp @@ -6,7 +6,7 @@ namespace test0 { }; class B { - template <class T> friend class A<T>::Member; // expected-warning {{not supported}} + template <class T> friend class A<T>::Member; int n; }; @@ -19,7 +19,7 @@ namespace test1 { class C { static void foo(); - template <class T> friend void A<T>::f(); // expected-warning {{not supported}} + template <class T> friend void A<T>::f(); }; template <class T> struct A { @@ -35,25 +35,24 @@ namespace test1 { }; } -// FIXME: these should fail! namespace test2 { template <class T> struct A; class C { - static void foo(); - template <class T> friend void A<T>::g(); // expected-warning {{not supported}} + static void foo(); // expected-note 3 {{implicitly declared private here}} + template <class T> friend void A<T>::g(); }; template <class T> struct A { - void f() { C::foo(); } + void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} }; template <class T> struct A<T*> { - void f() { C::foo(); } + void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} }; template <> struct A<char> { - void f() { C::foo(); } + void f() { C::foo(); } // expected-error {{'foo' is a private member of 'test2::C'}} }; } @@ -66,7 +65,7 @@ namespace test3 { template <class U> class C { int i; - template <class T> friend struct A<T>::Inner; // expected-warning {{not supported}} + template <class T> friend struct A<T>::Inner; }; template <class T> int A<T>::Inner::foo() { @@ -81,22 +80,148 @@ namespace test3 { namespace test4 { template <class T> struct X { template <class U> void operator+=(U); - + template <class V> template <class U> - friend void X<V>::operator+=(U); // expected-warning {{not supported}} + friend void X<V>::operator+=(U); }; - void test() { + void test() { X<int>() += 1.0; } } namespace test5 { template<template <class> class T> struct A { - template<template <class> class U> friend void A<U>::foo(); // expected-warning {{not supported}} + template<template <class> class U> friend void A<U>::foo(); }; template <class> struct B {}; template class A<B>; } + +namespace test6 { + template <class T> struct A { + struct B { + static int f(); + }; + }; + + struct C { + int n; + template <class T> friend struct A<T>::B; + }; + + template <class T> int A<T>::B::f() { + C c; + c.n = 0; + return 0; + } + + int k = A<int>::B::f(); +} + +namespace test7 { + template <class T> struct A { + struct D { + void g(); + }; + }; + + struct C { + template <class T> friend void A<T>::D::g(); // expected-error {{friend declaration does not name a member of a class template specialization}} + }; +} + +namespace test8 { + template <class T> struct A { + T h(); + }; + + template <> struct A<int> { + int h(); + }; + + template <> struct A<float *> { + int *h(); + }; + + class C { + int n; // expected-note {{implicitly declared private here}} + template <class T> friend int *A<T *>::h(); + }; + + template <class T> T A<T>::h() { + return T(); + } + + int A<int>::h() { + C c; + c.n = 0; // expected-error {{'n' is a private member of 'test8::C'}} + return 0; + } + + template <> int *A<int *>::h() { + C c; + c.n = 0; + return nullptr; + } + + int *A<float *>::h() { + C c; + c.n = 0; + return nullptr; + } + + int *t1 = A<int *>().h(); + int *t2 = A<float *>().h(); + int t3 = A<int>().h(); +} + +namespace test9 { + template <class T> struct A { + template <T U> T i(); + }; + + template <> struct A<int> { + template <int U> int i(); + }; + + struct C { + int n; + template <class T> template <T U> friend T A<T>::i(); + }; + + template <class T> template <T U> T A<T>::i() { + C c; + c.n = 0; + return U; + } + + template <int U> int A<int>::i() { + C c; + c.n = 0; + return U; + } + + int x = A<int>().i<1>(); +} + +namespace test10 { + template <class T> struct A; + class C { + static void foo(); // expected-note {{implicitly declared private here}} + template <class T> friend void A<T>::f(); + }; + + template <class T> struct A { + void f() { C::foo(); } + }; + + template <> struct A<int> { + int f() { + C::foo(); // expected-error {{'foo' is a private member of 'test10::C'}} + return 0; + } + }; +} diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp new file mode 100644 index 0000000000000..67bb7e19b4aaf --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.friend/p6.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template <class T> struct A; +template <class T> struct B { + void f(); +}; + +void t1() { + struct S { + template <class T> friend void f(); // expected-error {{templates can only be declared in namespace or class scope}} + }; +} + +void t2() { + struct S { + template <class T> friend struct A; // expected-error {{templates cannot be declared inside of a local class}} + }; +} + +void t3() { + struct S { + template <class T> friend void B<T>::f(); // expected-error {{templates cannot be declared inside of a local class}} + }; +} diff --git a/clang/test/SemaCXX/many-template-parameter-lists.cpp b/clang/test/SemaCXX/many-template-parameter-lists.cpp index f98005c7e6fb5..cbd04db3301d6 100644 --- a/clang/test/SemaCXX/many-template-parameter-lists.cpp +++ b/clang/test/SemaCXX/many-template-parameter-lists.cpp @@ -5,7 +5,7 @@ template <class T> struct X { template <class U> - struct A { // expected-note {{not-yet-instantiated member is declared here}} + struct A { template <class V> struct B { template <class W> @@ -28,7 +28,9 @@ struct X { template <class X> template <class Y> template <class Z> - friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z); // expected-warning {{not supported}} expected-error {{no member 'A' in 'X<int>'; it has not yet been instantiated}} + friend void A<U>::template B<V>::template C<W>::template D<X>::template E<Y>::operator+=(Z); + // expected-error@-1 {{no member 'operator+=' in 'X<int>'; it has not yet been instantiated}} + // expected-note@-2 {{not-yet-instantiated member is declared here}} }; void test() { diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp index daec9410e547a..637f00e886ef7 100644 --- a/clang/test/SemaTemplate/GH71595.cpp +++ b/clang/test/SemaTemplate/GH71595.cpp @@ -20,15 +20,15 @@ class temp { template<C<temp> T> friend void g(); // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}} - temp(); + temp(); // expected-note {{implicitly declared private here}} }; template<C<temp<int>> T> void g() { - auto v = temp<T>(); + auto v = temp<T>(); // expected-error {{calling a private constructor of class 'temp<int>'}} } void h() { f<int>(); - g<int>(); + g<int>(); // expected-note {{in instantiation of function template specialization 'g<int>' requested here}} } diff --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp index 52ffef980fcdf..6f505d20e89e6 100644 --- a/clang/test/SemaTemplate/ctad.cpp +++ b/clang/test/SemaTemplate/ctad.cpp @@ -36,7 +36,7 @@ namespace Access { // Once we implement proper support for dependent nested name specifiers in // friends, this should still work. class Y { - template <typename T> friend D<T>::D(T, typename T::type); // expected-warning {{dependent nested name specifier}} + template <typename T> friend D<T>::D(T, typename T::type); struct type {}; }; D y = {Y(), {}}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
