Recommit my change to how C++ does elaborated type lookups, now with

two bugfixes which fix selfhost and (hopefully) the nightly tests.

llvm-svn: 102198
This commit is contained in:
John McCall 2010-04-23 18:46:30 +00:00
parent 126a58e084
commit e87beb2591
13 changed files with 277 additions and 105 deletions

View File

@ -76,24 +76,63 @@ public:
#include "clang/AST/DeclNodes.def"
};
/// IdentifierNamespace - According to C99 6.2.3, there are four
/// namespaces, labels, tags, members and ordinary
/// identifiers. These are meant as bitmasks, so that searches in
/// C++ can look into the "tag" namespace during ordinary lookup. We
/// use additional namespaces for Objective-C entities. We also put
/// C++ friend declarations (of previously-undeclared entities) in
/// shadow namespaces, and 'using' declarations (as opposed to their
/// implicit shadow declarations) can be found in their own
/// namespace.
/// IdentifierNamespace - The different namespaces in which
/// declarations may appear. According to C99 6.2.3, there are
/// four namespaces, labels, tags, members and ordinary
/// identifiers. C++ describes lookup completely differently:
/// certain lookups merely "ignore" certain kinds of declarations,
/// usually based on whether the declaration is of a type, etc.
///
/// These are meant as bitmasks, so that searches in
/// C++ can look into the "tag" namespace during ordinary lookup.
///
/// Decl currently provides 16 bits of IDNS bits.
enum IdentifierNamespace {
IDNS_Label = 0x1,
IDNS_Tag = 0x2,
IDNS_Member = 0x4,
IDNS_Ordinary = 0x8,
IDNS_ObjCProtocol = 0x10,
IDNS_OrdinaryFriend = 0x80,
IDNS_TagFriend = 0x100,
IDNS_Using = 0x200
/// Labels, declared with 'x:' and referenced with 'goto x'.
IDNS_Label = 0x0001,
/// Tags, declared with 'struct foo;' and referenced with
/// 'struct foo'. All tags are also types. This is what
/// elaborated-type-specifiers look for in C.
IDNS_Tag = 0x0002,
/// Types, declared with 'struct foo', typedefs, etc.
/// This is what elaborated-type-specifiers look for in C++,
/// but note that it's ill-formed to find a non-tag.
IDNS_Type = 0x0004,
/// Members, declared with object declarations within tag
/// definitions. In C, these can only be found by "qualified"
/// lookup in member expressions. In C++, they're found by
/// normal lookup.
IDNS_Member = 0x0008,
/// Namespaces, declared with 'namespace foo {}'.
/// Lookup for nested-name-specifiers find these.
IDNS_Namespace = 0x0010,
/// Ordinary names. In C, everything that's not a label, tag,
/// or member ends up here.
IDNS_Ordinary = 0x0020,
/// Objective C @protocol.
IDNS_ObjCProtocol = 0x0040,
/// This declaration is a friend function. A friend function
/// declaration is always in this namespace but may also be in
/// IDNS_Ordinary if it was previously declared.
IDNS_OrdinaryFriend = 0x0080,
/// This declaration is a friend class. A friend class
/// declaration is always in this namespace but may also be in
/// IDNS_Tag|IDNS_Type if it was previously declared.
IDNS_TagFriend = 0x0100,
/// This declaration is a using declaration. A using declaration
/// *introduces* a number of other declarations into the current
/// scope, and those declarations use the IDNS of their targets,
/// but the actual using declarations go in this namespace.
IDNS_Using = 0x0200
};
/// ObjCDeclQualifier - Qualifier used on types in method declarations
@ -311,6 +350,13 @@ public:
}
static unsigned getIdentifierNamespaceForKind(Kind DK);
bool hasTagIdentifierNamespace() const {
return isTagIdentifierNamespace(getIdentifierNamespace());
}
static bool isTagIdentifierNamespace(unsigned NS) {
// TagDecls have Tag and Type set and may also have TagFriend.
return (NS & ~IDNS_TagFriend) == (IDNS_Tag | IDNS_Type);
}
/// getLexicalDeclContext - The declaration context where this Decl was
/// lexically declared (LexicalDC). May be different from
@ -453,14 +499,14 @@ public:
assert((OldNS & (IDNS_Tag | IDNS_Ordinary |
IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
"namespace includes neither ordinary nor tag");
assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary |
assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary | IDNS_Type |
IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
"namespace includes other than ordinary or tag");
IdentifierNamespace = 0;
if (OldNS & (IDNS_Tag | IDNS_TagFriend)) {
IdentifierNamespace |= IDNS_TagFriend;
if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag;
if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag | IDNS_Type;
}
if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) {

View File

@ -230,7 +230,7 @@ public:
// Tag declarations always go at the end of the list so that an
// iterator which points at the first tag will start a span of
// decls that only contains tags.
if (D->getIdentifierNamespace() == Decl::IDNS_Tag)
if (D->hasTagIdentifierNamespace())
Vec.push_back(reinterpret_cast<uintptr_t>(D));
// Resolved using declarations go at the front of the list so that
@ -251,7 +251,7 @@ public:
// tag declarations. But we can be clever about tag declarations
// because there can only ever be one in a scope.
} else if (reinterpret_cast<NamedDecl *>(Vec.back())
->getIdentifierNamespace() == Decl::IDNS_Tag) {
->hasTagIdentifierNamespace()) {
uintptr_t TagD = Vec.back();
Vec.back() = reinterpret_cast<uintptr_t>(D);
Vec.push_back(TagD);

View File

@ -1583,6 +1583,11 @@ def err_redefinition_different_kind : Error<
"redefinition of %0 as different kind of symbol">;
def err_redefinition_different_typedef : Error<
"typedef redefinition with different types (%0 vs %1)">;
def err_tag_reference_non_tag : Error<
"elaborated type refers to %select{a non-tag type|a typedef|a template}0">;
def err_tag_reference_conflict : Error<
"implicit declaration introduced by elaborated type conflicts with "
"%select{a declaration|a typedef|a template}0 of the same name">;
def err_tag_definition_of_typedef : Error<
"definition of type %0 conflicts with typedef of the same name">;
def err_conflicting_types : Error<"conflicting types for %0">;

View File

@ -1438,7 +1438,7 @@ Decl *ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) {
for (DeclContext::lookup_result Lookup = DC->lookup(Name);
Lookup.first != Lookup.second;
++Lookup.first) {
if (!(*Lookup.first)->isInIdentifierNamespace(Decl::IDNS_Ordinary))
if (!(*Lookup.first)->isInIdentifierNamespace(Decl::IDNS_Namespace))
continue;
if (NamespaceDecl *FoundNS = dyn_cast<NamespaceDecl>(*Lookup.first)) {
@ -1451,7 +1451,7 @@ Decl *ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) {
}
if (!ConflictingDecls.empty()) {
Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Ordinary,
Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Namespace,
ConflictingDecls.data(),
ConflictingDecls.size());
}

View File

@ -231,23 +231,28 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case CXXConstructor:
case CXXDestructor:
case CXXConversion:
case Typedef:
case EnumConstant:
case Var:
case ImplicitParam:
case ParmVar:
case NonTypeTemplateParm:
case ObjCMethod:
case ObjCInterface:
case ObjCProperty:
case ObjCCompatibleAlias:
return IDNS_Ordinary;
case ObjCCompatibleAlias:
case ObjCInterface:
return IDNS_Ordinary | IDNS_Type;
case Typedef:
case UnresolvedUsingTypename:
case TemplateTypeParm:
return IDNS_Ordinary | IDNS_Type;
case UsingShadow:
return 0; // we'll actually overwrite this later
case UnresolvedUsingValue:
case UnresolvedUsingTypename:
return IDNS_Ordinary | IDNS_Using;
case Using:
@ -264,15 +269,18 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case Record:
case CXXRecord:
case Enum:
case TemplateTypeParm:
return IDNS_Tag;
return IDNS_Tag | IDNS_Type;
case Namespace:
case NamespaceAlias:
return IDNS_Namespace;
case FunctionTemplate:
return IDNS_Ordinary;
case ClassTemplate:
case TemplateTemplateParm:
case NamespaceAlias:
return IDNS_Tag | IDNS_Ordinary;
return IDNS_Ordinary | IDNS_Tag | IDNS_Type;
// Never have names.
case Friend:

View File

@ -415,6 +415,9 @@ bool ResultBuilder::isInterestingDecl(NamedDecl *ND,
return false;
}
if (Filter == &ResultBuilder::IsNestedNameSpecifier)
AsNestedNameSpecifier = true;
// ... then it must be interesting!
return true;
@ -504,7 +507,7 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
}
for (; I != IEnd; ++I) {
// A tag declaration does not hide a non-tag declaration.
if (I->first->getIdentifierNamespace() == Decl::IDNS_Tag &&
if (I->first->hasTagIdentifierNamespace() &&
(IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary |
Decl::IDNS_ObjCProtocol)))
continue;
@ -629,7 +632,7 @@ void ResultBuilder::ExitScope() {
bool ResultBuilder::IsOrdinaryName(NamedDecl *ND) const {
unsigned IDNS = Decl::IDNS_Ordinary;
if (SemaRef.getLangOptions().CPlusPlus)
IDNS |= Decl::IDNS_Tag;
IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace;
else if (SemaRef.getLangOptions().ObjC1 && isa<ObjCIvarDecl>(ND))
return true;
@ -641,7 +644,7 @@ bool ResultBuilder::IsOrdinaryName(NamedDecl *ND) const {
bool ResultBuilder::IsOrdinaryNonValueName(NamedDecl *ND) const {
unsigned IDNS = Decl::IDNS_Ordinary;
if (SemaRef.getLangOptions().CPlusPlus)
IDNS |= Decl::IDNS_Tag;
IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace;
return (ND->getIdentifierNamespace() & IDNS) &&
!isa<ValueDecl>(ND) && !isa<FunctionTemplateDecl>(ND);
@ -2094,10 +2097,16 @@ void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) {
return;
}
ResultBuilder Results(*this, Filter);
Results.allowNestedNameSpecifiers();
ResultBuilder Results(*this);
CodeCompletionDeclConsumer Consumer(Results, CurContext);
// First pass: look for tags.
Results.setFilter(Filter);
LookupVisibleDecls(S, LookupTagName, Consumer);
// Second pass: look for nested name specifiers.
Results.setFilter(&ResultBuilder::IsNestedNameSpecifier);
LookupVisibleDecls(S, LookupNestedNameSpecifierName, Consumer);
HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
}

View File

@ -5000,7 +5000,8 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
SearchDC = SearchDC->getEnclosingNamespaceContext();
}
// In C++, look for a shadow friend decl.
// In C++, we need to do a redeclaration lookup to properly
// diagnose some problems.
if (getLangOptions().CPlusPlus) {
Previous.setRedeclarationKind(ForRedeclaration);
LookupQualifiedName(Previous, SearchDC);
@ -5009,6 +5010,30 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
if (!Previous.empty()) {
NamedDecl *PrevDecl = (*Previous.begin())->getUnderlyingDecl();
// It's okay to have a tag decl in the same scope as a typedef
// which hides a tag decl in the same scope. Finding this
// insanity with a redeclaration lookup can only actually happen
// in C++.
//
// This is also okay for elaborated-type-specifiers, which is
// technically forbidden by the current standard but which is
// okay according to the likely resolution of an open issue;
// see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#407
if (getLangOptions().CPlusPlus) {
if (TypedefDecl *TD = dyn_cast<TypedefDecl>(PrevDecl)) {
if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) {
TagDecl *Tag = TT->getDecl();
if (Tag->getDeclName() == Name &&
Tag->getDeclContext()->Equals(TD->getDeclContext())) {
PrevDecl = Tag;
Previous.clear();
Previous.addDecl(Tag);
}
}
}
}
if (TagDecl *PrevTagDecl = dyn_cast<TagDecl>(PrevDecl)) {
// If this is a use of a previous tag, or if the tag is already declared
// in the same scope (so that the definition/declaration completes or
@ -5100,23 +5125,61 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
// If we get here, we're going to create a new Decl. If PrevDecl
// is non-NULL, it's a definition of the tag declared by
// PrevDecl. If it's NULL, we have a new definition.
// Otherwise, PrevDecl is not a tag, but was found with tag
// lookup. This is only actually possible in C++, where a few
// things like templates still live in the tag namespace.
} else {
// PrevDecl is a namespace, template, or anything else
// that lives in the IDNS_Tag identifier namespace.
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.
assert(getLangOptions().CPlusPlus);
// Use a better diagnostic if an elaborated-type-specifier
// found the wrong kind of type on the first
// (non-redeclaration) lookup.
if ((TUK == TUK_Reference || TUK == TUK_Friend) &&
!Previous.isForRedeclaration()) {
unsigned Kind = 0;
if (isa<TypedefDecl>(PrevDecl)) Kind = 1;
else if (isa<ClassTemplateDecl>(PrevDecl)) Kind = 2;
Diag(NameLoc, diag::err_tag_reference_non_tag) << Kind;
Diag(PrevDecl->getLocation(), diag::note_declared_at);
Invalid = true;
// Otherwise, only diagnose if the declaration is in scope.
} else if (!isDeclInScope(PrevDecl, SearchDC, S)) {
// do nothing
// Diagnose implicit declarations introduced by elaborated types.
} else if (TUK == TUK_Reference || TUK == TUK_Friend) {
unsigned Kind = 0;
if (isa<TypedefDecl>(PrevDecl)) Kind = 1;
else if (isa<ClassTemplateDecl>(PrevDecl)) Kind = 2;
Diag(NameLoc, diag::err_tag_reference_conflict) << Kind;
Diag(PrevDecl->getLocation(), diag::note_previous_decl) << PrevDecl;
Invalid = true;
// Otherwise it's a declaration. Call out a particularly common
// case here.
} else if (isa<TypedefDecl>(PrevDecl)) {
Diag(NameLoc, diag::err_tag_definition_of_typedef)
<< Name
<< cast<TypedefDecl>(PrevDecl)->getUnderlyingType();
Diag(PrevDecl->getLocation(), diag::note_previous_decl) << PrevDecl;
Invalid = true;
// Otherwise, diagnose.
} else {
// The tag name clashes with something else in the target scope,
// issue an error and recover by making this tag be anonymous.
Diag(NameLoc, diag::err_redefinition_different_kind) << Name;
Diag(PrevDecl->getLocation(), diag::note_previous_definition);
Name = 0;
Previous.clear();
Invalid = true;
} else {
// The existing declaration isn't relevant to us; we're in a
// new scope, so clear out the previous declaration.
Previous.clear();
}
// The existing declaration isn't relevant to us; we're in a
// new scope, so clear out the previous declaration.
Previous.clear();
}
}
@ -5187,28 +5250,6 @@ CreateNewDecl:
New->addAttr(::new (Context) PragmaPackAttr(Alignment * 8));
}
if (getLangOptions().CPlusPlus && SS.isEmpty() && Name && !Invalid) {
// C++ [dcl.typedef]p3:
// [...] Similarly, in a given scope, a class or enumeration
// shall not be declared with the same name as a typedef-name
// that is declared in that scope and refers to a type other
// than the class or enumeration itself.
LookupResult Lookup(*this, Name, NameLoc, LookupOrdinaryName,
ForRedeclaration);
LookupName(Lookup, S);
TypedefDecl *PrevTypedef = Lookup.getAsSingle<TypedefDecl>();
NamedDecl *PrevTypedefNamed = PrevTypedef;
if (PrevTypedef && isDeclInScope(PrevTypedefNamed, SearchDC, S) &&
Context.getCanonicalType(Context.getTypeDeclType(PrevTypedef)) !=
Context.getCanonicalType(Context.getTypeDeclType(New))) {
Diag(Loc, diag::err_tag_definition_of_typedef)
<< Context.getTypeDeclType(New)
<< PrevTypedef->getUnderlyingType();
Diag(PrevTypedef->getLocation(), diag::note_previous_definition);
Invalid = true;
}
}
// If this is a specialization of a member class (of a class template),
// check the specialization.
if (isExplicitSpecialization && CheckMemberSpecialization(New, Previous))

View File

@ -202,26 +202,6 @@ static bool IsAcceptableOperatorName(NamedDecl *D, unsigned IDNS) {
!D->getDeclContext()->isRecord();
}
static bool IsAcceptableNestedNameSpecifierName(NamedDecl *D, unsigned IDNS) {
// This lookup ignores everything that isn't a type.
// This is a fast check for the far most common case.
if (D->isInIdentifierNamespace(Decl::IDNS_Tag))
return true;
if (isa<UsingShadowDecl>(D))
D = cast<UsingShadowDecl>(D)->getTargetDecl();
return isa<TypeDecl>(D);
}
static bool IsAcceptableNamespaceName(NamedDecl *D, unsigned IDNS) {
// We don't need to look through using decls here because
// using decls aren't allowed to name namespaces.
return isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D);
}
/// Gets the default result filter for the given lookup.
static inline
LookupResult::ResultFilter getResultFilter(Sema::LookupNameKind NameKind) {
@ -232,16 +212,12 @@ LookupResult::ResultFilter getResultFilter(Sema::LookupNameKind NameKind) {
case Sema::LookupRedeclarationWithLinkage: // FIXME: check linkage, scoping
case Sema::LookupUsingDeclName:
case Sema::LookupObjCProtocolName:
case Sema::LookupNestedNameSpecifierName:
case Sema::LookupNamespaceName:
return &IsAcceptableIDNS;
case Sema::LookupOperatorName:
return &IsAcceptableOperatorName;
case Sema::LookupNestedNameSpecifierName:
return &IsAcceptableNestedNameSpecifierName;
case Sema::LookupNamespaceName:
return &IsAcceptableNamespaceName;
}
llvm_unreachable("unkknown lookup kind");
@ -260,15 +236,25 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
case Sema::LookupRedeclarationWithLinkage:
IDNS = Decl::IDNS_Ordinary;
if (CPlusPlus) {
IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member;
IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Namespace;
if (Redeclaration) IDNS |= Decl::IDNS_TagFriend | Decl::IDNS_OrdinaryFriend;
}
break;
case Sema::LookupTagName:
IDNS = Decl::IDNS_Tag;
if (CPlusPlus && Redeclaration)
IDNS |= Decl::IDNS_TagFriend;
if (CPlusPlus) {
IDNS = Decl::IDNS_Type;
// When looking for a redeclaration of a tag name, we add:
// 1) TagFriend to find undeclared friend decls
// 2) Namespace because they can't "overload" with tag decls.
// 3) Tag because it includes class templates, which can't
// "overload" with tag decls.
if (Redeclaration)
IDNS |= Decl::IDNS_Tag | Decl::IDNS_TagFriend | Decl::IDNS_Namespace;
} else {
IDNS = Decl::IDNS_Tag;
}
break;
case Sema::LookupMemberName:
@ -278,8 +264,11 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
break;
case Sema::LookupNestedNameSpecifierName:
IDNS = Decl::IDNS_Type | Decl::IDNS_Namespace;
break;
case Sema::LookupNamespaceName:
IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member;
IDNS = Decl::IDNS_Namespace;
break;
case Sema::LookupUsingDeclName:
@ -2134,7 +2123,7 @@ NamedDecl *VisibleDeclsRecord::checkHidden(NamedDecl *ND) {
IEnd = Pos->second.end();
I != IEnd; ++I) {
// A tag declaration does not hide a non-tag declaration.
if ((*I)->getIdentifierNamespace() == Decl::IDNS_Tag &&
if ((*I)->hasTagIdentifierNamespace() &&
(IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary |
Decl::IDNS_ObjCProtocol)))
continue;

View File

@ -0,0 +1,60 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
namespace test0 {
struct A {
static int foo;
};
namespace i0 {
typedef int A; // expected-note {{declared here}}
int test() {
struct A a; // expected-error {{elaborated type refers to a typedef}}
return a.foo;
}
}
namespace i1 {
template <class> class A; // expected-note {{declared here}}
int test() {
struct A a; // expected-error {{elaborated type refers to a template}}
return a.foo;
}
}
namespace i2 {
int A;
int test() {
struct A a;
return a.foo;
}
}
namespace i3 {
void A();
int test() {
struct A a;
return a.foo;
}
}
namespace i4 {
template <class T> void A();
int test() {
struct A a;
return a.foo;
}
}
// This should magically be okay; see comment in SemaDecl.cpp.
// rdar://problem/7898108
typedef struct A A;
int test() {
struct A a;
return a.foo;
}
}

View File

@ -280,3 +280,10 @@ namespace test8 {
}
template A::I g2<A::I>(A::I i);
}
// PR6885
namespace test9 {
class B {
friend class test9;
};
}

View File

@ -155,7 +155,7 @@ namespace Dependent {
}
namespace test7 {
template <class T> class A { // expected-note {{previous definition is here}}
template <class T> class A { // expected-note {{declared here}}
friend class B;
int x; // expected-note {{declared private here}}
};
@ -174,7 +174,7 @@ namespace test7 {
// This shouldn't crash.
template <class T> class D {
friend class A; // expected-error {{redefinition of 'A' as different kind of symbol}}
friend class A; // expected-error {{elaborated type refers to a template}}
};
template class D<int>;
}

View File

@ -182,3 +182,10 @@ struct B {
};
}
namespace test1 {
struct A {
enum Kind { Foo } Kind;
A() : Kind(Foo) {}
};
}

View File

@ -13,7 +13,7 @@ struct X {
struct Y; // expected-note{{previous definition is here}}
typedef int Y; // expected-error{{typedef redefinition with different types ('int' vs 'Y')}}
typedef int Y2; // expected-note{{previous definition is here}}
typedef int Y2; // expected-note{{declared here}}
struct Y2; // expected-error{{definition of type 'Y2' conflicts with typedef of the same name}}
void f(); // expected-note{{previous definition is here}}