sepavloff created this revision.
sepavloff added a reviewer: rsmith.
sepavloff added a subscriber: cfe-commits.

When friend template function is parsed, it obtains wrong type. For
instance, in the code:

template<typename T1> void func(T1 *x);
template<typename T> struct C1 {
    template<typename T1> void func(T1 *x);
};

the friend function func gets canonical type 'void (template-param-1-0)',
while type of the same function  declared at file level is
'void (template-param-0-0)'. As a result, these two declarations are not
connected into redeclaration chain.

It occurs because beginning of template friend is parsed in the same way
as a declaration of member template. So template parameters get canonical
types as for a member template. Whether the declaration is a friend or
not can be determined only after declspec is parsed. It is too late, and
use of token look ahead is impractical to determine how parameters should
be handled. Type parameters should be fixed after friend declaration is
seen. They require shift of template parameter depth downward. It the
example above the shift would convert the type 'void (template-param-1-0)'
into 'void (template-param-0-0)'. However this change cannot be made for
all instances of template parameter, because friend function body may use
befriending class parameters as well, after the shift they would have the
same canonical representation as the friend function template parameters.

The fix implements update of canonical representation of friend template
function type. It allows to build up redeclaration chain correctly and
still does not break body of the friend function.

http://reviews.llvm.org/D19336

Files:
  include/clang/Sema/Sema.h
  lib/Sema/SemaDeclCXX.cpp
  lib/Sema/SemaTemplate.cpp
  test/SemaTemplate/friend-template-redecl.cpp

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<typename T> void func_01(T *x);
+// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]]
+// CHECK-NOT: prev
+// CHECK-SAME: func_01
+
+template<typename T> class C1 {
+// CHECK: ClassTemplateDecl {{.*}} C1
+
+  template<typename T1> 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<typename T1> 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<typename T> T *func_02(T *x);
+// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]]
+// CHECK-NOT: prev
+// CHECK-SAME: func_02
+
+template<typename T> class C2 {
+// CHECK: ClassTemplateDecl {{.*}} C2
+
+  template<typename T1> friend T *func_02(T1 *x);
+  // CHECK: FriendDecl
+  // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]]
+  // CHECK-NOT: prev
+  // CHECK-SAME: func_02
+};
+
+template<typename T1> 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<typename T> 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<typename T> class C1 {
+// CHECK: ClassTemplateDecl {{.*}} C1
+
+  template<typename T1> 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<typename T1> 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<typename T> void func_01(const T *, T *, T&, const T&);
+// CHECK: FunctionTemplateDecl
+// CHECK-SAME: func_01
+template<typename T> void func_01(T *, const T *, const T&, T&);
+// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]]
+// CHECK-NOT: prev
+// CHECK-SAME: func_01
+
+template<typename T> class C1 {
+// CHECK: ClassTemplateDecl {{.*}} C1
+
+  template<typename T1> 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<typename T1> 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<typename T> void func_01(T x[sizeof(T)]);
+// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]]
+// CHECK-NOT: prev
+// CHECK-SAME: func_01
+
+template<typename T> class C1 {
+// CHECK: ClassTemplateDecl {{.*}} C1
+
+  template<typename T1> 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<typename T1> void func_01(T1 x[sizeof(T1)]) {}
+// CHECK: FunctionTemplateDecl
+// CHECK-SAME: prev [[FRIEND]]
+// CHECK-SAME: func_01
+
+}
+
+
+namespace N6 {
+// CHECK-LABEL: NamespaceDecl {{.*}} N6
+
+template<typename T> class C3;
+template<typename T> void func_03(C3<T> *x);
+// CHECK: FunctionTemplateDecl [[FWD:0x[0-9a-f]+]]
+// CHECK-NOT: prev
+// CHECK-SAME: func_03
+
+template<typename T> class C3 {
+// CHECK: ClassTemplateDecl {{.*}} C3
+
+  template<typename T1> friend void func_03(C3<T1> *x);
+  // CHECK: FriendDecl
+  // CHECK-NEXT: FunctionTemplateDecl [[FRIEND:0x[0-9a-f]+]]
+  // CHECK-SAME: prev [[FWD]]
+  // CHECK-SAME: func_03
+};
+
+template<typename T1> void func_03(C3<T1> *x) {}
+// CHECK: FunctionTemplateDecl
+// CHECK-SAME: prev [[FRIEND]]
+// CHECK-SAME: func_03
+
+}
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<ClassTemplateDecl>(Template)) {
+    if (isa<ClassTemplateDecl>(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<typename T> class C1 {
+///         template<typename T1> 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<OuterParameterFinder> {
+  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<typename T> class C1 {
+///         template<typename T1> 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<TypeShifter> {
+  unsigned Shift;
+public:
+  TypeShifter(Sema &S, unsigned ShiftVal)
+    : TreeTransform<TypeShifter>(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<TemplateTypeParmTypeLoc>(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<typename T> class C1 {
+///         template<typename T1> friend void func_1(T1 *x);
+///         template<typename T1> 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: 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: 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,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to