[cfe-commits] r82848 - in /cfe/trunk: include/clang/Basic/DiagnosticParseKinds.td include/clang/Basic/DiagnosticSemaKinds.td lib/Parse/ParseDeclCXX.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaTemplate.cpp test/CXX/class/class.friend/p2.cpp test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp test/CXX/temp/temp.decls/temp.friend/p3.cpp test/Parser/cxx-friend.cpp test/SemaTemplate/friend-template.cpp

Douglas Gregor dgregor at apple.com
Fri Sep 25 23:47:28 PDT 2009


Author: dgregor
Date: Sat Sep 26 01:47:28 2009
New Revision: 82848

URL: http://llvm.org/viewvc/llvm-project?rev=82848&view=rev
Log:
Rework the Parse-Sema interaction for friends to better support friend
class templates. We now treat friend class templates much more like
normal class templates, except that they still get special name lookup
rules. Fixes PR5057 and eliminates a bunch of spurious diagnostics in
<iostream>.


Added:
    cfe/trunk/test/SemaTemplate/friend-template.cpp   (with props)
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/CXX/class/class.friend/p2.cpp
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp
    cfe/trunk/test/Parser/cxx-friend.cpp

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

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Sat Sep 26 01:47:28 2009
@@ -279,6 +279,10 @@
 
 def err_variadic_templates : Error<
   "variadic templates are only allowed in C++0x">;
+  
+// C++ declarations
+def err_friend_decl_defines_class : Error<
+  "cannot define a type in a friend declaration">;
 
 // Language specific pragmas
 // - Generic warnings

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

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Sat Sep 26 01:47:28 2009
@@ -308,8 +308,6 @@
   "static_assert expression is not an integral constant expression">;
 def err_static_assert_failed : Error<"static_assert failed \"%0\"">;
 
-def err_friend_decl_defines_class : Error<
-  "cannot define a type in a friend declaration">;
 def err_unexpected_friend : Error<
   "friends can only be classes or functions">;
 def err_friend_is_member : Error<
@@ -317,8 +315,7 @@
 def ext_friend_inner_class : Extension<
   "C++ 98 does not allow inner classes as friends">;
 def err_unelaborated_friend_type : Error<
-  "must specify '%select{class|union}0' in a friend "
-  "%select{class|union}0 declaration">;
+  "must specify '%select{struct|union|class|enum}0' to befriend %1">;
 def err_qualified_friend_not_found : Error<
   "no function named %0 with type %1 was found in the specified scope">;
 def err_introducing_special_friend : Error<

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

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Sat Sep 26 01:47:28 2009
@@ -624,9 +624,22 @@
   // 'struct foo :...' then this is a definition. Otherwise we have
   // something like 'struct foo xyz', a reference.
   Action::TagUseKind TUK;
-  if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
-    TUK = Action::TUK_Definition;
-  else if (Tok.is(tok::semi))
+  if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon))) {
+    if (DS.isFriendSpecified()) {
+      // C++ [class.friend]p2:
+      //   A class shall not be defined in a friend declaration.
+      Diag(Tok.getLocation(), diag::err_friend_decl_defines_class)
+        << SourceRange(DS.getFriendSpecLoc());
+
+      // Skip everything up to the semicolon, so that this looks like a proper
+      // friend class (or template thereof) declaration.
+      SkipUntil(tok::semi, true, true);
+      TUK = Action::TUK_Friend;
+    } else {
+      // Okay, this is a class definition.
+      TUK = Action::TUK_Definition;
+    }
+  } else if (Tok.is(tok::semi))
     TUK = DS.isFriendSpecified() ? Action::TUK_Friend : Action::TUK_Declaration;
   else
     TUK = Action::TUK_Reference;
@@ -1043,12 +1056,7 @@
 
   if (Tok.is(tok::semi)) {
     ConsumeToken();
-
-    if (DS.isFriendSpecified()) {
-      Actions.ActOnFriendTypeDecl(CurScope, DS, move(TemplateParams));
-    } else
-      Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
-
+    Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
     return;
   }
 

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Sat Sep 26 01:47:28 2009
@@ -1288,6 +1288,59 @@
     Tag = dyn_cast<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
   }
 
