[cfe-commits] r100635 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/class.access/class.friend/p2-cxx03.cpp test/CXX/class.access/class.friend/p3-cxx0x.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

Douglas Gregor dgregor at apple.com
Wed Apr 7 10:57:13 PDT 2010


Author: dgregor
Date: Wed Apr  7 12:57:12 2010
New Revision: 100635

URL: http://llvm.org/viewvc/llvm-project?rev=100635&view=rev
Log:
Improve handling of friend types in several ways:
  - When instantiating a friend type template, perform semantic
  analysis on the resulting type.
  - Downgrade the errors concerning friend type declarations that do
  not refer to classes to ExtWarns in C++98/03. C++0x allows
  practically any type to be befriended, and ignores the friend
  declaration if the type is not a class.


Added:
    cfe/trunk/test/CXX/class.access/class.friend/p2-cxx03.cpp
    cfe/trunk/test/CXX/class.access/class.friend/p3-cxx0x.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.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/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=100635&r1=100634&r2=100635&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Apr  7 12:57:12 2010
@@ -382,11 +382,13 @@
 
 def err_unexpected_friend : Error<
   "friends can only be classes or functions">;
-def err_enum_friend : Error<
-  "enum types cannot be friends">;
+def ext_enum_friend : ExtWarn<
+  "enumeration type %0 cannot be a friend">;
+def ext_nonclass_type_friend : ExtWarn<
+  "non-class type %0 cannot be a friend">;
 def err_friend_is_member : Error<
   "friends cannot be members of the declaring class">;
-def err_unelaborated_friend_type : Error<
+def ext_unelaborated_friend_type : ExtWarn<
   "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">;

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=100635&r1=100634&r2=100635&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Apr  7 12:57:12 2010
@@ -5328,39 +5328,48 @@
   QualType T = TSInfo->getType();
   SourceRange TypeRange = TSInfo->getTypeLoc().getSourceRange();
   
-  // C++03 [class.friend]p2:
-  //   An elaborated-type-specifier shall be used in a friend declaration
-  //   for a class.*
-  //
-  //   * The class-key of the elaborated-type-specifier is required.
-  if (!getLangOptions().CPlusPlus0x && !T->isElaboratedTypeSpecifier()) {
-    // If we evaluated the type to a record type, suggest putting
-    // a tag in front.
-    if (const RecordType *RT = T->getAs<RecordType>()) {
-      RecordDecl *RD = RT->getDecl();
-      
-      std::string InsertionText = std::string(" ") + RD->getKindName();
-      
-      Diag(FriendLoc, diag::err_unelaborated_friend_type)
-        << (unsigned) RD->getTagKind()
+  if (!getLangOptions().CPlusPlus0x) {
+    // C++03 [class.friend]p2:
+    //   An elaborated-type-specifier shall be used in a friend declaration
+    //   for a class.*
+    //
+    //   * The class-key of the elaborated-type-specifier is required.
+    if (!ActiveTemplateInstantiations.empty()) {
+      // Do not complain about the form of friend template types during
+      // template instantiation; we will already have complained when the
+      // template was declared.
+    } else if (!T->isElaboratedTypeSpecifier()) {
+      // If we evaluated the type to a record type, suggest putting
+      // a tag in front.
+      if (const RecordType *RT = T->getAs<RecordType>()) {
+        RecordDecl *RD = RT->getDecl();
+        
+        std::string InsertionText = std::string(" ") + RD->getKindName();
+        
+        Diag(TypeRange.getBegin(), diag::ext_unelaborated_friend_type)
+          << (unsigned) RD->getTagKind()
+          << T
+          << FixItHint::CreateInsertion(PP.getLocForEndOfToken(FriendLoc),
+                                        InsertionText);
+      } else {
+        Diag(FriendLoc, diag::ext_nonclass_type_friend)
+          << T
+          << SourceRange(FriendLoc, TypeRange.getEnd());
+      }
+    } else if (T->getAs<EnumType>()) {
+      Diag(FriendLoc, diag::ext_enum_friend)
         << T
-        << SourceRange(FriendLoc)
-        << FixItHint::CreateInsertion(TypeRange.getBegin(),
-                                      InsertionText);
-      return 0; 
-    } else {
-      Diag(FriendLoc, diag::err_unexpected_friend)
         << SourceRange(FriendLoc, TypeRange.getEnd());
-      return 0;
     }
   }
   
-  // Enum types cannot be friends.
-  if (T->getAs<EnumType>()) {
-    Diag(FriendLoc, diag::err_enum_friend)
-      << SourceRange(FriendLoc, TypeRange.getEnd());
-    return 0;
-  }  
+  // C++0x [class.friend]p3:
+  //   If the type specifier in a friend declaration designates a (possibly
+  //   cv-qualified) class type, that class is declared as a friend; otherwise, 
+  //   the friend declaration is ignored.
+  
+  // FIXME: C++0x has some syntactic restrictions on friend type declarations
+  // in [class.friend]p3 that we do not implement.
   
   return FriendDecl::Create(Context, CurContext, FriendLoc, TSInfo, FriendLoc);
 }
@@ -5421,13 +5430,6 @@
     return DeclPtrTy();
   }
   
