[cfe-commits] r99552 - in /cfe/trunk: lib/Sema/Lookup.h lib/Sema/SemaDecl.cpp test/CXX/temp/temp.decls/temp.friend/p1.cpp

John McCall rjmccall at apple.com
Thu Mar 25 14:28:06 PDT 2010


Author: rjmccall
Date: Thu Mar 25 16:28:06 2010
New Revision: 99552

URL: http://llvm.org/viewvc/llvm-project?rev=99552&view=rev
Log:
Handle simple friend-class decls in class templates better by ensuring that
we look for shadow friend decls in the appropriate scope before injecting
a new declaration.


Modified:
    cfe/trunk/lib/Sema/Lookup.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp

Modified: cfe/trunk/lib/Sema/Lookup.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Lookup.h?rev=99552&r1=99551&r2=99552&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Lookup.h (original)
+++ cfe/trunk/lib/Sema/Lookup.h Thu Mar 25 16:28:06 2010
@@ -397,6 +397,12 @@
     configure();
   }
 
+  /// \brief Change this lookup's redeclaration kind.
+  void setRedeclarationKind(Sema::RedeclarationKind RK) {
+    Redecl = RK;
+    configure();
+  }
+
   void print(llvm::raw_ostream &);
 
   /// Suppress the diagnostics that would normally fire because of this

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=99552&r1=99551&r2=99552&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Mar 25 16:28:06 2010
@@ -4774,6 +4774,79 @@
     }
   }
 
+  // If we didn't find a previous declaration, and this is a reference
+  // (or friend reference), move to the correct scope.  In C++, we
+  // also need to do a redeclaration lookup there, just in case
+  // there's a shadow friend decl.
+  if (Name && Previous.empty() &&
+      (TUK == TUK_Reference || TUK == TUK_Friend)) {
+    if (Invalid) goto CreateNewDecl;
+    assert(SS.isEmpty());
+
+    if (TUK == TUK_Reference) {
+      // C++ [basic.scope.pdecl]p5:
+      //   -- for an elaborated-type-specifier of the form
+      //
+      //          class-key identifier
+      //
+      //      if the elaborated-type-specifier is used in the
+      //      decl-specifier-seq or parameter-declaration-clause of a
+      //      function defined in namespace scope, the identifier is
+      //      declared as a class-name in the namespace that contains
+      //      the declaration; otherwise, except as a friend
+      //      declaration, the identifier is declared in the smallest
+      //      non-class, non-function-prototype scope that contains the
+      //      declaration.
+      //
+      // C99 6.7.2.3p8 has a similar (but not identical!) provision for
+      // C structs and unions.
+      //
+      // It is an error in C++ to declare (rather than define) an enum
+      // type, including via an elaborated type specifier.  We'll
+      // diagnose that later; for now, declare the enum in the same
+      // scope as we would have picked for any other tag type.
+      //
+      // GNU C also supports this behavior as part of its incomplete
+      // enum types extension, while GNU C++ does not.
+      //
+      // Find the context where we'll be declaring the tag.
+      // FIXME: We would like to maintain the current DeclContext as the
+      // lexical context,
+      while (SearchDC->isRecord())
+        SearchDC = SearchDC->getParent();
+
+      // Find the scope where we'll be declaring the tag.
+      while (S->isClassScope() ||
+             (getLangOptions().CPlusPlus &&
+              S->isFunctionPrototypeScope()) ||
+             ((S->getFlags() & Scope::DeclScope) == 0) ||
+             (S->getEntity() &&
+              ((DeclContext *)S->getEntity())->isTransparentContext()))
+        S = S->getParent();
+    } else {
+      assert(TUK == TUK_Friend);
+      // C++ [namespace.memdef]p3:
+      //   If a friend declaration in a non-local class first declares a
+      //   class or function, the friend class or function is a member of
+      //   the innermost enclosing namespace.
+      SearchDC = SearchDC->getEnclosingNamespaceContext();
+
+      // Look up through our scopes until we find one with an entity which
+      // matches our declaration context.
+      while (S->getEntity() &&
+             ((DeclContext *)S->getEntity())->getPrimaryContext() != SearchDC) {
+        S = S->getParent();
+        assert(S && "No enclosing scope matching the enclosing namespace.");
+      }
+    }
+
+    // In C++, look for a shadow friend decl.
+    if (getLangOptions().CPlusPlus) {
+      Previous.setRedeclarationKind(ForRedeclaration);
+      LookupQualifiedName(Previous, SearchDC);
+    }
+  }
+
   if (!Previous.empty()) {
     assert(Previous.isSingleResult());
     NamedDecl *PrevDecl = Previous.getFoundDecl();
@@ -4871,7 +4944,8 @@
     } else {
       // PrevDecl is a namespace, template, or anything else
       // that lives in the IDNS_Tag identifier namespace.
-      if (isDeclInScope(PrevDecl, SearchDC, S)) {
+      if (TUK == TUK_Reference || TUK == TUK_Friend ||
+          isDeclInScope(PrevDecl, SearchDC, S)) {
         // The tag name clashes with a namespace name, issue an error and
         // recover by making this tag be anonymous.
         Diag(NameLoc, diag::err_redefinition_different_kind) << Name;
@@ -4885,60 +4959,6 @@
         Previous.clear();
       }
     }
-  } else if (TUK == TUK_Reference && SS.isEmpty() && Name) {
-    // C++ [basic.scope.pdecl]p5:
-    //   -- for an elaborated-type-specifier of the form
-    //
-    //          class-key identifier
-    //
-    //      if the elaborated-type-specifier is used in the
-    //      decl-specifier-seq or parameter-declaration-clause of a
-    //      function defined in namespace scope, the identifier is
-    //      declared as a class-name in the namespace that contains
-    //      the declaration; otherwise, except as a friend
-    //      declaration, the identifier is declared in the smallest
-    //      non-class, non-function-prototype scope that contains the
-    //      declaration.
-    //
-    // C99 6.7.2.3p8 has a similar (but not identical!) provision for
-    // C structs and unions.
-    //
-    // It is an error in C++ to declare (rather than define) an enum
-    // type, including via an elaborated type specifier.  We'll
-    // diagnose that later; for now, declare the enum in the same
-    // scope as we would have picked for any other tag type.
-    //
-    // GNU C also supports this behavior as part of its incomplete
-    // enum types extension, while GNU C++ does not.
-    //
-    // Find the context where we'll be declaring the tag.
-    // FIXME: We would like to maintain the current DeclContext as the
-    // lexical context,
-    while (SearchDC->isRecord())
-      SearchDC = SearchDC->getParent();
-
-    // Find the scope where we'll be declaring the tag.
-    while (S->isClassScope() ||
-           (getLangOptions().CPlusPlus && S->isFunctionPrototypeScope()) ||
-           ((S->getFlags() & Scope::DeclScope) == 0) ||
-           (S->getEntity() &&
-            ((DeclContext *)S->getEntity())->isTransparentContext()))
-      S = S->getParent();
-
-  } else if (TUK == TUK_Friend && SS.isEmpty() && Name) {
-    // C++ [namespace.memdef]p3:
-    //   If a friend declaration in a non-local class first declares a
-    //   class or function, the friend class or function is a member of
-    //   the innermost enclosing namespace.
-    SearchDC = SearchDC->getEnclosingNamespaceContext();
-
-    // Look up through our scopes until we find one with an entity which
-    // matches our declaration context.
-    while (S->getEntity() &&
-           ((DeclContext *)S->getEntity())->getPrimaryContext() != SearchDC) {
-      S = S->getParent();
-      assert(S && "No enclosing scope matching the enclosing namespace.");
-    }
   }
 
 CreateNewDecl:
