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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62386 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 8e7410c..7cb035b 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -1076,11 +1076,6 @@
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
/// specified typename decl.
QualType ASTContext::getTypedefType(TypedefDecl *Decl) {
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 0bc0043..8ae9311 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -144,13 +144,8 @@
void EnumDecl::completeDefinition(ASTContext &C, QualType NewType) {
assert(!isDefinition() && "Cannot redefine enums!");
- setDefinition(true);
-
IntegerType = NewType;
-
- // Let ASTContext know that this is the defining EnumDecl for this
- // type.
- C.setTagDefinition(this);
+ TagDecl::completeDefinition();
}
FileScopeAsmDecl *FileScopeAsmDecl::Create(ASTContext &C,
@@ -311,6 +306,20 @@
// 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 {
QualType T = C.getTypeDeclType(const_cast<TagDecl*>(this));
TagDecl* D = cast<TagDecl>(cast<TagType>(T)->getDecl());
@@ -351,12 +360,7 @@
/// complete.
void RecordDecl::completeDefinition(ASTContext& C) {
assert(!isDefinition() && "Cannot redefine record!");
-
- setDefinition(true);
-
- // Let ASTContext know that this is the defining RecordDecl for this
- // type.
- C.setTagDefinition(this);
+ TagDecl::completeDefinition();
}
//===----------------------------------------------------------------------===//
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 860a65a..2865687 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -442,37 +442,15 @@
return static_cast<NamespaceDecl*>(this)->getOriginalNamespace();
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::CXXRecord: {
- // The declaration associated with the type is be our primary
- // context.
-#if 0
- // FIXME: This is what we expect to do. However, it doesn't work
- // because ASTContext::setTagDefinition changes the result of
- // Context.getTypeDeclType, meaning that our "primary" declaration
- // 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.
+ case Decl::CXXRecord:
+ // If this is a tag type that has a definition or is currently
+ // being defined, that definition is our primary context.
+ if (TagType *TagT = cast_or_null<TagType>(cast<TagDecl>(this)->TypeForDecl))
+ if (TagT->isBeingDefined() ||
+ (TagT->getDecl() && TagT->getDecl()->isDefinition()))
+ return TagT->getDecl();
return this;
-#endif
- }
case Decl::ObjCMethod:
return this;
diff --git a/lib/AST/TypeSerialization.cpp b/lib/AST/TypeSerialization.cpp
index 42569da..4f3eeca 100644
--- a/lib/AST/TypeSerialization.cpp
+++ b/lib/AST/TypeSerialization.cpp
@@ -258,7 +258,8 @@
Types.push_back(T);
// 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;
}
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index be9da6b..72de710 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -319,7 +319,7 @@
virtual DeclTy *BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
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,
IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr,
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index cfd33f6..7346526 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2813,9 +2813,9 @@
/// 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.
-/// 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.
-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,
IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr,
@@ -2825,7 +2825,7 @@
"Nameless record must be a definition!");
TagDecl::TagKind Kind;
- switch (TagType) {
+ switch (TagSpec) {
default: assert(0 && "Unknown tag type!");
case DeclSpec::TST_struct: Kind = TagDecl::TK_struct; break;
case DeclSpec::TST_union: Kind = TagDecl::TK_union; break;
@@ -2838,6 +2838,8 @@
DeclContext *LexicalContext = CurContext;
ScopedDecl *PrevDecl = 0;
+ bool Invalid = false;
+
if (Name && SS.isNotEmpty()) {
// We have a nested-name tag ('struct foo::bar').
@@ -2898,6 +2900,7 @@
// Recover by making this an anonymous redefinition.
Name = 0;
PrevDecl = 0;
+ Invalid = true;
} else {
// If this is a use, just return the declaration we found.
@@ -2913,15 +2916,29 @@
if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) {
Diag(NameLoc, diag::err_redefinition) << Name;
Diag(Def->getLocation(), diag::note_previous_definition);
- // If this is a redefinition, recover by making this struct be
- // anonymous, which will make any later references get the previous
- // definition.
+ // If this is a redefinition, recover by making this
+ // struct be anonymous, which will make any later
+ // references get the previous definition.
Name = 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
// tag PrevDecl. We're going to create a new Decl for it.
- }
+ }
}
// If we get here we have (another) forward declaration or we
// have a definition. Just create a new decl.
@@ -2944,6 +2961,7 @@
Diag(PrevDecl->getLocation(), diag::note_previous_definition);
Name = 0;
PrevDecl = 0;
+ Invalid = true;
} else {
// The existing declaration isn't relevant to us; we're in a
// new scope, so clear out the previous declaration.
@@ -3034,16 +3052,23 @@
New->addAttr(new PackedAttr(Alignment * 8));
}
+ if (Invalid)
+ New->setInvalidDecl();
+
if (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)
Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New);
// Set the lexical context. If the tag has a C++ scope specifier, the
// lexical context will be different from the semantic context.
New->setLexicalDeclContext(LexicalContext);
+
+ if (TK == TK_Definition)
+ New->startDefinition();
// If this has an identifier, add it to the scope stack.
if (Name) {
@@ -3332,22 +3357,6 @@
assert(EnclosingDecl && "missing record or interface decl");
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.
unsigned NumNamedMembers = 0;
llvm::SmallVector<FieldDecl*, 32> RecFields;
@@ -3564,19 +3573,6 @@
EnumDecl *Enum = cast<EnumDecl>(static_cast<Decl*>(EnumDeclX));
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
// long value. ISO C does not support this, but GCC does as an extension,
// emit a warning.
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index a16c28b..561ed20 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -625,6 +625,7 @@
++Found;
Diag((*Found)->getLocation(), diag::note_ambiguous_member_found);
+
return true;
}