]> granicus.if.org Git - clang/commitdiff
Teach DeclContext how to find the primary declaration for any TagDecl
authorDouglas Gregor <dgregor@apple.com>
Sat, 17 Jan 2009 00:42:38 +0000 (00:42 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 17 Jan 2009 00:42:38 +0000 (00:42 +0000)
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

14 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/Decl.h
include/clang/AST/Type.h
include/clang/Basic/SourceLocation.h
include/clang/Parse/Action.h
lib/AST/ASTContext.cpp
lib/AST/Decl.cpp
lib/AST/DeclBase.cpp
lib/AST/TypeSerialization.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaLookup.cpp
test/Sema/enum.c
test/SemaCXX/qualified-id-lookup.cpp

index ec48971aebfa53548124dcd601a2d2d324c9ead5..7707b1d21582b569b00456d13c6ae94682bf973a 100644 (file)
@@ -543,14 +543,6 @@ private:
   void InitBuiltinTypes();
   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.
   void getObjCEncodingForTypeImpl(QualType t, std::string &S, 
                                   bool ExpandPointedToStructures,
index f8e50925360aeb3316869faec9f03a6ee4d7f919..c0325bdafbb71736bf19dd59233e867eb726af83 100644 (file)
@@ -887,6 +887,9 @@ class TypeDecl : public ScopedDecl {
   /// ASTContext::getTagDeclType, and ASTContext::getTemplateTypeParmType.
   Type *TypeForDecl;
   friend class ASTContext;
+  friend class DeclContext;
+  friend class TagDecl;
+
 protected:
   TypeDecl(Kind DK, DeclContext *DC, SourceLocation L,
            IdentifierInfo *Id, ScopedDecl *PrevDecl)
@@ -965,6 +968,16 @@ public:
     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 
   ///  struct/union/class/enum.  When determining whether or not a
   ///  struct/union/class/enum is completely defined, one should use this method
@@ -998,6 +1011,14 @@ public:
     return D->getKind() >= TagFirst && D->getKind() <= TagLast;
   }
   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:
   void setDefinition(bool V) { IsDefinition = V; }
 };
index afcd1edd80fe209f7622cbba6e19389750a8c9b3..a74ac9532443b7788006ca1b76197dfc6b755c98 100644 (file)
@@ -1194,19 +1194,30 @@ protected:
 };
 
 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 TagDecl;
 
 protected:
   // FIXME: We'll need the user to pass in information about whether
   // this type is dependent or not, because we don't have enough
   // information to compute it here.
   TagType(TagDecl *D, QualType can) 
-    : Type(Tagged, can, /*Dependent=*/false), decl(D) {}
+    : Type(Tagged, can, /*Dependent=*/false), decl(D, 0) {}
 
 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;
   
   static bool classof(const Type *T) { return T->getTypeClass() == Tagged; }
index da7ea25a5463a7cb0a3dcb38d84aba31f51c3b15..ba359bddaec13e6bb26d7125eec7f88c5f84976e 100644 (file)
@@ -253,17 +253,20 @@ public:
   /// Prints information about this FullSourceLoc to stderr. Useful for
   ///  debugging.
   void dump() const;
-};
 
-inline bool operator==(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
-  return LHS.getRawEncoding() == RHS.getRawEncoding() &&
-         &LHS.getManager() == &RHS.getManager();
-}
+  friend inline bool 
+  operator==(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
+    return LHS.getRawEncoding() == RHS.getRawEncoding() &&
+          LHS.SrcMgr == RHS.SrcMgr;
+  }
 
-inline bool operator!=(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
-  return !(LHS == RHS);
-}
-  
+  friend inline bool 
+  operator!=(const FullSourceLoc &LHS, const FullSourceLoc &RHS) {
+    return !(LHS == RHS);
+  }
+
+};
 }  // end namespace clang
 
 #endif
index d0891bb35047570d8fd5a329d57538fea3f4be44..f78f4f38d14c36c003d24c9d974ad0f00b7d9c38 100644 (file)
@@ -310,7 +310,7 @@ public:
     TK_Declaration, // Fwd decl of a tag:   'struct foo;'
     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,
                            IdentifierInfo *Name, SourceLocation NameLoc,
                            AttributeList *Attr,
index 8e7410c47c77930ae92f3a7e7a99683a9a2a3676..7cb035b8680be6e69199350a5f92a9ed2880c03f 100644 (file)
@@ -1076,11 +1076,6 @@ QualType ASTContext::getTypeDeclType(TypeDecl *Decl, TypeDecl* PrevDecl) {
   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) {
index 0bc0043ccb8f0ea7103a2458b71899ceb137e011..8ae93110170de4f0661dd43d2b55d724eb37a403 100644 (file)
@@ -144,13 +144,8 @@ void EnumDecl::Destroy(ASTContext& C) {
 
 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 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
 // 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 @@ void RecordDecl::Destroy(ASTContext& C) {
 /// 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();
 }
 
 //===----------------------------------------------------------------------===//
index 860a65a1e3e117bed41596949c963f9591bbb829..28656873ab0596c858b4b3c4668db315909899bf 100644 (file)
@@ -442,37 +442,15 @@ DeclContext *DeclContext::getPrimaryContext() {
     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;
index 42569da400a766328234b0f1be23dff7455d573f..4f3eeca28c695e1b052a284d15498f1066eab032 100644 (file)
@@ -258,7 +258,8 @@ Type* TagType::CreateImpl(ASTContext& Context, Deserializer& D) {
   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;
 }
index be9da6bd80f257c90d310adb4450ef15134c26eb..72de7107c80277b86275e3858f794b5b667a561e 100644 (file)
@@ -319,7 +319,7 @@ public:
   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,
index cfd33f6123d33795dc657a952e0e4a439d10c889..734652626596a20174dcd02f67be83de688ee463 100644 (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
 /// 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 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
          "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 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
   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 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
           // 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 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
             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 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
         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 @@ CreateNewDecl:
       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 @@ void Sema::ActOnFields(Scope* S,
   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 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDeclX,
   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.
index a16c28b9dcebb4c48b446121e18cd6c171f8f250..561ed205e894d3624fcde312a6931d37b4996132 100644 (file)
@@ -625,6 +625,7 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result, DeclarationName Name,
       ++Found;
 
     Diag((*Found)->getLocation(), diag::note_ambiguous_member_found);
+
     return true;
   } 
 
index 4c24b580d8fed281b0a6c507f8a194464cf56a0f..b42036dc02ed1deb9007c7c0ff7e8ee5b78fca7d 100644 (file)
@@ -1,5 +1,4 @@
 // RUN: clang %s -fsyntax-only -verify -pedantic
-
 enum e {A, 
         B = 42LL << 32,        // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
       C = -4, D = 12456 };
index 34c06140d8938836c216024c2c042c8f6d583f3d..cf86971f3507015b3f4cd28bd22865702a98b4c0 100644 (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 {
   float& f1(int x) {
@@ -93,4 +96,17 @@ void test_a() {
   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);
+}