Teach DeclContext how to find the primary declaration for any TagDecl

even when we are still defining the TagDecl. This is required so that
qualified name lookup of a class name within its definition works (see
the new bits in test/SemaCXX/qualified-id-lookup.cpp).

As part of this, move the nested redefinition checking code into
ActOnTag. This gives us diagnostics earlier (when we try to perform
the nested redefinition, rather than when we try to complete the 2nd
definition) and removes some code duplication.

llvm-svn: 62386
This commit is contained in:
Douglas Gregor 2009-01-17 00:42:38 +00:00
parent 1c6549db6f
commit dee1be8e95
14 changed files with 126 additions and 109 deletions

View File

@ -543,14 +543,6 @@ private:
void InitBuiltinTypes(); void InitBuiltinTypes();
void InitBuiltinType(QualType &R, BuiltinType::Kind K); void InitBuiltinType(QualType &R, BuiltinType::Kind K);
/// setTagDefinition - Used by RecordDecl::completeDefinition and
/// EnumDecl::completeDefinition to inform
/// about which RecordDecl/EnumDecl serves as the definition of a particular
/// struct/union/class/enum.
void setTagDefinition(TagDecl* R);
friend class EnumDecl;
friend class RecordDecl;
// Return the ObjC type encoding for a given type. // Return the ObjC type encoding for a given type.
void getObjCEncodingForTypeImpl(QualType t, std::string &S, void getObjCEncodingForTypeImpl(QualType t, std::string &S,
bool ExpandPointedToStructures, bool ExpandPointedToStructures,

View File

@ -887,6 +887,9 @@ class TypeDecl : public ScopedDecl {
/// ASTContext::getTagDeclType, and ASTContext::getTemplateTypeParmType. /// ASTContext::getTagDeclType, and ASTContext::getTemplateTypeParmType.
Type *TypeForDecl; Type *TypeForDecl;
friend class ASTContext; friend class ASTContext;
friend class DeclContext;
friend class TagDecl;
protected: protected:
TypeDecl(Kind DK, DeclContext *DC, SourceLocation L, TypeDecl(Kind DK, DeclContext *DC, SourceLocation L,
IdentifierInfo *Id, ScopedDecl *PrevDecl) IdentifierInfo *Id, ScopedDecl *PrevDecl)
@ -965,6 +968,16 @@ public:
return IsDefinition; return IsDefinition;
} }
/// @brief Starts the definition of this tag declaration.
///
/// This method should be invoked at the beginning of the definition
/// of this tag declaration. It will set the tag type into a state
/// where it is in the process of being defined.
void startDefinition();
/// @brief Completes the definition of this tag declaration.
void completeDefinition();
/// getDefinition - Returns the TagDecl that actually defines this /// getDefinition - Returns the TagDecl that actually defines this
/// struct/union/class/enum. When determining whether or not a /// struct/union/class/enum. When determining whether or not a
/// struct/union/class/enum is completely defined, one should use this method /// struct/union/class/enum is completely defined, one should use this method
@ -998,6 +1011,14 @@ public:
return D->getKind() >= TagFirst && D->getKind() <= TagLast; return D->getKind() >= TagFirst && D->getKind() <= TagLast;
} }
static bool classof(const TagDecl *D) { return true; } static bool classof(const TagDecl *D) { return true; }
static DeclContext *castToDeclContext(const TagDecl *D) {
return static_cast<DeclContext *>(const_cast<TagDecl*>(D));
}
static TagDecl *castFromDeclContext(const DeclContext *DC) {
return static_cast<TagDecl *>(const_cast<DeclContext*>(DC));
}
protected: protected:
void setDefinition(bool V) { IsDefinition = V; } void setDefinition(bool V) { IsDefinition = V; }
}; };

View File