@@ -5055,7 +5075,7 @@
     New->setObjectOfFriendDecl(/* PreviouslyDeclared = */ !Previous.empty());
 
   // Set the access specifier.
-  if (!Invalid && TUK != TUK_Friend)
+  if (!Invalid && SearchDC->isRecord())
     SetMemberAccessSpecifier(New, PrevDecl, AS);
 
   if (TUK == TUK_Definition)
@@ -5068,13 +5088,11 @@
     if (PrevDecl)
       New->setAccess(PrevDecl->getAccess());
 
-    // Friend tag decls are visible in fairly strange ways.
-    if (!CurContext->isDependentContext()) {
-      DeclContext *DC = New->getDeclContext()->getLookupContext();
-      DC->makeDeclVisibleInContext(New, /* Recoverable = */ false);
+    DeclContext *DC = New->getDeclContext()->getLookupContext();
+    DC->makeDeclVisibleInContext(New, /* Recoverable = */ false);
+    if (Name) // can be null along some error paths
       if (Scope *EnclosingScope = getScopeForDeclContext(S, DC))
         PushOnScopeChains(New, EnclosingScope, /* AddToContext = */ false);
-    }
   } else if (Name) {
     S = getNonFieldDeclScope(S);
     PushOnScopeChains(New, S);

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp?rev=99552&r1=99551&r2=99552&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp Thu Mar 25 16:28:06 2010
@@ -1,4 +1,6 @@
 // RUN: %clang_cc1 -faccess-control -verify -emit-llvm-only %s
+
+namespace test0 {
 template <typename T> struct Num {
   T value_;
 
@@ -53,6 +55,7 @@
   Num<int> result = x * n;
   return result.get();
 }
+}
 
 // Reduced from GNU <locale>
 namespace test1 {
@@ -150,3 +153,28 @@
     friend X operator+<>(const X&, const value_type*);
   };
 }
+
+namespace test7 {
+  template <class T> class A { // expected-note {{previous definition is here}}
+    friend class B;
+    int x; // expected-note {{declared private here}}
+  };
+
+  class B {
+    int foo(A<int> &a) {
+      return a.x;
+    }
+  };
+
+  class C {
+    int foo(A<int> &a) {
+      return a.x; // expected-error {{'x' is a private member of 'test7::A<int>'}}
+    }
+  };
+
+  // This shouldn't crash.
+  template <class T> class D {
+    friend class A; // expected-error {{redefinition of 'A' as different kind of symbol}}
+  };
+  template class D<int>;
+}





More information about the cfe-commits mailing list