[cfe-commits] r82088 - in /cfe/trunk: include/clang/AST/DeclNodes.def include/clang/AST/DeclTemplate.h include/clang/Basic/DiagnosticSemaKinds.td include/clang/Parse/Action.h lib/AST/DeclBase.cpp lib/AST/DeclTemplate.cpp lib/Parse/ParseDeclCXX.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp test/CXX/temp/temp.decls/temp.friend/p3.cpp test/CXX/temp/temp.decls/temp.friend/p5.cpp

John McCall rjmccall at apple.com
Wed Sep 16 15:47:08 PDT 2009


Author: rjmccall
Date: Wed Sep 16 17:47:08 2009
New Revision: 82088

URL: http://llvm.org/viewvc/llvm-project?rev=82088&view=rev
Log:
Improved representation and support for friend class templates.  Angst about same.


Added:
    cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp
Modified:
    cfe/trunk/include/clang/AST/DeclNodes.def
    cfe/trunk/include/clang/AST/DeclTemplate.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/AST/DeclTemplate.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p5.cpp

Modified: cfe/trunk/include/clang/AST/DeclNodes.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclNodes.def?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclNodes.def (original)
+++ cfe/trunk/include/clang/AST/DeclNodes.def Wed Sep 16 17:47:08 2009
@@ -127,6 +127,7 @@
 DECL(ObjCClass, Decl)
 DECL(FileScopeAsm, Decl)
 DECL(Friend, Decl)
+DECL(FriendTemplate, Decl)
 DECL(StaticAssert, Decl)
 LAST_DECL(Block, Decl)
 

Modified: cfe/trunk/include/clang/AST/DeclTemplate.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclTemplate.h?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclTemplate.h (original)
+++ cfe/trunk/include/clang/AST/DeclTemplate.h Wed Sep 16 17:47:08 2009
@@ -1227,6 +1227,85 @@
   virtual void Destroy(ASTContext& C);
 };
 
+/// Declaration of a friend template.  For example:
+///
+/// template <typename T> class A {
+///   friend class MyVector<T>; // not a friend template
+///   template <typename U> friend class B; // friend template
+///   template <typename U> friend class Foo<T>::Nested; // friend template
+class FriendTemplateDecl : public Decl {
+public:
+  typedef llvm::PointerUnion<NamedDecl*,Type*> FriendUnion;
+
+private:
+  // The number of template parameters;  always non-zero.
+  unsigned NumParams;
+
+  // The parameter list.
+  TemplateParameterList **Params;
+
+  // The declaration that's a friend of this class.
+  FriendUnion Friend;
+
+  // Location of the 'friend' specifier.
+  SourceLocation FriendLoc;
+
+
+  FriendTemplateDecl(DeclContext *DC, SourceLocation Loc,
+                     unsigned NParams, 
+                     TemplateParameterList **Params,
+                     FriendUnion Friend,
+                     SourceLocation FriendLoc)
+    : Decl(Decl::FriendTemplate, DC, Loc),
+      NumParams(NParams),
+      Params(Params),
+      Friend(Friend),
+      FriendLoc(FriendLoc)
+  {}
+
+public:
+  static FriendTemplateDecl *Create(ASTContext &Context,
+                                    DeclContext *DC, SourceLocation Loc,
+                                    unsigned NParams, 
+                                    TemplateParameterList **Params,
+                                    FriendUnion Friend,
+                                    SourceLocation FriendLoc);
+
+  /// If this friend declaration names a templated type (or
+  /// a dependent member type of a templated type), return that
+  /// type;  otherwise return null.
+  Type *getFriendType() const {
+    return Friend.dyn_cast<Type*>();
+  }
+
+  /// If this friend declaration names a templated function (or
+  /// a member function of a templated type), return that type;
+  /// otherwise return null.
+  NamedDecl *getFriendDecl() const {
+    return Friend.dyn_cast<NamedDecl*>();
+  }
+
+  /// Retrieves the location of the 'friend' keyword.
+  SourceLocation getFriendLoc() const {
+    return FriendLoc;
+  }
+
+  TemplateParameterList *getTemplateParameterList(unsigned i) const {
+    assert(i <= NumParams);
+    return Params[i];
+  }
+
+  unsigned getNumTemplateParameters() const {
+    return NumParams;
+  }
+
+  // Implement isa/cast/dyncast/etc.
+  static bool classof(const Decl *D) {
+    return D->getKind() == Decl::FriendTemplate;
+  }
+  static bool classof(const FriendTemplateDecl *D) { return true; }
+};
+
 /// Implementation of inline functions that require the template declarations
 inline AnyFunctionDecl::AnyFunctionDecl(FunctionTemplateDecl *FTD)
   : Function(FTD) { }

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Sep 16 17:47:08 2009
@@ -324,6 +324,8 @@
 def err_introducing_special_friend : Error<
   "must use a qualified name when declaring a %select{constructor|"
   "destructor|conversion operator}0 as a friend">;