@ -1194,18 +1194,29 @@ protected:
}; };
class TagType : public Type { class TagType : public Type {
TagDecl *decl; /// Stores the TagDecl associated with this type. The decl will
/// point to the TagDecl that actually defines the entity (or is a
/// definition in progress), if there is such a definition. The
/// single-bit value will be non-zero when this tag is in the
/// process of being defined.
llvm::PointerIntPair<TagDecl *, 1> decl;
friend class ASTContext; friend class ASTContext;
friend class TagDecl;
protected: protected:
// FIXME: We'll need the user to pass in information about whether // FIXME: We'll need the user to pass in information about whether
// this type is dependent or not, because we don't have enough // this type is dependent or not, because we don't have enough
// information to compute it here. // information to compute it here.
TagType(TagDecl *D, QualType can) TagType(TagDecl *D, QualType can)
: Type(Tagged, can, /*Dependent=*/false), decl(D) {} : Type(Tagged, can, /*Dependent=*/false), decl(D, 0) {}
public: public:
TagDecl *getDecl() const { return decl; } TagDecl *getDecl() const { return decl.getPointer(); }
/// @brief Determines whether this type is in the process of being
/// defined.
bool isBeingDefined() const { return decl.getInt(); }
void setBeingDefined(bool Def) { decl.setInt(Def? 1 : 0); }
virtual void getAsStringInternal(std::string &InnerString) const; virtual void getAsStringInternal(std::string &InnerString) const;

View File

@ -253,17 +253,20 @@ public:
/// Prints information about this FullSourceLoc to stderr. Useful for /// Prints information about this FullSourceLoc to stderr. Useful for
/// debugging. /// debugging.
void dump() const; void dump() const;
friend inline bool
operator==(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
return LHS.getRawEncoding() == RHS.getRawEncoding() &&
LHS.SrcMgr == RHS.SrcMgr;
}
friend inline bool
operator!=(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
return !(LHS == RHS);
}
}; };
inline bool operator==(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
return LHS.getRawEncoding() == RHS.getRawEncoding() &&
&LHS.getManager() == &RHS.getManager();
}
inline bool operator!=(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
return !(LHS == RHS);
}
} // end namespace clang } // end namespace clang
#endif #endif

View File