-  // Enum templates cannot be friends.
-  if (TempParams.size() && T->getAs<EnumType>()) {
-    Diag(DS.getTypeSpecTypeLoc(), diag::err_enum_friend)
-    << SourceRange(DS.getFriendSpecLoc());
-    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 . . .
   // This is fixed in DR77, which just barely didn't make the C++03

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=100635&r1=100634&r2=100635&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Wed Apr  7 12:57:12 2010
@@ -476,47 +476,44 @@
 }
 
 Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
-  FriendDecl::FriendUnion FU;
-
   // Handle friend type expressions by simply substituting template
-  // parameters into the pattern type.
+  // parameters into the pattern type and checking the result.
   if (TypeSourceInfo *Ty = D->getFriendType()) {
     TypeSourceInfo *InstTy = 
       SemaRef.SubstType(Ty, TemplateArgs,
                         D->getLocation(), DeclarationName());
-    if (!InstTy) return 0;
-
-    // This assertion is valid because the source type was necessarily
-    // an elaborated-type-specifier with a record tag.
-    assert(getLangOptions().CPlusPlus0x || InstTy->getType()->isRecordType());
-
-    FU = InstTy;
-
-  // Handle everything else by appropriate substitution.
-  } else {
-    NamedDecl *ND = D->getFriendDecl();
-    assert(ND && "friend decl must be a decl or a type!");
-
-    // FIXME: We have a problem here, because the nested call to Visit(ND)
-    // will inject the thing that the friend references into the current
-    // owner, which is wrong.
-    Decl *NewND;
-
-    // Hack to make this work almost well pending a rewrite.
-    if (D->wasSpecialization()) {
-      // Totally egregious hack to work around PR5866
+    if (!InstTy) 
       return 0;
-    } else {
-      NewND = Visit(ND);
-    }
-    if (!NewND) return 0;
 
-    FU = cast<NamedDecl>(NewND);
+    FriendDecl *FD = SemaRef.CheckFriendTypeDecl(D->getFriendLoc(), InstTy);
+    if (!FD)
+      return 0;
+    
+    FD->setAccess(AS_public);
+    Owner->addDecl(FD);
+    return FD;
+  } 
+  
+  NamedDecl *ND = D->getFriendDecl();
+  assert(ND && "friend decl must be a decl or a type!");
+
+  // FIXME: We have a problem here, because the nested call to Visit(ND)
+  // will inject the thing that the friend references into the current
+  // owner, which is wrong.
+  Decl *NewND;
+
+  // Hack to make this work almost well pending a rewrite.
+  if (D->wasSpecialization()) {
+    // Totally egregious hack to work around PR5866
+    return 0;
+  } else {
+    NewND = Visit(ND);
   }
+  if (!NewND) return 0;
 
   FriendDecl *FD =