+def err_tagless_friend_type_template : Error<
+  "friend type templates must use an elaborated type">;
 
 def err_abstract_type_in_decl : Error<
   "%select{return|parameter|variable|field}0 type %1 is an abstract class">;

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Wed Sep 16 17:47:08 2009
@@ -1215,7 +1215,7 @@
   /// ActOnFriendTypeDecl - Parsed a friend type declaration.
   virtual DeclPtrTy ActOnFriendTypeDecl(Scope *S,
                                         const DeclSpec &DS,
-                                        bool IsTemplate) {
+                                        MultiTemplateParamsArg TParams) {
     return DeclPtrTy();
   }
 

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Wed Sep 16 17:47:08 2009
@@ -230,6 +230,7 @@
 
     // Never have names.
     case Friend:
+    case FriendTemplate:
     case LinkageSpec:
     case FileScopeAsm:
     case StaticAssert:

Modified: cfe/trunk/lib/AST/DeclTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclTemplate.cpp?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/lib/AST/DeclTemplate.cpp (original)
+++ cfe/trunk/lib/AST/DeclTemplate.cpp Wed Sep 16 17:47:08 2009
@@ -463,3 +463,19 @@
   Context.getTypeDeclType(Result, PrevDecl);
   return Result;
 }
+
+//===----------------------------------------------------------------------===//
+// FriendTemplateDecl Implementation
+//===----------------------------------------------------------------------===//
+
+FriendTemplateDecl *FriendTemplateDecl::Create(ASTContext &Context,
+                                               DeclContext *DC,
+                                               SourceLocation L,
+                                               unsigned NParams,
+                                               TemplateParameterList **Params,
+                                               FriendUnion Friend,
+                                               SourceLocation FLoc) {
+  FriendTemplateDecl *Result
+    = new (Context) FriendTemplateDecl(DC, L, NParams, Params, Friend, FLoc);
+  return Result;
+}

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Sep 16 17:47:08 2009
@@ -1011,12 +1011,15 @@
   DeclSpec DS;
   ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class);
 
+  Action::MultiTemplateParamsArg TemplateParams(Actions,
+      TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0,
+      TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0);
+
   if (Tok.is(tok::semi)) {
     ConsumeToken();
 
     if (DS.isFriendSpecified()) {
-      bool IsTemplate = TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate;
-      Actions.ActOnFriendTypeDecl(CurScope, DS, IsTemplate);
+      Actions.ActOnFriendTypeDecl(CurScope, DS, move(TemplateParams));
     } else
       Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
 
@@ -1119,10 +1122,6 @@
     // this call will *not* return the created decl; It will return null.
     // See Sema::ActOnCXXMemberDeclarator for details.
 
-    Action::MultiTemplateParamsArg TemplateParams(Actions,
-        TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->data() : 0,
-        TemplateInfo.TemplateParams? TemplateInfo.TemplateParams->size() : 0);
-
     DeclPtrTy ThisDecl;
     if (DS.isFriendSpecified()) {
       // TODO: handle initializers, bitfields, 'delete'

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Sep 16 17:47:08 2009
@@ -2248,7 +2248,8 @@
                                                  ExprArg AssertExpr,
                                                  ExprArg AssertMessageExpr);
 
-  DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, bool IsTemplate);
+  DeclPtrTy ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
+                                MultiTemplateParamsArg TemplateParams);
   DeclPtrTy ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition,
                                     MultiTemplateParamsArg TemplateParams);
 

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Sep 16 17:47:08 2009
@@ -3957,7 +3957,11 @@
           = MatchTemplateParametersToScopeSpecifier(KWLoc, SS,
                         (TemplateParameterList**)TemplateParameterLists.get(),
                                               TemplateParameterLists.size())) {
-      if (TemplateParams->size() > 0) {
+      if (TUK == TUK_Friend) {
+        // When declaring a friend template, we do want to match the
+        // template parameters to the scope specifier, but don't go so far
+        // as to try to declare a new template.
+      } else if (TemplateParams->size() > 0) {
         // This is a declaration or definition of a class template (which may
         // be a member of another template).
         OwnedDecl = false;

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Sep 16 17:47:08 2009
@@ -4023,41 +4023,58 @@
   return DeclPtrTy::make(Decl);
 }
 
+/// Handle a friend type declaration.  This works in tandem with
+/// ActOnTag.
+///
+/// Notes on friend class templates:
+///
+/// We generally treat friend class declarations as if they were
+/// declaring a class.  So, for example, the elaborated type specifier
+/// in a friend declaration is required to obey the restrictions of a
+/// class-head (i.e. no typedefs in the scope chain), template
+/// parameters are required to match up with simple template-ids, &c.
+/// However, unlike when declaring a template specialization, it's
+/// okay to refer to a template specialization without an empty
+/// template parameter declaration, e.g.
+///   friend class A<T>::B<unsigned>;
+/// We permit this as a special case; if there are any template
+/// parameters present at all, require proper matching, i.e.
+///   template <> template <class T> friend class A<int>::B;
 Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
                                           const DeclSpec &DS,
-                                          bool IsTemplate) {
+                                          MultiTemplateParamsArg TempParams) {
   SourceLocation Loc = DS.getSourceRange().getBegin();
 
   assert(DS.isFriendSpecified());
   assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
 
-  // Handle friend templates specially.
-  if (IsTemplate) {
-    Decl *D;
-    switch (DS.getTypeSpecType()) {
-    default:
-      // FIXME: implement this
-      assert(false && "unelaborated type templates are currently unimplemented!");
-    case DeclSpec::TST_class:
-    case DeclSpec::TST_union:
-    case DeclSpec::TST_struct:
-      D = (Decl*) DS.getTypeRep();
-    }
-
-    ClassTemplateDecl *Temp = cast<ClassTemplateDecl>(D);
-    FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, Temp,
-                                        DS.getFriendSpecLoc());
-    FD->setAccess(AS_public);
-    CurContext->addDecl(FD);
-
-    return DeclPtrTy::make(FD);
-  }
-
-  // Try to convert the decl specifier to a type.
+  // Try to convert the decl specifier to a type.  This works for
+  // friend templates because ActOnTag never produces a ClassTemplateDecl
+  // for a TUK_Friend.
   bool invalid = false;
   QualType T = ConvertDeclSpecToType(DS, Loc, invalid);
   if (invalid) return DeclPtrTy();
 
+  // This is definitely an error in C++98.  It's probably meant to
+  // be forbidden in C++0x, too, but the specification is just
+  // poorly written.
+  //
+  // The problem is with declarations like the following:
+  //   template <T> friend A<T>::foo;
+  // where deciding whether a class C is a friend or not now hinges
+  // on whether there exists an instantiation of A that causes
+  // 'foo' to equal C.  There are restrictions on class-heads
+  // (which we declare (by fiat) elaborated friend declarations to
+  // be) that makes this tractable.
+  //
+  // FIXME: handle "template <> friend class A<T>;", which
+  // is possibly well-formed?  Who even knows?
+  if (TempParams.size() && !isa<ElaboratedType>(T)) {
+    Diag(Loc, diag::err_tagless_friend_type_template)
+      << DS.getSourceRange();
+    return DeclPtrTy();
+  }
+
   // C++ [class.friend]p2:
   //   An elaborated-type-specifier shall be used in a friend declaration
   //   for a class.*
@@ -4085,7 +4102,6 @@
   }
 
   bool IsDefinition = false;
