]> granicus.if.org Git - clang/commitdiff
Rework the Parse-Sema interaction for friends to better support friend
authorDouglas Gregor <dgregor@apple.com>
Sat, 26 Sep 2009 06:47:28 +0000 (06:47 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 26 Sep 2009 06:47:28 +0000 (06:47 +0000)
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>.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82848 91177308-0d34-0410-b5e6-96231b3b80d8

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 [new file with mode: 0644]

index c018497907f63b1e6f86ac546720eb1e77d3ce61..6971df50cb551ed883f6d36d82e3e1583e4d9e60 100644 (file)
@@ -279,6 +279,10 @@ def err_expected_type_name_after_typename : Error<
 
 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
index 095b7ab38753cab646b5a89a8e79e58eb5321a3a..44b582d43c469466bdd7ffeb9e99c245a34545a0 100644 (file)
@@ -308,8 +308,6 @@ def err_static_assert_expression_is_not_constant : Error<
   "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 err_friend_is_member : Error<
 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<
index 59a6e6281c2e6615524806b9e76bee0c61504df8..bcf332bf3094ba1ab19e12c51e59d4e52eb677ff 100644 (file)
@@ -624,9 +624,22 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
   // '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 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
 
   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;
   }
 
index 1f53cde64b3977e3c3a278faaeebb6c59fc2e980..f8751fb9c9b0b955df0300b2fd1b9cb1591602ce 100644 (file)
@@ -1288,6 +1288,59 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
     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 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
     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 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
           = 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;
index 390a8048db2a8b2047ebc16628f12798925bf4a9..02c1a08c8f10704657413be8e92e3020241e74e5 100644 (file)
@@ -4141,14 +4141,6 @@ Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S,
     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
index d5fb7e82a8d35a3bc7f1836c03085735133eb145..72274525fd07221c54af6659632eb0eb0f9e65db 100644 (file)
@@ -577,6 +577,18 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
       return true;
     }
 
+    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 {
@@ -654,13 +666,6 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
   // 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 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
   (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 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
   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 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
         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) {
index f130beb7d2c3fb53b754217871fd9edab74ff882..98be2049e75663d532d0aca635ab2cd800071542 100644 (file)
@@ -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
 };
index 89f938d1f7d90277da11de5a62c5fd45fab405b6..657cf20cf312f841f10b88c43e0edd8d0f17cc8d 100644 (file)
@@ -13,7 +13,8 @@ class A1 {
   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}}
index dd856e9cc3bdb685667d0c173df941ccb1fb6938..4615bebe711fae6b9e5453172f98d8e094aa51e9 100644 (file)
@@ -7,5 +7,7 @@ template <class T> class A {
 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}}
 };
index 397894727b709ec59d2c8559e6b15691659f76c2..14b31af761d3df88e6082ef01f10f7a9c1155681 100644 (file)
@@ -21,8 +21,8 @@ class B {
   // '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();
diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp
new file mode 100644 (file)
index 0000000..6268cd8
--- /dev/null
@@ -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
+  {
+  };
+}