[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