-  FriendDecl::FriendUnion FU = T.getTypePtr();
 
   // We want to do a few things differently if the type was declared with
   // a tag:  specifically, we want to use the associated RecordDecl as
@@ -4097,10 +4113,8 @@
   case DeclSpec::TST_struct:
   case DeclSpec::TST_union:
     CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep());
-    if (RD) {
+    if (RD)
       IsDefinition |= RD->isDefinition();
-      FU = RD;
-    }
     break;
   }
 
@@ -4122,12 +4136,20 @@
       if (RT->getDecl()->getDeclContext() == CurContext)
         Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class);
 
-  FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU,
-                                      DS.getFriendSpecLoc());
-  FD->setAccess(AS_public);
-  CurContext->addDecl(FD);
+  Decl *D;
+  if (TempParams.size())
+    D = FriendTemplateDecl::Create(Context, CurContext, Loc,
+                                   TempParams.size(),
+                                 (TemplateParameterList**) TempParams.release(),
+                                   T.getTypePtr(),
+                                   DS.getFriendSpecLoc());
+  else
+    D = FriendDecl::Create(Context, CurContext, Loc, T.getTypePtr(),
+                           DS.getFriendSpecLoc());
+  D->setAccess(AS_public);
+  CurContext->addDecl(D);
 
-  return DeclPtrTy::make(FD);
+  return DeclPtrTy::make(D);
 }
 
 Sema::DeclPtrTy

Added: cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp?rev=82088&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp Wed Sep 16 17:47:08 2009
@@ -0,0 +1,11 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template <class T> class A {
+  typedef int Member;
+};
+
+class B {
+  template <class T> friend class A;
+  template <class T> friend class Undeclared;
+  template <class T> friend typename A<T>::Member; // expected-error {{friend type templates must use an elaborated type}}
+};

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p5.cpp?rev=82088&r1=82087&r2=82088&view=diff

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p5.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p5.cpp Wed Sep 16 17:47:08 2009
@@ -1,6 +1,11 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 
-class A {
-  template <class T> friend class B;
+template <class T> class A {
+  class Member {
+  };
+};
+
+class B {
+  template <class T> friend class A<T>::Member;
 };
 





More information about the cfe-commits mailing list