]> granicus.if.org Git - clang/commitdiff
Unify the code for defining tags in C and C++, so that we always
authorDouglas Gregor <dgregor@apple.com>
Thu, 8 Jan 2009 20:45:30 +0000 (20:45 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 8 Jan 2009 20:45:30 +0000 (20:45 +0000)
introduce a Scope for the body of a tag. This reduces the number of
semantic differences between C and C++ structs and unions, and will
help with other features (e.g., anonymous unions) in C. Some important
points:

  - Fields are now in the "member" namespace (IDNS_Member), to keep
    them separate from tags and ordinary names in C. See the new test
    in Sema/member-reference.c for an example of why this matters. In
    C++, ordinary and member name lookup will find members in both the
    ordinary and member namespace, so the difference between
    IDNS_Member and IDNS_Ordinary is erased by Sema::LookupDecl (but
    only in C++!).
  - We always introduce a Scope and push a DeclContext when we're
    defining a tag, in both C and C++. Previously, we had different
    actions and different Scope/CurContext behavior for enums, C
    structs/unions, and C++ structs/unions/classes. Now, it's one pair
    of actions. (Yay!)

There's still some fuzziness in the handling of struct/union/enum
definitions within other struct/union/enum definitions in C. We'll
need to do some more cleanup to eliminate some reliance on CurContext
before we can solve this issue for real. What we want is for something
like this:

  struct X {
    struct T { int x; } t;
  };

to introduce T into translation unit scope (placing it at the
appropriate point in the IdentifierResolver chain, too), but it should
still have struct X as its lexical declaration
context. PushOnScopeChains isn't smart enough to do that yet, though,
so there's a FIXME test in nested-redef.c

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61940 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
include/clang/AST/Decl.h
include/clang/AST/DeclBase.h
include/clang/Parse/Action.h
lib/AST/Decl.cpp
lib/AST/DeclBase.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/ParseObjc.cpp
lib/Sema/IdentifierResolver.cpp
lib/Sema/Sema.h
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
test/Sema/member-reference.c
test/Sema/nested-redef.c [new file with mode: 0644]
test/SemaCXX/anonymous-union.cpp
test/SemaCXX/class.cpp
test/SemaObjC/duplicate-ivar-check.m
test/SemaObjC/interface-scope.m [new file with mode: 0644]
test/SemaObjC/ivar-sem-check-1.m

index 460e3de2282b70e23a7b45ea18bd755baf848ce0..5623fe981ac7d2af63d8e0be077db8dc3001d0d4 100644 (file)
@@ -900,7 +900,7 @@ protected:
 
 
 /// TagDecl - Represents the declaration of a struct/union/class/enum.
-class TagDecl : public TypeDecl {
+class TagDecl : public TypeDecl, public DeclContext {
 public:
   enum TagKind {
     TK_struct,
@@ -919,7 +919,7 @@ private:
 protected:
   TagDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L,
           IdentifierInfo *Id, ScopedDecl *PrevDecl)
-    : TypeDecl(DK, DC, L, Id, PrevDecl) {
+    : TypeDecl(DK, DC, L, Id, PrevDecl), DeclContext(DK) {
     assert((DK != Enum || TK == TK_enum) &&"EnumDecl not matched with TK_enum");
     TagDeclKind = TK;
     IsDefinition = false;
@@ -970,7 +970,7 @@ protected:
 
 /// EnumDecl - Represents an enum.  As an extension, we allow forward-declared
 /// enums.
-class EnumDecl : public TagDecl, public DeclContext {
+class EnumDecl : public TagDecl {
   /// IntegerType - This represent the integer type that the enum corresponds
   /// to for code generation purposes.  Note that the enumerator constants may
   /// have a different type than this does.
@@ -978,7 +978,7 @@ class EnumDecl : public TagDecl, public DeclContext {
   
   EnumDecl(DeclContext *DC, SourceLocation L,
            IdentifierInfo *Id, ScopedDecl *PrevDecl)
-    : TagDecl(Enum, TK_enum, DC, L, Id, PrevDecl), DeclContext(Enum) {
+    : TagDecl(Enum, TK_enum, DC, L, Id, PrevDecl) {
       IntegerType = QualType();
     }
 public:
@@ -1053,7 +1053,7 @@ protected:
 ///   union Y { int A, B; };     // Has body with members A and B (FieldDecls).
 /// This decl will be marked invalid if *any* members are invalid.
 ///
-class RecordDecl : public TagDecl, public DeclContext {
+class RecordDecl : public TagDecl {
   /// HasFlexibleArrayMember - This is true if this struct ends with a flexible
   /// array member (e.g. int X[]) or if this union contains a struct that does.
   /// If so, this cannot be contained in arrays or other structs as a member.
index 6780a33c5b64c94e95b91980ea6fd97b4b84c471..689ae69c63b39603cf9b4b97c610e9e947694ba8 100644 (file)
@@ -68,9 +68,9 @@ public:
              Namespace,  // [DeclContext]
     //       TypeDecl
                Typedef,
-    //         TagDecl
-                 Enum,  // [DeclContext]
-                 Record, // [DeclContext]
+    //         TagDecl // [DeclContext]
+                 Enum,  
+                 Record,
                    CXXRecord,  
               TemplateTypeParm,
     //       ValueDecl
@@ -206,9 +206,6 @@ public:
     case OriginalParmVar:
     case EnumConstant:
     case NonTypeTemplateParm:
-    case Field:
-    case ObjCAtDefsField:
-    case ObjCIvar:
     case ObjCInterface:
     case ObjCCompatibleAlias:
     case OverloadedFunction:
@@ -216,6 +213,12 @@ public:
     case CXXConversion:
     case CXXClassVar:
       return IDNS_Ordinary;
+
+    case Field:
+    case ObjCAtDefsField:
+    case ObjCIvar:
+      return IDNS_Member;
+
     case Record:
     case CXXRecord:
     case TemplateTypeParm:
index 5bba2a7b6df2ee31f43b14bb5bad9501cc1c9ade..4c524f48d2df66cfc83cbdbaac40e9d472417abc 100644 (file)
@@ -341,10 +341,14 @@ public:
                            SourceLocation LBrac, SourceLocation RBrac,
                            AttributeList *AttrList) {}
   
-  /// ActOnEnumStartDefinition - Invoked when we have entered the
-  /// scope of the enumeration body and will be parsing its
-  /// enumerators.  
-  virtual void ActOnEnumStartDefinition(Scope *S, DeclTy *EnumDecl) { }
+  /// ActOnTagStartDefinition - Invoked when we have entered the
+  /// scope of a tag's definition (e.g., for an enumeration, class,
+  /// struct, or union).
+  virtual void ActOnTagStartDefinition(Scope *S, DeclTy *TagDecl) { }
+
+  /// ActOnTagFinishDefinition - Invoked once we have finished parsing
+  /// the definition of a tag (enumeration, class, struct, or union).
+  virtual void ActOnTagFinishDefinition(Scope *S, DeclTy *TagDecl) { }
 
   virtual DeclTy *ActOnEnumConstant(Scope *S, DeclTy *EnumDecl,
                                     DeclTy *LastEnumConstant,
@@ -915,12 +919,6 @@ public:
                                    unsigned NumBases) {
   }
                                    
-  /// ActOnStartCXXClassDef - This is called at the start of a class/struct/union
-  /// definition, when on C++.
-  virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
-                                     SourceLocation LBrace) {
-  }
-  
   /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
   /// declarator is parsed. 'AS' is the access specifier, 'BitfieldWidth'
   /// specifies the bitfield width if there is one and 'Init' specifies the
@@ -962,11 +960,6 @@ public:
                                                  SourceLocation RBrac) {
   }
 
-  /// ActOnFinishCXXClassDef - This is called when a class/struct/union has
-  /// completed parsing, when on C++.
-  virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl) {
-  }
-
   //===---------------------------C++ Templates----------------------------===//
 
   /// ActOnTypeParameter - Called when a C++ template type parameter
index b3f19a25b86043b2c18f874a89abb62272b1cb40..8521b9b44ead6b33b17d50ead9c70d200bd940bb 100644 (file)
@@ -314,8 +314,7 @@ TagDecl* TagDecl::getDefinition(ASTContext& C) const {
 
 RecordDecl::RecordDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L,
                        IdentifierInfo *Id)
-  : TagDecl(DK, TK, DC, L, Id, 0), DeclContext(DK) {
-  
+  : TagDecl(DK, TK, DC, L, Id, 0) {
   HasFlexibleArrayMember = false;
   AnonymousStructOrUnion = false;
   assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
index 49890144a956c1e9991d6c5b0ec5965c60ba9a81..83f8fa411f93be2b78711686c6f42dd1551d57a6 100644 (file)
@@ -579,6 +579,7 @@ DeclContext::lookup(DeclarationName Name) const {
 
 const DeclContext *DeclContext::getLookupContext() const {
   const DeclContext *Ctx = this;
+  // Skip through transparent contexts.
   while (Ctx->isTransparentContext())
     Ctx = Ctx->getParent();
   return Ctx;
index c4126f3bb46d4403317a9787e78551d451281e87..219295fa74d8ed8c156f1a46663f2d5219eb5e9e 100644 (file)
@@ -948,6 +948,9 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
                                   unsigned TagType, DeclTy *TagDecl) {
   SourceLocation LBraceLoc = ConsumeBrace();
   
+  ParseScope StructScope(this, Scope::DeclScope);
+  Actions.ActOnTagStartDefinition(CurScope, TagDecl);
+
   // Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
   // C++.
   if (Tok.is(tok::r_brace) && !getLang().CPlusPlus)
@@ -1027,7 +1030,9 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
   Actions.ActOnFields(CurScope,
                       RecordLoc,TagDecl,&FieldDecls[0],FieldDecls.size(),
                       LBraceLoc, RBraceLoc,
-                      AttrList);  
+                      AttrList);
+  StructScope.Exit();
+  Actions.ActOnTagFinishDefinition(CurScope, TagDecl);
 }
 
 
@@ -1125,7 +1130,7 @@ void Parser::ParseEnumSpecifier(DeclSpec &DS) {
 void Parser::ParseEnumBody(SourceLocation StartLoc, DeclTy *EnumDecl) {
   // Enter the scope of the enum body and start the definition.
   ParseScope EnumScope(this, Scope::DeclScope);
-  Actions.ActOnEnumStartDefinition(CurScope, EnumDecl);
+  Actions.ActOnTagStartDefinition(CurScope, EnumDecl);
 
   SourceLocation LBraceLoc = ConsumeBrace();
   
@@ -1178,6 +1183,9 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, DeclTy *EnumDecl) {
   // If attributes exist after the identifier list, parse them.
   if (Tok.is(tok::kw___attribute))
     AttrList = ParseAttributes(); // FIXME: where do they do?
+
+  EnumScope.Exit();
+  Actions.ActOnTagFinishDefinition(CurScope, EnumDecl);
 }
 
 /// isTypeSpecifierQualifier - Return true if the current token could be the
index 81ea52ca690ef1d04069e263b45b564ebf2de344..c6c2ae4cfe18bf011d93070edd97b21d91ee2d6e 100644 (file)
@@ -730,7 +730,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
   // Enter a scope for the class.
   ParseScope ClassScope(this, Scope::CXXClassScope|Scope::DeclScope);
 
-  Actions.ActOnStartCXXClassDef(CurScope, TagDecl, LBraceLoc);
+  Actions.ActOnTagStartDefinition(CurScope, TagDecl);
 
   // C++ 11p3: Members of a class defined with the keyword class are private
   // by default. Members of a class defined with the keywords struct or union
@@ -802,7 +802,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
   // Leave the class scope.
   ClassScope.Exit();
 
-  Actions.ActOnFinishCXXClassDef(TagDecl);
+  Actions.ActOnTagFinishDefinition(CurScope, TagDecl);
 }
 
 /// ParseConstructorInitializer - Parse a C++ constructor initializer,
index 759de7a0648dd28253bc5b73b9746ff98cba216c..cc13a5e0ae18dc609c18f94d57048d1b6c047fea 100644 (file)
@@ -840,6 +840,8 @@ void Parser::ParseObjCClassInstanceVariables(DeclTy *interfaceDecl,
   llvm::SmallVector<DeclTy*, 32> AllIvarDecls;
   llvm::SmallVector<FieldDeclarator, 8> FieldDeclarators;
 
+  ParseScope ClassScope(this, Scope::DeclScope);
+
   SourceLocation LBraceLoc = ConsumeBrace(); // the "{"
   
   tok::ObjCKeywordKind visibility = tok::objc_protected;
index 90906e72746e1d3ac088ab99ef67298f8638f604..762885e42c388ecd41f8b6236bbd6cd4f3c18087 100644 (file)
@@ -60,6 +60,9 @@ DeclContext *IdentifierResolver::LookupContext::getContext(Decl *D) {
   else
     return TUCtx();
 
+  if (!Ctx) // FIXME: HACK! We shouldn't end up with a NULL context here.
+    return TUCtx();
+
   Ctx = Ctx->getLookupContext();
 
   if (isa<TranslationUnitDecl>(Ctx))
index 765dfe5420ca3932d98768f4ec4fb5b748b8f046..61be6445d1294cd718431491c3323d158d6478bf 100644 (file)
@@ -327,7 +327,16 @@ public:
                            DeclTy **Fields, unsigned NumFields,
                            SourceLocation LBrac, SourceLocation RBrac,
                            AttributeList *AttrList);
-  virtual void ActOnEnumStartDefinition(Scope *S, DeclTy *EnumDecl);
+
+  /// ActOnTagStartDefinition - Invoked when we have entered the
+  /// scope of a tag's definition (e.g., for an enumeration, class,
+  /// struct, or union).
+  virtual void ActOnTagStartDefinition(Scope *S, DeclTy *TagDecl);
+
+  /// ActOnTagFinishDefinition - Invoked once we have finished parsing
+  /// the definition of a tag (enumeration, class, struct, or union).
+  virtual void ActOnTagFinishDefinition(Scope *S, DeclTy *TagDecl);
+
   virtual DeclTy *ActOnEnumConstant(Scope *S, DeclTy *EnumDecl,
                                     DeclTy *LastEnumConstant,
                                     SourceLocation IdLoc, IdentifierInfo *Id,
@@ -1017,9 +1026,6 @@ public:
   virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
                                   const CXXScopeSpec *SS);
   
-  virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
-                                     SourceLocation LBrace);
-
   virtual DeclTy *ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS,
                                            Declarator &D, ExprTy *BitfieldWidth,
                                            ExprTy *Init, DeclTy *LastInGroup);
@@ -1040,8 +1046,6 @@ public:
                                                  SourceLocation LBrac,
                                                  SourceLocation RBrac);
 
-  virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl);
-  
   virtual void ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method);
   virtual void ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *Param);
   virtual void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method);
index 5fb2740fcf31a6b12e059c87f7b9ac26ea60d5c4..dcb2a5fba5513a79bc092fb8a93975f795f97050 100644 (file)
@@ -28,7 +28,7 @@ namespace {
       DeclContext::lookup_const_iterator I, E;\r
       for (llvm::tie(I, E) = LookupCtx->lookup(Name); I != E; ++I) {\r
        IdIsUndeclared = false;\r
-       if (((*I)->getIdentifierNamespace() & Decl::IDNS_Tag) || \r
+       if (((*I)->isInIdentifierNamespace(Decl::IDNS_Tag)) || \r
            isa<TypedefDecl>(*I))\r
          return *I;\r
       }\r
index 6585733ab38bd8cec186121603c803bb4f1c8576..c6e4336a3dc8f246d11001866817dca01cbe9fab 100644 (file)
@@ -263,18 +263,37 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S,
                        bool NamespaceNameOnly) {
   if (!Name) return 0;
   unsigned NS = NSI;
-  if (getLangOptions().CPlusPlus && (NS & Decl::IDNS_Ordinary))
-    NS |= Decl::IDNS_Tag;
-
-  if (LookupCtx == 0 && 
-      (!getLangOptions().CPlusPlus || (NS == Decl::IDNS_Label))) {
-    // Unqualified name lookup in C/Objective-C and name lookup for
-    // labels in C++ is purely lexical, so search in the
-    // declarations attached to the name.
+
+  // In C++, ordinary and member lookup will always find all
+  // kinds of names.
+  if (getLangOptions().CPlusPlus && 
+      (NS & (Decl::IDNS_Ordinary | Decl::IDNS_Member)))
+    NS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Ordinary;
+
+  if (LookupCtx == 0 && !getLangOptions().CPlusPlus) {
+    // Unqualified name lookup in C/Objective-C is purely lexical, so
+    // search in the declarations attached to the name.
     assert(!LookupCtx && "Can't perform qualified name lookup here");
     assert(!NamespaceNameOnly && "Can't perform namespace name lookup here");
+
+    // For the purposes of unqualified name lookup, structs and unions
+    // don't have scopes at all. For example:
+    //
+    //   struct X {
+    //     struct T { int i; } x;
+    //   };
+    //
+    //   void f() {
+    //     struct T t; // okay: T is defined lexically within X, but
+    //                 // semantically at global scope
+    //   };
+    //
+    // FIXME: Is there a better way to deal with this?
+    DeclContext *SearchCtx = CurContext;
+    while (isa<RecordDecl>(SearchCtx) || isa<EnumDecl>(SearchCtx))
+      SearchCtx = SearchCtx->getParent();
     IdentifierResolver::iterator I
-      = IdResolver.begin(Name, CurContext, LookInParent);
+      = IdResolver.begin(Name, SearchCtx, LookInParent);
     
     // Scan up the scope chain looking for a decl that matches this
     // identifier that is in the appropriate namespace.  This search
@@ -284,6 +303,11 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S,
       if ((*I)->isInIdentifierNamespace(NS))
         return *I;
   } else if (LookupCtx) {
+    // If we're performing qualified name lookup (e.g., lookup into a
+    // struct), find fields as part of ordinary name lookup.
+    if (NS & Decl::IDNS_Ordinary)
+      NS |= Decl::IDNS_Member;
+
     // Perform qualified name lookup into the LookupCtx.
     // FIXME: Will need to look into base classes and such.
     DeclContext::lookup_const_iterator I, E;
@@ -2852,6 +2876,15 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
     // declaration or definition.
     // Use ScopedDecl instead of TagDecl, because a NamespaceDecl may come up.
     PrevDecl = dyn_cast_or_null<ScopedDecl>(LookupDecl(Name, Decl::IDNS_Tag,S));
+
+    if (!getLangOptions().CPlusPlus && TK != TK_Reference) {
+      // FIXME: This makes sure that we ignore the contexts associated
+      // with C structs, unions, and enums when looking for a matching
+      // tag declaration or definition. See the similar lookup tweak
+      // in Sema::LookupDecl; is there a better way to deal with this?
+      while (isa<RecordDecl>(DC) || isa<EnumDecl>(DC))
+        DC = DC->getParent();
+    }
   }
 
   if (PrevDecl && PrevDecl->isTemplateParameter()) {
@@ -3007,6 +3040,43 @@ CreateNewDecl:
   return New;
 }
 
+void Sema::ActOnTagStartDefinition(Scope *S, DeclTy *TagD) {
+  TagDecl *Tag = cast<TagDecl>((Decl *)TagD);
+
+  // Enter the tag context.
+  PushDeclContext(S, Tag);
+
+  if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Tag)) {
+    FieldCollector->StartClass();
+
+    if (Record->getIdentifier()) {
+      // C++ [class]p2: 
+      //   [...] The class-name is also inserted into the scope of the
+      //   class itself; this is known as the injected-class-name. For
+      //   purposes of access checking, the injected-class-name is treated
+      //   as if it were a public member name.
+      RecordDecl *InjectedClassName
+        = CXXRecordDecl::Create(Context, Record->getTagKind(),
+                                CurContext, Record->getLocation(),
+                                Record->getIdentifier(), Record);
+      InjectedClassName->setImplicit();
+      PushOnScopeChains(InjectedClassName, S);
+    }
+  }
+}
+
+void Sema::ActOnTagFinishDefinition(Scope *S, DeclTy *TagD) {
+  TagDecl *Tag = cast<TagDecl>((Decl *)TagD);
+
+  if (isa<CXXRecordDecl>(Tag))
+    FieldCollector->FinishClass();
+
+  // Exit this scope of this tag's definition.
+  PopDeclContext();
+
+  // Notify the consumer that we've defined a tag.
+  Consumer.HandleTagDeclDefinition(Tag);
+}
 
 /// TryToFixInvalidVariablyModifiedType - Helper method to turn variable array
 /// types into constant array types in certain situations which would otherwise
@@ -3108,6 +3178,18 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD,
                               DeclSpec::SCS_mutable,
                             /*PrevDecl=*/0);
 
+  if (II) {
+    Decl *PrevDecl 
+      = LookupDecl(II, Decl::IDNS_Member, S, 0, false, false, false);
+    if (PrevDecl && isDeclInScope(PrevDecl, CurContext, S)
+        && !isa<TagDecl>(PrevDecl)) {
+      Diag(Loc, diag::err_duplicate_member) << II;
+      Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+      NewFD->setInvalidDecl();
+      Record->setInvalidDecl();
+    }
+  }
+
   if (getLangOptions().CPlusPlus) {
     CheckExtraCXXDefaultArguments(D);
     if (!T->isPODType())
@@ -3119,9 +3201,9 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD,
   if (D.getInvalidType() || InvalidDecl)
     NewFD->setInvalidDecl();
 
-  if (II && getLangOptions().CPlusPlus)
+  if (II) {
     PushOnScopeChains(NewFD, S);
-  else
+  else
     Record->addDecl(Context, NewFD);
 
   return NewFD;
@@ -3146,6 +3228,7 @@ Sema::DeclTy *Sema::ActOnIvar(Scope *S,
                               SourceLocation DeclStart, 
                               Declarator &D, ExprTy *BitfieldWidth,
                               tok::ObjCKeywordKind Visibility) {
+  
   IdentifierInfo *II = D.getIdentifier();
   Expr *BitWidth = (Expr*)BitfieldWidth;
   SourceLocation Loc = DeclStart;
@@ -3188,12 +3271,30 @@ Sema::DeclTy *Sema::ActOnIvar(Scope *S,
   ObjCIvarDecl *NewID = ObjCIvarDecl::Create(Context, Loc, II, T, ac,                                             
                                              (Expr *)BitfieldWidth);
   
+  if (II) {
+    Decl *PrevDecl 
+      = LookupDecl(II, Decl::IDNS_Member, S, 0, false, false, false);
+    if (PrevDecl && isDeclInScope(PrevDecl, CurContext, S)
+        && !isa<TagDecl>(PrevDecl)) {
+      Diag(Loc, diag::err_duplicate_member) << II;
+      Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+      NewID->setInvalidDecl();
+    }
+  }
+
   // Process attributes attached to the ivar.
   ProcessDeclAttributes(NewID, D);
   
   if (D.getInvalidType() || InvalidDecl)
     NewID->setInvalidDecl();
 
+  if (II) {
+    // FIXME: When interfaces are DeclContexts, we'll need to add
+    // these to the interface.
+    S->AddDecl(NewID);
+    IdResolver.AddDecl(NewID);
+  }
+
   return NewID;
 }
 
@@ -3206,27 +3307,26 @@ void Sema::ActOnFields(Scope* S,
   assert(EnclosingDecl && "missing record or interface decl");
   RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl);
   
-  if (Record)
-    if (RecordDecl* DefRecord = Record->getDefinition(Context)) {
+  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(DefRecord->getLocation(), diag::err_nested_redefinition)
-        << DefRecord->getDeclName();
+      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;
 
-  // FIXME: Eventually, we'd like to eliminate this in favor of
-  // checking for redeclarations on-the-fly.
-  llvm::DenseMap<const IdentifierInfo*, FieldDecl *> FieldIDs;
-  
   for (unsigned i = 0; i != NumFields; ++i) {
     FieldDecl *FD = cast_or_null<FieldDecl>(static_cast<Decl*>(Fields[i]));
     assert(FD && "missing field decl");
@@ -3234,37 +3334,7 @@ void Sema::ActOnFields(Scope* S,
     // Get the type for the field.
     Type *FDTy = FD->getType().getTypePtr();
 
-    if (FD->isAnonymousStructOrUnion()) {
-      // We have found a field that represents an anonymous struct
-      // or union. Introduce all of the inner fields (recursively)
-      // into the list of fields we know about, so that we can produce
-      // an appropriate error message in cases like:
-      //
-      //   struct X {
-      //     union {
-      //       int x;
-      //       float f;
-      //     };
-      //     double x;
-      //   };
-      llvm::SmallVector<FieldDecl *, 4> AnonStructUnionFields;
-      AnonStructUnionFields.push_back(FD);
-      while (!AnonStructUnionFields.empty()) {
-        FieldDecl *AnonField = AnonStructUnionFields.back();
-        AnonStructUnionFields.pop_back();
-        
-        RecordDecl *AnonRecord 
-          = AnonField->getType()->getAsRecordType()->getDecl();
-        for (RecordDecl::field_iterator F = AnonRecord->field_begin(),
-                                     FEnd = AnonRecord->field_end();
-             F != FEnd; ++F) {
-          if ((*F)->isAnonymousStructOrUnion())
-            AnonStructUnionFields.push_back(*F);
-          else if (const IdentifierInfo *II = (*F)->getIdentifier())
-            FieldIDs[II] = *F;
-        }
-      }
-    } else {
+    if (!FD->isAnonymousStructOrUnion()) {
       // Remember all fields written by the user.
       RecFields.push_back(FD);
     }
@@ -3340,28 +3410,13 @@ void Sema::ActOnFields(Scope* S,
       continue;
     }
     // Keep track of the number of named members.
-    if (IdentifierInfo *II = FD->getIdentifier()) {
-      // Detect duplicate member names.
-      if (FieldIDs[II]) {
-        Diag(FD->getLocation(), diag::err_duplicate_member) << II;
-        // Find the previous decl.
-        Diag(FieldIDs[II]->getLocation(), diag::note_previous_definition);
-        FD->setInvalidDecl();
-        EnclosingDecl->setInvalidDecl();
-        continue;
-      }
+    if (FD->getIdentifier())
       ++NumNamedMembers;
-      FieldIDs[II] = FD;
-    }
   }
 
   // Okay, we successfully defined 'Record'.
   if (Record) {
     Record->completeDefinition(Context);
-    // If this is a C++ record, HandleTagDeclDefinition will be invoked in
-    // Sema::ActOnFinishCXXClassDef.
-    if (!isa<CXXRecordDecl>(Record))
-      Consumer.HandleTagDeclDefinition(Record);
   } else {
     ObjCIvarDecl **ClsFields = reinterpret_cast<ObjCIvarDecl**>(&RecFields[0]);
     if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(EnclosingDecl)) {
@@ -3376,7 +3431,7 @@ void Sema::ActOnFields(Scope* S,
           ObjCIvarDecl* prevIvar = ID->getSuperClass()->FindIvarDeclaration(II);
           if (prevIvar) {
             Diag(Ivar->getLocation(), diag::err_duplicate_member) << II;
-            Diag(prevIvar->getLocation(), diag::note_previous_definition);
+            Diag(prevIvar->getLocation(), diag::note_previous_declaration);
           }
         }
       }
@@ -3393,13 +3448,6 @@ void Sema::ActOnFields(Scope* S,
     ProcessDeclAttributeList(Record, Attr);
 }
 
-void Sema::ActOnEnumStartDefinition(Scope *S, DeclTy *EnumD) {
-  EnumDecl *Enum = cast<EnumDecl>((Decl *)EnumD);
-
-  // Enter the enumeration context.
-  PushDeclContext(S, Enum);
-}
-
 Sema::DeclTy *Sema::ActOnEnumConstant(Scope *S, DeclTy *theEnumDecl,
                                       DeclTy *lastEnumConst,
                                       SourceLocation IdLoc, IdentifierInfo *Id,
@@ -3507,7 +3555,6 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDeclX,
       << Enum->getDeclName();
     Diag(Enum->getLocation(), diag::note_previous_definition);
     Enum->setInvalidDecl();
-    PopDeclContext();
     return;
   }
 
@@ -3675,10 +3722,6 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDeclX,
   }
   
   Enum->completeDefinition(Context, BestType);
-  Consumer.HandleTagDeclDefinition(Enum);
-
-  // Leave the context of the enumeration.
-  PopDeclContext();
 }
 
 Sema::DeclTy *Sema::ActOnFileScopeAsmDecl(SourceLocation Loc,
index edc3a28dcf5dc5768585a1a9b4fd851d67210cd8..dea3688bdbfb720b4ef18c20ff684b67c61ace8b 100644 (file)
@@ -414,28 +414,6 @@ void Sema::ActOnBaseSpecifiers(DeclTy *ClassDecl, BaseTy **Bases,
 // C++ class member Handling
 //===----------------------------------------------------------------------===//
 
-/// ActOnStartCXXClassDef - This is called at the start of a class/struct/union
-/// definition, when on C++.
-void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) {
-  CXXRecordDecl *Dcl = cast<CXXRecordDecl>(static_cast<Decl *>(D));
-  PushDeclContext(S, Dcl);
-  FieldCollector->StartClass();
-
-  if (Dcl->getIdentifier()) {
-    // C++ [class]p2: 
-    //   [...] The class-name is also inserted into the scope of the
-    //   class itself; this is known as the injected-class-name. For
-    //   purposes of access checking, the injected-class-name is treated
-    //   as if it were a public member name.
-    RecordDecl *InjectedClassName
-      = CXXRecordDecl::Create(Context, Dcl->getTagKind(),
-                              CurContext, Dcl->getLocation(),
-                              Dcl->getIdentifier(), Dcl);
-    InjectedClassName->setImplicit();
-    PushOnScopeChains(InjectedClassName, S);
-  }
-}
-
 /// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
 /// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the
 /// bitfield width if there is one and 'InitExpr' specifies the initializer if
@@ -975,16 +953,6 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
   }
 }
 
-void Sema::ActOnFinishCXXClassDef(DeclTy *D) {
-  CXXRecordDecl *Rec = cast<CXXRecordDecl>(static_cast<Decl *>(D));
-  FieldCollector->FinishClass();
-  PopDeclContext();
-
-  // Everything, including inline method definitions, have been parsed.
-  // Let the consumer know of the new TagDecl definition.
-  Consumer.HandleTagDeclDefinition(Rec);
-}
-
 /// ActOnStartDelayedCXXMethodDeclaration - We have completed
 /// parsing a top-level (non-nested) C++ class, and we are now
 /// parsing those parts of the given Method declaration that could
index 784d60020ad3aa29d81e19b884375d6db28a32a6..b434f5b77938b4650ca2653e027dd3b108429575 100644 (file)
@@ -7,3 +7,14 @@ void f(void) {
    s->i = 1;
 }
 
+typedef int x;
+struct S {
+  int x;
+  x z;
+};
+
+void g(void) {
+  struct S s[1];
+  s->x = 1;
+  s->z = 2;
+}
diff --git a/test/Sema/nested-redef.c b/test/Sema/nested-redef.c
new file mode 100644 (file)
index 0000000..83cd420
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: clang -fsyntax-only -verify %s
+struct X { // expected-note{{previous definition is here}}
+  struct X { } x; // expected-error{{nested redefinition of 'X'}}
+}; 
+
+struct Y { };
+void f(void) {
+  struct Y { }; // okay: this is a different Y
+}
+
+struct T;
+struct Z {
+  struct T { int x; } t;
+  struct U { int x; } u;
+};
+
+void f2(void) {
+  struct T t;
+  // FIXME: this is well-formed, but Clang breaks on it struct U u;
+}
+
+
index a66745b378481b599d7fdc70ed88cd36d629fee7..872c45c468979cd19dcdfe7238d84742b1ec5226 100644 (file)
@@ -66,7 +66,7 @@ struct Redecl {
   union {
     int x; // expected-error{{member of anonymous union redeclares 'x'}}
     float y;
-    double z; // expected-note{{previous definition is here}}
+    double z; // expected-note{{previous declaration is here}}
     double zz; // expected-note{{previous definition is here}}
   };
 
index 7e06415a09da47df8c8f247730f89615844d8678..d739af87dd79b42d7f8688c2b9ff0f58f18c5152 100644 (file)
@@ -53,7 +53,7 @@ public:
 
   typedef int A;
 
-  virtual int vi; // expected-error {{error: 'virtual' can only appear on non-static member functions}}
+  virtual int viv; // expected-error {{error: 'virtual' can only appear on non-static member functions}}
   virtual static int vsif(); // expected-error {{error: 'virtual' can only appear on non-static member functions}}
   virtual int vif();
 
@@ -105,3 +105,8 @@ void ogfn()
   struct C4;
   C4; // expected-error {{declaration does not declare anything}}
 }
+
+struct C4 {
+  void f(); // expected-note{{previous declaration is here}}
+  int f; // expected-error{{duplicate member 'f'}}
+};
index ea42e9e277b7573dd161c8820c8e5ad71388e9be..6dcdc38dc300a8ebb67572d32d9f0420a0aaed71 100644 (file)
@@ -2,13 +2,13 @@
 
 @interface B1 {
 @public
-  double fill_B;       // expected-note {{previous definition is here}}
+  double fill_B;       // expected-note {{previous declaration is here}}
 }
 @end
 
 @interface B : B1 {
 @public
-  int one;     // expected-note {{previous definition is here}}
+  int one;     // expected-note {{previous declaration is here}}
   int one;     // expected-error {{duplicate member 'one'}}
 }
 @end
diff --git a/test/SemaObjC/interface-scope.m b/test/SemaObjC/interface-scope.m
new file mode 100644 (file)
index 0000000..8f164a9
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang -fsyntax-only -verify %s
+
+@interface I1 {
+@private
+  int x;
+  struct {
+    unsigned int x : 3;
+    unsigned int y : 3;
+  } flags;
+  int y;
+}
+@end
index 3416b31112ac0dbd0e6b30d2320022c548a8d566..a6d1a22a9157399f5a10b75f5065f130dcb7b333 100644 (file)
@@ -12,7 +12,7 @@ typedef int FOO();
          struct T {} X;  // expected-error {{nested redefinition of 'T'}}
        }YYY; 
        FOO    BADFUNC;  // expected-error {{field 'BADFUNC' declared as a function}}
-       int kaka;       // expected-note {{previous definition is here}}
+       int kaka;       // expected-note {{previous declaration is here}}
        int kaka;       // expected-error {{duplicate member 'kaka'}}
        char ch[];      // expected-error {{field 'ch' has incomplete type}}
 }