-    FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), FU,
-                       D->getFriendLoc());
+    FriendDecl::Create(SemaRef.Context, Owner, D->getLocation(), 
+                       cast<NamedDecl>(NewND), D->getFriendLoc());
   FD->setAccess(AS_public);
   Owner->addDecl(FD);
   return FD;

Added: cfe/trunk/test/CXX/class.access/class.friend/p2-cxx03.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/class.friend/p2-cxx03.cpp?rev=100635&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.friend/p2-cxx03.cpp (added)
+++ cfe/trunk/test/CXX/class.access/class.friend/p2-cxx03.cpp Wed Apr  7 12:57:12 2010
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+template<typename T>
+class X0 {
+  friend T; // expected-warning{{non-class type 'T' cannot be a friend}}
+};
+
+class X1 { };
+enum E1 { };
+X0<X1> x0a;
+X0<X1 *> x0b;
+X0<int> x0c;
+X0<E1> x0d;
+

Added: cfe/trunk/test/CXX/class.access/class.friend/p3-cxx0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/class.friend/p3-cxx0x.cpp?rev=100635&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.friend/p3-cxx0x.cpp (added)
+++ cfe/trunk/test/CXX/class.access/class.friend/p3-cxx0x.cpp Wed Apr  7 12:57:12 2010
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++0x -verify %s
+template<typename T>
+class X0 {
+  friend T;
+};
+
+class Y1 { };
+enum E1 { };
+X0<Y1> x0a;
+X0<Y1 *> x0b;
+X0<int> x0c;
+X0<E1> x0d;
+
+template<typename T>
+class X1 {
+  friend typename T::type; // expected-error{{no type named 'type' in 'Y1'}}
+};
+
+struct Y2 {
+  struct type { };
+};
+
+struct Y3 {
+  typedef int type;
+};
+
+X1<Y2> x1a;
+X1<Y3> x1b;
+X1<Y1> x1c; // expected-note{{in instantiation of template class 'X1<Y1>' requested here}}

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=100635&r1=100634&r2=100635&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p2.cpp (original)
+++ cfe/trunk/test/CXX/class/class.friend/p2.cpp Wed Apr  7 12:57:12 2010
@@ -4,7 +4,7 @@
 
 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 B0; // expected-error {{must specify 'struct' to befriend}}
+  friend int; // expected-warning {{non-class type 'int' cannot be a friend}}
+  friend B0; // expected-warning {{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=100635&r1=100634&r2=100635&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 Wed Apr  7 12:57:12 2010
@@ -13,7 +13,7 @@
   friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}}
 
   friend enum A; // expected-error {{ISO C++ forbids forward references to 'enum' types}} \
-                 // expected-error {{enum types cannot be friends}}
+                 // expected-warning {{cannot be a friend}}
 };
 
 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=100635&r1=100634&r2=100635&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 Wed Apr  7 12:57:12 2010
@@ -8,6 +8,5 @@
   template <class T> friend class A;
   template <class T> friend class Undeclared;
   
-  // FIXME: Diagnostic below could be (and was) better.
-  template <class T> friend typename A<T>::Member; // expected-error {{classes or functions}}
+  template <class T> friend typename A<T>::Member; // expected-warning {{non-class type 'typename A<T>::Member' cannot be a friend}}
 };

Modified: cfe/trunk/test/Parser/cxx-friend.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-friend.cpp?rev=100635&r1=100634&r2=100635&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-friend.cpp (original)
+++ cfe/trunk/test/Parser/cxx-friend.cpp Wed Apr  7 12:57:12 2010
@@ -21,9 +21,9 @@
   // 'A' here should refer to the declaration above.  
   friend class A;
 
-  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 C; // expected-warning {{must specify 'class' to befriend}}
+  friend U; // expected-warning {{must specify 'union' to befriend}}
+  friend int; // expected-warning {{non-class type 'int' cannot be a friend}}
 
   friend void myfunc();
 





More information about the cfe-commits mailing list