+  if (DS.isFriendSpecified()) {
+    // We have a "friend" declaration that does not have a declarator.
+    // Look at the type to see if the friend declaration was handled
+    // elsewhere (e.g., for friend classes and friend class templates).
+    // If not, produce a suitable diagnostic or go try to befriend the
+    // type itself.
+    QualType T;
+    if (DS.getTypeSpecType() == DeclSpec::TST_typename ||
+        DS.getTypeSpecType() == DeclSpec::TST_typeofType)
+      T = QualType::getFromOpaquePtr(DS.getTypeRep());
+    else if (DS.getTypeSpecType() == DeclSpec::TST_typeofExpr ||
+             DS.getTypeSpecType() == DeclSpec::TST_decltype)
+      T = ((Expr *)DS.getTypeRep())->getType();
+    else if (DS.getTypeSpecType() == DeclSpec::TST_class ||
+             DS.getTypeSpecType() == DeclSpec::TST_struct ||
+             DS.getTypeSpecType() == DeclSpec::TST_union)
+      return DeclPtrTy::make(Tag);
+    
+    if (T.isNull()) {
+      // Fall through to diagnose this error, below.
+    } else if (const RecordType *RecordT = T->getAs<RecordType>()) {
+      // C++ [class.friend]p2:
+      //   An elaborated-type-specifier shall be used in a friend declaration
+      //   for a class.
+
+      // We have something like "friend C;", where C is the name of a
+      // class type but is missing an elaborated type specifier. Complain,
+      // but tell the user exactly how to fix the problem.
+      RecordDecl *RecordD = RecordT->getDecl();
+      Diag(DS.getTypeSpecTypeLoc(), diag::err_unelaborated_friend_type)
+        << (unsigned)RecordD->getTagKind()
+        << QualType(RecordT, 0)
+        << SourceRange(DS.getFriendSpecLoc())
+        << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
+                                  RecordD->getKindName() + std::string(" "));
+      
+      // FIXME: We could go into ActOnTag to actually make the friend
+      // declaration happen at this point.
+      return DeclPtrTy();
+    } 
+    
+    if (!T.isNull() && T->isDependentType()) {
+      // Since T is a dependent type, handle it as a friend type
+      // declaration.
+      return ActOnFriendTypeDecl(S, DS, MultiTemplateParamsArg(*this, 0, 0));
+    } 
+        
+    // Complain about any non-dependent friend type here.
+    Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
+      << DS.getSourceRange();
+    return DeclPtrTy();
+  }
+         
   if (RecordDecl *Record = dyn_cast_or_null<RecordDecl>(Tag)) {
     if (!Record->getDeclName() && Record->isDefinition() &&
         DS.getStorageClassSpec() != DeclSpec::SCS_typedef) {
@@ -1305,7 +1358,7 @@
     if (Record->getDeclName() && getLangOptions().Microsoft)
       return DeclPtrTy::make(Tag);
   }
-
+  
   if (!DS.isMissingDeclaratorOk() &&
       DS.getTypeSpecType() != DeclSpec::TST_error) {
     // Warn about typedefs of enums without names, since this is an
@@ -4022,11 +4075,7 @@
           = MatchTemplateParametersToScopeSpecifier(KWLoc, SS,
                         (TemplateParameterList**)TemplateParameterLists.get(),
                                               TemplateParameterLists.size())) {
-      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) {
+      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=82848&r1=82847&r2=82848&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Sat Sep 26 01:47:28 2009
@@ -4141,14 +4141,6 @@
     break;
   }
 
-  // C++ [class.friend]p2: A class shall not be defined inside
-  //   a friend declaration.
-  if (IsDefinition) {
-    Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class)
-      << DS.getSourceRange();
-    return DeclPtrTy();
-  }
-
   // C++98 [class.friend]p1: A friend of a class is a function
   //   or class that is not a member of the class . . .
   // But that's a silly restriction which nobody implements for

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Sat Sep 26 01:47:28 2009
@@ -579,6 +579,18 @@
 
     Previous = LookupQualifiedName(SemanticContext, Name, LookupOrdinaryName,
                                    true);
+  } else if (TUK == TUK_Friend) {
+    // C++ [namespace.memdef]p3:
+    //   [...] When looking for a prior declaration of a class or a function 
+    //   declared as a friend, and when the name of the friend class or 
+    //   function is neither a qualified name nor a template-id, scopes outside
+    //   the innermost enclosing namespace scope are not considered.
+    SemanticContext = CurContext;
+    while (!SemanticContext->isFileContext())
+      SemanticContext = SemanticContext->getLookupParent();
+    
+    Previous = LookupQualifiedName(SemanticContext, Name, LookupOrdinaryName,
+                                   true);
   } else {
     SemanticContext = CurContext;
     Previous = LookupName(S, Name, LookupOrdinaryName, true);
@@ -654,13 +666,6 @@
   // FIXME: If we had a scope specifier, we better have a previous template
   // declaration!
 
-  // If this is a friend declaration of an undeclared template,
-  // create the template in the innermost namespace scope.
-  if (TUK == TUK_Friend && !PrevClassTemplate) {
-    while (!SemanticContext->isFileContext())
-      SemanticContext = SemanticContext->getParent();
-  }
-
   CXXRecordDecl *NewClass =
     CXXRecordDecl::Create(Context, Kind, SemanticContext, NameLoc, Name, KWLoc,
                           PrevClassTemplate?
@@ -682,10 +687,7 @@
   (void)T;
 
   // Set the access specifier.
-  if (TUK == TUK_Friend)
-    NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */
-                                       PrevClassTemplate != NULL);
-  else 
+  if (!Invalid && TUK != TUK_Friend)
     SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS);
 
   // Set the lexical context of these templates