@ -310,7 +310,7 @@ public:
TK_Declaration, // Fwd decl of a tag: 'struct foo;' TK_Declaration, // Fwd decl of a tag: 'struct foo;'
TK_Definition // Definition of a tag: 'struct foo { int X; } Y;' TK_Definition // Definition of a tag: 'struct foo { int X; } Y;'
}; };
virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK, virtual DeclTy *ActOnTag(Scope *S, unsigned TagSpec, TagKind TK,
SourceLocation KWLoc, const CXXScopeSpec &SS, SourceLocation KWLoc, const CXXScopeSpec &SS,
IdentifierInfo *Name, SourceLocation NameLoc, IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr, AttributeList *Attr,

View File

@ -1076,11 +1076,6 @@ QualType ASTContext::getTypeDeclType(TypeDecl *Decl, TypeDecl* PrevDecl) {
return QualType(Decl->TypeForDecl, 0); return QualType(Decl->TypeForDecl, 0);
} }
void ASTContext::setTagDefinition(TagDecl* D) {
assert (D->isDefinition());
cast<TagType>(D->TypeForDecl)->decl = D;
}
/// getTypedefType - Return the unique reference to the type for the /// getTypedefType - Return the unique reference to the type for the
/// specified typename decl. /// specified typename decl.
QualType ASTContext::getTypedefType(TypedefDecl *Decl) { QualType ASTContext::getTypedefType(TypedefDecl *Decl) {

View File

@ -144,13 +144,8 @@ void EnumDecl::Destroy(ASTContext& C) {
void EnumDecl::completeDefinition(ASTContext &C, QualType NewType) { void EnumDecl::completeDefinition(ASTContext &C, QualType NewType) {
assert(!isDefinition() && "Cannot redefine enums!"); assert(!isDefinition() && "Cannot redefine enums!");
setDefinition(true);
IntegerType = NewType; IntegerType = NewType;
TagDecl::completeDefinition();
// Let ASTContext know that this is the defining EnumDecl for this
// type.
C.setTagDefinition(this);
} }
FileScopeAsmDecl *FileScopeAsmDecl::Create(ASTContext &C, FileScopeAsmDecl *FileScopeAsmDecl::Create(ASTContext &C,
@ -311,6 +306,20 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
// TagDecl Implementation // TagDecl Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
void TagDecl::startDefinition() {
cast<TagType>(TypeForDecl)->decl.setPointer(this);
cast<TagType>(TypeForDecl)->decl.setInt(1);
}
void TagDecl::completeDefinition() {
assert((!TypeForDecl ||
cast<TagType>(TypeForDecl)->decl.getPointer() == this) &&
"Attempt to redefine a tag definition?");
IsDefinition = true;
cast<TagType>(TypeForDecl)->decl.setPointer(this);
cast<TagType>(TypeForDecl)->decl.setInt(0);
}
TagDecl* TagDecl::getDefinition(ASTContext& C) const { TagDecl* TagDecl::getDefinition(ASTContext& C) const {
QualType T = C.getTypeDeclType(const_cast<TagDecl*>(this)); QualType T = C.getTypeDeclType(const_cast<TagDecl*>(this));
TagDecl* D = cast<TagDecl>(cast<TagType>(T)->getDecl()); TagDecl* D = cast<TagDecl>(cast<TagType>(T)->getDecl());
@ -351,12 +360,7 @@ void RecordDecl::Destroy(ASTContext& C) {
/// complete. /// complete.
void RecordDecl::completeDefinition(ASTContext& C) { void RecordDecl::completeDefinition(ASTContext& C) {
assert(!isDefinition() && "Cannot redefine record!"); assert(!isDefinition() && "Cannot redefine record!");
TagDecl::completeDefinition();
setDefinition(true);
// Let ASTContext know that this is the defining RecordDecl for this
// type.
C.setTagDefinition(this);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -442,37 +442,15 @@ DeclContext *DeclContext::getPrimaryContext() {
return static_cast<NamespaceDecl*>(this)->getOriginalNamespace(); return static_cast<NamespaceDecl*>(this)->getOriginalNamespace();
case Decl::Enum: case Decl::Enum:
#if 0
// FIXME: See the comment for CXXRecord, below.
// The declaration associated with the enumeration type is our
// primary context.
return Context.getTypeDeclType(static_cast<EnumDecl*>(this))
->getAsEnumType()->getDecl();
#else
return this;
#endif
case Decl::Record: case Decl::Record:
case Decl::CXXRecord: { case Decl::CXXRecord:
// The declaration associated with the type is be our primary // If this is a tag type that has a definition or is currently
// context. // being defined, that definition is our primary context.
#if 0 if (TagType *TagT = cast_or_null<TagType>(cast<TagDecl>(this)->TypeForDecl))
// FIXME: This is what we expect to do. However, it doesn't work if (TagT->isBeingDefined() ||
// because ASTContext::setTagDefinition changes the result of (TagT->getDecl() && TagT->getDecl()->isDefinition()))
// Context.getTypeDeclType, meaning that our "primary" declaration return TagT->getDecl();
// of a RecordDecl/CXXRecordDecl will change, and we won't be able
// to find any values inserted into the earlier "primary"
// declaration. We need better tracking of redeclarations and
// definitions.
QualType Type = Context.getTypeDeclType(static_cast<RecordDecl*>(this));
return Type->getAsRecordType()->getDecl();
#else
// FIXME: This hack will work for now, because the declaration we
// create when we're defining the record is the one we'll use as
// the definition later.
return this; return this;
#endif
}
case Decl::ObjCMethod: case Decl::ObjCMethod:
return this; return this;

View File

@ -258,7 +258,8 @@ Type* TagType::CreateImpl(ASTContext& Context, Deserializer& D) {
Types.push_back(T); Types.push_back(T);
// Deserialize the decl. // Deserialize the decl.
T->decl = cast<TagDecl>(D.ReadOwnedPtr<Decl>(Context)); T->decl.setPointer(cast<TagDecl>(D.ReadOwnedPtr<Decl>(Context)));
T->decl.setInt(0);
return T; return T;
} }

View File

@ -319,7 +319,7 @@ public:
virtual DeclTy *BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, virtual DeclTy *BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
RecordDecl *Record); RecordDecl *Record);
virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK, virtual DeclTy *ActOnTag(Scope *S, unsigned TagSpec, TagKind TK,
SourceLocation KWLoc, const CXXScopeSpec &SS, SourceLocation KWLoc, const CXXScopeSpec &SS,
IdentifierInfo *Name, SourceLocation NameLoc, IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr, AttributeList *Attr,

View File

@ -2813,9 +2813,9 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T,
/// ActOnTag - This is invoked when we see 'struct foo' or 'struct {'. In the /// ActOnTag - This is invoked when we see 'struct foo' or 'struct {'. In the
/// former case, Name will be non-null. In the later case, Name will be null. /// former case, Name will be non-null. In the later case, Name will be null.
/// TagType indicates what kind of tag this is. TK indicates whether this is a /// TagSpec indicates what kind of tag this is. TK indicates whether this is a
/// reference/declaration/definition of a tag. /// reference/declaration/definition of a tag.
Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK, Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK,
SourceLocation KWLoc, const CXXScopeSpec &SS, SourceLocation KWLoc, const CXXScopeSpec &SS,
IdentifierInfo *Name, SourceLocation NameLoc, IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr, AttributeList *Attr,
@ -2825,7 +2825,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
"Nameless record must be a definition!"); "Nameless record must be a definition!");
TagDecl::TagKind Kind; TagDecl::TagKind Kind;
switch (TagType) { switch (TagSpec) {
default: assert(0 && "Unknown tag type!"); default: assert(0 && "Unknown tag type!");
case DeclSpec::TST_struct: Kind = TagDecl::TK_struct; break; case DeclSpec::TST_struct: Kind = TagDecl::TK_struct; break;
case DeclSpec::TST_union: Kind = TagDecl::TK_union; break; case DeclSpec::TST_union: Kind = TagDecl::TK_union; break;
@ -2838,6 +2838,8 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
DeclContext *LexicalContext = CurContext; DeclContext *LexicalContext = CurContext;
ScopedDecl *PrevDecl = 0; ScopedDecl *PrevDecl = 0;
bool Invalid = false;
if (Name && SS.isNotEmpty()) { if (Name && SS.isNotEmpty()) {
// We have a nested-name tag ('struct foo::bar'). // We have a nested-name tag ('struct foo::bar').
@ -2898,6 +2900,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
// Recover by making this an anonymous redefinition. // Recover by making this an anonymous redefinition.
Name = 0; Name = 0;
PrevDecl = 0; PrevDecl = 0;
Invalid = true;
} else { } else {
// If this is a use, just return the declaration we found. // If this is a use, just return the declaration we found.
@ -2913,12 +2916,26 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) { if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) {
Diag(NameLoc, diag::err_redefinition) << Name; Diag(NameLoc, diag::err_redefinition) << Name;
Diag(Def->getLocation(), diag::note_previous_definition); Diag(Def->getLocation(), diag::note_previous_definition);
// If this is a redefinition, recover by making this struct be // If this is a redefinition, recover by making this
// anonymous, which will make any later references get the previous // struct be anonymous, which will make any later
// definition. // references get the previous definition.
Name = 0; Name = 0;
PrevDecl = 0; PrevDecl = 0;
Invalid = true;
} else {
// If the type is currently being defined, complain
// about a nested redefinition.
TagType *Tag = cast<TagType>(Context.getTagDeclType(PrevTagDecl));
if (Tag->isBeingDefined()) {
Diag(NameLoc, diag::err_nested_redefinition) << Name;
Diag(PrevTagDecl->getLocation(),
diag::note_previous_definition);
Name = 0;
PrevDecl = 0;
Invalid = true;
}
} }
// Okay, this is definition of a previously declared or referenced // Okay, this is definition of a previously declared or referenced
// tag PrevDecl. We're going to create a new Decl for it. // tag PrevDecl. We're going to create a new Decl for it.
} }
@ -2944,6 +2961,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
Diag(PrevDecl->getLocation(), diag::note_previous_definition); Diag(PrevDecl->getLocation(), diag::note_previous_definition);
Name = 0; Name = 0;
PrevDecl = 0; PrevDecl = 0;
Invalid = true;
} else { } else {
// The existing declaration isn't relevant to us; we're in a // The existing declaration isn't relevant to us; we're in a
// new scope, so clear out the previous declaration. // new scope, so clear out the previous declaration.
@ -3034,10 +3052,14 @@ CreateNewDecl:
New->addAttr(new PackedAttr(Alignment * 8)); New->addAttr(new PackedAttr(Alignment * 8));
} }
if (Invalid)
New->setInvalidDecl();
if (Attr) if (Attr)
ProcessDeclAttributeList(New, Attr); ProcessDeclAttributeList(New, Attr);
// If we're declaring or defining // If we're declaring or defining a tag in function prototype scope
// in C, note that this type can only be used within the function.
if (Name && S->isFunctionPrototypeScope() && !getLangOptions().CPlusPlus) if (Name && S->isFunctionPrototypeScope() && !getLangOptions().CPlusPlus)
Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New); Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New);
@ -3045,6 +3067,9 @@ CreateNewDecl:
// lexical context will be different from the semantic context. // lexical context will be different from the semantic context.
New->setLexicalDeclContext(LexicalContext); New->setLexicalDeclContext(LexicalContext);
if (TK == TK_Definition)
New->startDefinition();
// If this has an identifier, add it to the scope stack. // If this has an identifier, add it to the scope stack.
if (Name) { if (Name) {
S = getNonFieldDeclScope(S); S = getNonFieldDeclScope(S);
@ -3332,22 +3357,6 @@ void Sema::ActOnFields(Scope* S,
assert(EnclosingDecl && "missing record or interface decl"); assert(EnclosingDecl && "missing record or interface decl");
RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl); RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl);
if (Record) {
QualType RecordType = Context.getTypeDeclType(Record);
if (RecordType->getAsRecordType()->getDecl()->isDefinition()) {
RecordDecl *Def = RecordType->getAsRecordType()->getDecl();
// Diagnose code like:
// struct S { struct S {} X; };
// We discover this when we complete the outer S. Reject and ignore the
// outer S.
Diag(Def->getLocation(), diag::err_nested_redefinition)
<< Def->getDeclName();
Diag(RecLoc, diag::note_previous_definition);
Record->setInvalidDecl();
return;
}
}
// Verify that all the fields are okay. // Verify that all the fields are okay.
unsigned NumNamedMembers = 0; unsigned NumNamedMembers = 0;
llvm::SmallVector<FieldDecl*, 32> RecFields; llvm::SmallVector<FieldDecl*, 32> RecFields;
@ -3564,19 +3573,6 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDeclX,
EnumDecl *Enum = cast<EnumDecl>(static_cast<Decl*>(EnumDeclX)); EnumDecl *Enum = cast<EnumDecl>(static_cast<Decl*>(EnumDeclX));
QualType EnumType = Context.getTypeDeclType(Enum); QualType EnumType = Context.getTypeDeclType(Enum);
if (EnumType->getAsEnumType()->getDecl()->isDefinition()) {
EnumDecl *Def = EnumType->getAsEnumType()->getDecl();
// Diagnose code like:
// enum e0 {
// E0 = sizeof(enum e0 { E1 })
// };
Diag(Def->getLocation(), diag::err_nested_redefinition)
<< Enum->getDeclName();
Diag(Enum->getLocation(), diag::note_previous_definition);
Enum->setInvalidDecl();
return;
}
// TODO: If the result value doesn't fit in an int, it must be a long or long // TODO: If the result value doesn't fit in an int, it must be a long or long
// long value. ISO C does not support this, but GCC does as an extension, // long value. ISO C does not support this, but GCC does as an extension,
// emit a warning. // emit a warning.

View File

@ -625,6 +625,7 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result, DeclarationName Name,
++Found; ++Found;
Diag((*Found)->getLocation(), diag::note_ambiguous_member_found); Diag((*Found)->getLocation(), diag::note_ambiguous_member_found);
return true; return true;
} }

View File

@ -1,5 +1,4 @@
// RUN: clang %s -fsyntax-only -verify -pedantic // RUN: clang %s -fsyntax-only -verify -pedantic
enum e {A, enum e {A,
B = 42LL << 32, // expected-warning {{ISO C restricts enumerator values to range of 'int'}} B = 42LL << 32, // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
C = -4, D = 12456 }; C = -4, D = 12456 };

View File

@ -44,7 +44,10 @@ namespace N {
} }
} }
void N::f1::foo(int) { } void N::f1::foo(int i) {
f1::member = i;
f1::type &ir = i;
}
namespace N { namespace N {
float& f1(int x) { float& f1(int x) {
@ -93,4 +96,17 @@ void test_a() {
a::a::a::i = 4; a::a::a::i = 4;
} }
struct Undef {
typedef int type;
Undef::type member;
static int size = sizeof(Undef); // expected-error{{invalid application of 'sizeof' to an incomplete type 'struct Undef'}}
int f();
};
int Undef::f() {
return sizeof(Undef);
}