Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5366,6 +5366,8 @@ FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart, SourceLocation FriendLoc, TypeSourceInfo *TSInfo); + QualType adjustFriendFunctionType(QualType T, CXXScopeSpec &SS, + MultiTemplateParamsArg TemplateParams); Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, MultiTemplateParamsArg TemplateParams); NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D, @@ -5667,7 +5669,8 @@ QualType CheckTemplateIdType(TemplateName Template, SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs); + TemplateArgumentListInfo &TemplateArgs, + bool FriendTemplate = false); TypeResult ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc, Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -12567,6 +12567,9 @@ SourceLocation Loc = D.getIdentifierLoc(); TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); + QualType FuncType = adjustFriendFunctionType(TInfo->getType(), + D.getCXXScopeSpec(), TemplateParams); + TInfo->overrideType(FuncType); // C++ [class.friend]p1 // A friend of a class is a function or class.... Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -2084,7 +2084,8 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs) { + TemplateArgumentListInfo &TemplateArgs, + bool FriendTemplate) { DependentTemplateName *DTN = Name.getUnderlying().getAsDependentTemplateName(); if (DTN && DTN->isIdentifier()) @@ -2175,7 +2176,7 @@ // TODO: in theory this could be a simple hashtable lookup; most // changes to CurContext don't change the set of current // instantiations. - if (isa(Template)) { + if (isa(Template) && !FriendTemplate) { for (DeclContext *Ctx = CurContext; Ctx; Ctx = Ctx->getLookupParent()) { // If we get out to a namespace, we're done. if (Ctx->isFileContext()) break; @@ -8541,3 +8542,122 @@ } return false; } + +namespace { + +/// \brief Helper class to test if type of friend template function depends on +/// template parameters of befriending template class. +/// +/// The following code contains friend function detected by this class: +/// \code +/// template class C1 { +/// template friend void func(T1 *x, T *y); +/// }; +/// \endcode +/// +/// The recognition is made by inspecting template parameters found in the type +/// definition. If parameter depth is lower than the depth at which the friend +/// declaration occurs, the friend template function is dependent. In the code +/// above \c func occurs at depth 1, depth of \c T is 0, so \c func is +/// dependent. +/// +class OuterParameterFinder : public RecursiveASTVisitor { + unsigned Depth; +public: + /// \brief Creates object for recognition. + /// \param TopLevel The depth at which friend template function occurs. + OuterParameterFinder(unsigned TopLevel) : Depth(TopLevel) {} + + /// \brief Main entry point for recognition. + /// \param T friend template function type. + /// \returns true if the function type is dependent. + bool isOuterDependent(QualType T) { + return !TraverseType(T); + } + + bool VisitTemplateTypeParmType(const TemplateTypeParmType *T) { + return T->getDepth() >= Depth; + } + + bool VisitInjectedClassNameType(const InjectedClassNameType *T) { + return false; + } +}; + +/// \brief Renumber template parameters found in the friend template function +/// type. +/// +/// When friend function template is parsed in the code: +/// \code +/// template class C1 { +/// template friend void func(T1 *x); +/// }; +/// \endcode +/// it gets canonical type 'void(template-param-1-0)'. All parameters are +/// shifted by the same value, which is the depth where the friend declaration +/// occurs. This class transforms the function type so in the example above the +/// function type becomes 'void(template-param-0-0)'. +/// +class TypeShifter : public TreeTransform { + unsigned Shift; +public: + TypeShifter(Sema &S, unsigned ShiftVal) + : TreeTransform(S), Shift(ShiftVal) {} + + bool AlreadyTransformed(QualType T) { + return T.isNull() || !T->isDependentType(); + } + + QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, + TemplateTypeParmTypeLoc TL) { + const TemplateTypeParmType *TTPType = TL.getTypePtr(); + QualType NewTTPType = SemaRef.getASTContext().getTemplateTypeParmType( + TTPType->getDepth() - Shift, TTPType->getIndex(), + TTPType->isParameterPack(), TTPType->getDecl()); + auto NewT = TLB.push(NewTTPType); + NewT.setNameLoc(TL.getNameLoc()); + return NewTTPType; + } + + QualType RebuildTemplateSpecializationType( + TemplateName Template, + SourceLocation TemplateNameLoc, + TemplateArgumentListInfo &TemplateArgs) { + return SemaRef.CheckTemplateIdType(Template, TemplateNameLoc, TemplateArgs, + true); + } +}; + +} + +/// \brief Given type of friend template function, shifts depth of template +/// type parameters, so that the type looks like if the function was declared +/// at file level. +/// +/// In the code: +/// \code +/// template class C1 { +/// template friend void func_1(T1 *x); +/// template friend void func_2(T1 *x, T *y); +/// }; +/// \endcode +/// this method changes type of \c func_1 from 'void(template-param-1-0)' to +/// 'void(template-param-0-0)' but leave type of \c func_2 intact. +/// +QualType Sema::adjustFriendFunctionType(QualType FuncType, CXXScopeSpec &SS, + MultiTemplateParamsArg TemplateParams) { + if (TemplateParams.empty()) + return FuncType; + if (!FuncType->isFunctionType()) + return FuncType; + + unsigned TopDepth = TemplateParams.front()->getDepth(); + if (OuterParameterFinder(TopDepth).isOuterDependent(FuncType)) + return FuncType; + if (SS.isValid()) { + if (SS.getScopeRep()->isDependent()) + return FuncType; + } + + return TypeShifter(*this, TopDepth).TransformType(FuncType); +} Index: test/SemaTemplate/friend-template-redecl.cpp =================================================================== --- /dev/null +++ test/SemaTemplate/friend-template-redecl.cpp @@ -0,0 +1,171 @@ +// RUN: %clang_cc1 -ast-dump -std=c++11 %s | FileCheck %s + + +// Friend function signature does not depend on befriending class +// template parameters. Such function must be in redeclaration chain. +// +namespace N1 { +// CHECK-LABEL: NamespaceDecl {{.*}} N1 + +template void func_01(T *x); +// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]] +// CHECK-NOT: prev +// CHECK-SAME: func_01 + +template class C1 { +// CHECK: ClassTemplateDecl {{.*}} C1 + + template friend void func_01(T1 *x); + // CHECK: FriendDecl + // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]] + // CHECK-SAME: prev [[FWD]] + // CHECK-SAME: func_01 +}; + +template void func_01(T1 *x) {} +// CHECK: FunctionTemplateDecl +// CHECK-SAME: prev [[FRIEND]] +// CHECK-SAME: func_01 + +} + + +// Friend function return type depends on befriending class +// template parameters. Such function does not participate in +// redeclaration chain. +// +namespace N2 { +// CHECK-LABEL: NamespaceDecl {{.*}} N2 + +template T *func_02(T *x); +// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]] +// CHECK-NOT: prev +// CHECK-SAME: func_02 + +template class C2 { +// CHECK: ClassTemplateDecl {{.*}} C2 + + template friend T *func_02(T1 *x); + // CHECK: FriendDecl + // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]] + // CHECK-NOT: prev + // CHECK-SAME: func_02 +}; + +template T1 *func_02(T1 *x) {} +// CHECK: FunctionTemplateDecl +// CHECK-SAME: prev [[FWD]] +// CHECK-SAME: func_02 + +} + + +// Friend function signature does not depend on befriending class +// template parameters. Such function must be in redeclaration chain. +// +namespace N3 { +// CHECK-LABEL: NamespaceDecl {{.*}} N3 + +template void func_01(T x1, T *x2, T &x3, T &&x4); +// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]] +// CHECK-NOT: prev +// CHECK-SAME: func_01 + +template class C1 { +// CHECK: ClassTemplateDecl {{.*}} C1 + + template friend void func_01(T1, T1 *, T1 &, T1 &&); + // CHECK: FriendDecl + // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]] + // CHECK-SAME: prev [[FWD]] + // CHECK-SAME: func_01 +}; + +template void func_01(T1 x1, T1 *x2, T1 &x3, T1 &&x4) {} +// CHECK: FunctionTemplateDecl +// CHECK-SAME: prev [[FRIEND]] +// CHECK-SAME: func_01 + +} + + +namespace N4 { +// CHECK-LABEL: NamespaceDecl {{.*}} N4 + +template void func_01(const T *, T *, T&, const T&); +// CHECK: FunctionTemplateDecl +// CHECK-SAME: func_01 +template void func_01(T *, const T *, const T&, T&); +// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]] +// CHECK-NOT: prev +// CHECK-SAME: func_01 + +template class C1 { +// CHECK: ClassTemplateDecl {{.*}} C1 + + template friend void func_01(T1 *x1, const T1 *x2, const T1 &x3, T1 &x4); + // CHECK: FriendDecl + // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]] + // CHECK-SAME: prev [[FWD]] + // CHECK-SAME: func_01 +}; + +template void func_01(T1 *x1, const T1 *x2, const T1 &x3, T1 &x4) {} +// CHECK: FunctionTemplateDecl +// CHECK-SAME: prev [[FRIEND]] +// CHECK-SAME: func_01 + +} + + +namespace N5 { +// CHECK-LABEL: NamespaceDecl {{.*}} N5 + +template void func_01(T x[sizeof(T)]); +// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]] +// CHECK-NOT: prev +// CHECK-SAME: func_01 + +template class C1 { +// CHECK: ClassTemplateDecl {{.*}} C1 + + template friend void func_01(T1 x[sizeof(T1)]); + // CHECK: FriendDecl + // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]] + // CHECK-SAME: prev [[FWD]] + // CHECK-SAME: func_01 +}; + +template void func_01(T1 x[sizeof(T1)]) {} +// CHECK: FunctionTemplateDecl +// CHECK-SAME: prev [[FRIEND]] +// CHECK-SAME: func_01 + +} + + +namespace N6 { +// CHECK-LABEL: NamespaceDecl {{.*}} N6 + +template class C3; +template void func_03(C3 *x); +// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]] +// CHECK-NOT: prev +// CHECK-SAME: func_03 + +template class C3 { +// CHECK: ClassTemplateDecl {{.*}} C3 + + template friend void func_03(C3 *x); + // CHECK: FriendDecl + // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]] + // CHECK-SAME: prev [[FWD]] + // CHECK-SAME: func_03 +}; + +template void func_03(C3 *x) {} +// CHECK: FunctionTemplateDecl +// CHECK-SAME: prev [[FRIEND]] +// CHECK-SAME: func_03 + +}