@@ -701,11 +703,14 @@
   if (TUK != TUK_Friend)
     PushOnScopeChains(NewTemplate, S);
   else {
-    // We might be replacing an existing declaration in the lookup tables;
-    // if so, borrow its access specifier.
-    if (PrevClassTemplate)
+    if (PrevClassTemplate && PrevClassTemplate->getAccess() != AS_none) {
       NewTemplate->setAccess(PrevClassTemplate->getAccess());
+      NewClass->setAccess(PrevClassTemplate->getAccess());
+    }
 
+    NewTemplate->setObjectOfFriendDecl(/* PreviouslyDeclared = */
+                                       PrevClassTemplate != NULL);
+    
     // Friend templates are visible in fairly strange ways.
     if (!CurContext->isDependentContext()) {
       DeclContext *DC = SemanticContext->getLookupContext();
@@ -714,6 +719,13 @@
         PushOnScopeChains(NewTemplate, EnclosingScope,
                           /* AddToContext = */ false);      
     }
+    
+    FriendDecl *Friend = FriendDecl::Create(Context, CurContext,
+                                            NewClass->getLocation(),
+                                            NewTemplate,
+                                    /*FIXME:*/NewClass->getLocation());
+    Friend->setAccess(AS_public);
+    CurContext->addDecl(Friend);
   }
 
   if (Invalid) {

Modified: cfe/trunk/test/CXX/class/class.friend/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p2.cpp?rev=82848&r1=82847&r2=82848&view=diff

==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p2.cpp (original)
+++ cfe/trunk/test/CXX/class/class.friend/p2.cpp Sat Sep 26 01:47:28 2009
@@ -1,8 +1,10 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 
+struct B0;
+
 class A {
   friend class B {}; // expected-error {{cannot define a type in a friend declaration}}
   friend int; // expected-error {{friends can only be classes or functions}}
-  friend B; // expected-error {{must specify 'class' in a friend class declaration}}
+  friend B0; // expected-error {{must specify 'struct' to befriend}}
   friend class C; // okay
 };

Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp?rev=82848&r1=82847&r2=82848&view=diff

==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp (original)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp Sat Sep 26 01:47:28 2009
@@ -13,7 +13,8 @@
   friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}}
 
   // FIXME: a better error would be something like 'enum types cannot be friends'
-  friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}}
+  friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} \
+                // expected-error{{classes or functions}}
 };
 
 template <class T> struct B { // expected-note {{previous use is here}}

Modified: 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=82848&r1=82847&r2=82848&view=diff

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p3.cpp Sat Sep 26 01:47:28 2009
@@ -7,5 +7,7 @@
 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}}
+  
+  // FIXME: Diagnostic below could be (and was) better.
+  template <class T> friend typename A<T>::Member; // expected-error {{classes or functions}}
 };

Modified: cfe/trunk/test/Parser/cxx-friend.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-friend.cpp?rev=82848&r1=82847&r2=82848&view=diff

==============================================================================
--- cfe/trunk/test/Parser/cxx-friend.cpp (original)
+++ cfe/trunk/test/Parser/cxx-friend.cpp Sat Sep 26 01:47:28 2009
@@ -21,8 +21,8 @@
   // 'A' here should refer to the declaration above.  
   friend class A;
 
-  friend C; // expected-error {{must specify 'class' in a friend class declaration}}
-  friend U; // expected-error {{must specify 'union' in a friend union declaration}}
+  friend C; // expected-error {{must specify 'class' to befriend}}
+  friend U; // expected-error {{must specify 'union' to befriend}}
   friend int; // expected-error {{friends can only be classes or functions}}
 
   friend void myfunc();

Added: cfe/trunk/test/SemaTemplate/friend-template.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/friend-template.cpp?rev=82848&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/friend-template.cpp (added)
+++ cfe/trunk/test/SemaTemplate/friend-template.cpp Sat Sep 26 01:47:28 2009
@@ -0,0 +1,17 @@
+// RUN: clang-cc -fsyntax-only %s
+
+// PR5057
+namespace std {
+  class X {
+  public:
+    template<typename T>
+    friend struct Y;
+  };
+}
+
+namespace std {
+  template<typename T>
+  struct Y
+  {
+  };
+}

Propchange: cfe/trunk/test/SemaTemplate/friend-template.cpp

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/SemaTemplate/friend-template.cpp

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/SemaTemplate/friend-template.cpp

------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list