[cfe-commits] r61840 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/AST/DeclBase.h include/clang/Basic/DiagnosticKinds.def lib/AST/Decl.cpp lib/AST/DeclBase.cpp lib/AST/DeclSerialization.cpp lib/Sema/Sema.h lib/Sema/SemaDecl.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaType.cpp

Douglas Gregor dgregor at apple.com
Tue Jan 6 16:43:41 PST 2009


Author: dgregor
Date: Tue Jan  6 18:43:41 2009
New Revision: 61840

URL: http://llvm.org/viewvc/llvm-project?rev=61840&view=rev
Log:
Initial implementation of anonymous unions (and, as a GNU extension,
structures and classes) in C++. Covers name lookup and the synthesis
and member access for the unnamed objects/fields associated with
anonymous unions.

Some C++ semantic checks are still missing (anonymous unions can't
have function members, static data members, etc.), and there is no
support for anonymous structs or unions in C.

Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclBase.h
    cfe/trunk/include/clang/Basic/DiagnosticKinds.def
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/AST/DeclSerialization.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaType.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Tue Jan  6 18:43:41 2009
@@ -1053,7 +1053,10 @@
   /// 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.
   bool HasFlexibleArrayMember : 1;
-  
+
+  ///
+  bool AnonymousStructOrUnion : 1;
+
 protected:
   RecordDecl(Kind DK, TagKind TK, DeclContext *DC,
              SourceLocation L, IdentifierInfo *Id);
@@ -1068,7 +1071,24 @@
       
   bool hasFlexibleArrayMember() const { return HasFlexibleArrayMember; }
   void setHasFlexibleArrayMember(bool V) { HasFlexibleArrayMember = V; }
-  
+
+  /// isAnonymousStructOrUnion - Whether this is an anonymous struct
+  /// or union. To be an anonymous struct or union, it must have been
+  /// declared without a name and there must be no objects of this
+  /// type declared, e.g.,
+  /// @code
+  ///   union { int i; float f; };
+  /// @endcode   
+  /// is an anonymous union but neither of the following are:
+  /// @code
+  ///  union X { int i; float f; };
+  ///  union { int i; float f; } obj;
+  /// @endcode
+  bool isAnonymousStructOrUnion() const { return AnonymousStructOrUnion; }
+  void setAnonymousStructOrUnion(bool Anon) {
+    AnonymousStructOrUnion = Anon;
+  }
+
   /// getDefinition - Returns the RecordDecl that actually defines this 
   ///  struct/union/class.  When determining whether or not a struct/union/class
   ///  is completely defined, one should use this method as opposed to

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Tue Jan  6 18:43:41 2009
@@ -384,8 +384,8 @@
     return DeclKind == Decl::TranslationUnit || DeclKind == Decl::Namespace;
   }
 
-  bool isCXXRecord() const {
-    return DeclKind == Decl::CXXRecord;
+  bool isRecord() const {
+    return DeclKind == Decl::Record || DeclKind == Decl::CXXRecord;
   }
 
   bool isNamespace() const {

Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Tue Jan  6 18:43:41 2009
@@ -1476,6 +1476,22 @@
      "base class initializer %0 names both a direct base class and an"
      " inherited virtual base class")
 
+// C++ anonymous unions and GNU anonymous structs
+DIAG(ext_anonymous_union, EXTENSION,
+     "anonymous unions are a GNU extension in C")
+DIAG(ext_anonymous_struct, EXTENSION,
+     "anonymous structs are a GNU extension")
+DIAG(err_anonymous_union_not_static, ERROR,
+     "anonymous unions at namespace or global scope must be declared 'static'")
+DIAG(err_anonymous_union_with_storage_spec, ERROR,
+     "anonymous union at class scope must not have a storage specifier")
+DIAG(err_anonymous_struct_not_member, ERROR,
+     "anonymous structs and classes must be class members")
+DIAG(err_anonymous_union_member_redecl, ERROR,
+     "member of anonymous union redeclares %0")
+DIAG(err_anonymous_struct_member_redecl, ERROR,
+     "member of anonymous struct redeclares %0")
+
 // Derived classes.
 DIAG(err_dup_virtual, ERROR,
      "duplicate 'virtual' in base specifier")

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Tue Jan  6 18:43:41 2009
@@ -290,7 +290,7 @@
 }
 
 //===----------------------------------------------------------------------===//
-// TagdDecl Implementation
+// TagDecl Implementation
 //===----------------------------------------------------------------------===//
 
 TagDecl* TagDecl::getDefinition(ASTContext& C) const {
@@ -308,6 +308,7 @@
   : TagDecl(DK, TK, DC, L, Id, 0), DeclContext(DK) {
   
   HasFlexibleArrayMember = false;
+  AnonymousStructOrUnion = false;
   assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
 }
 

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Tue Jan  6 18:43:41 2009
@@ -412,7 +412,7 @@
   else if (DeclKind == Decl::LinkageSpec)
     return true;
   else if (DeclKind == Decl::Record || DeclKind == Decl::CXXRecord)
-    return false; // FIXME: need to know about anonymous unions/structs
+    return cast<RecordDecl>(this)->isAnonymousStructOrUnion();
   else if (DeclKind == Decl::Namespace)
     return false; // FIXME: Check for C++0x inline namespaces
 

Modified: cfe/trunk/lib/AST/DeclSerialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclSerialization.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/AST/DeclSerialization.cpp (original)
+++ cfe/trunk/lib/AST/DeclSerialization.cpp Tue Jan  6 18:43:41 2009
@@ -618,6 +618,7 @@
   ScopedDecl::EmitInRec(S);
   S.EmitBool(isDefinition());
   S.EmitBool(hasFlexibleArrayMember());
+  S.EmitBool(isAnonymousStructOrUnion());
   ScopedDecl::EmitOutRec(S);
 }
 
@@ -630,6 +631,7 @@
   decl->ScopedDecl::ReadInRec(D, C);
   decl->setDefinition(D.ReadBool());
   decl->setHasFlexibleArrayMember(D.ReadBool());
+  decl->setAnonymousStructOrUnion(D.ReadBool());
   decl->ScopedDecl::ReadOutRec(D, C);
     
   return decl;

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Jan  6 18:43:41 2009
@@ -299,6 +299,11 @@
   /// no declarator (e.g. "struct foo;") is parsed.
   virtual DeclTy *ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS);
   
+  bool InjectAnonymousStructOrUnionMembers(Scope *S, DeclContext *Owner,
+                                           RecordDecl *AnonRecord);
+  virtual DeclTy *BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, 
+                                              RecordDecl *Record);
+
   virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK,
                            SourceLocation KWLoc, const CXXScopeSpec &SS,
                            IdentifierInfo *Name, SourceLocation NameLoc,
@@ -678,6 +683,11 @@
   DeclRefExpr *BuildDeclRefExpr(NamedDecl *D, QualType Ty, SourceLocation Loc,
                                 bool TypeDependent, bool ValueDependent,
                                 const CXXScopeSpec *SS = 0);
+  ExprResult 
+  BuildAnonymousStructUnionMemberReference(SourceLocation Loc,
+                                           FieldDecl *Field,
+                                           Expr *BaseObjectExpr = 0,
+                                      SourceLocation OpLoc = SourceLocation());
   ExprResult ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
                                       DeclarationName Name,
                                       bool HasTrailingLParen,

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Jan  6 18:43:41 2009
@@ -330,7 +330,7 @@
       DeclContext *Ctx = static_cast<DeclContext *>(S->getEntity());
       while (Ctx && Ctx->isFunctionOrMethod())
         Ctx = Ctx->getParent();
-      while (Ctx && (Ctx->isNamespace() || Ctx->isCXXRecord())) {
+      while (Ctx && (Ctx->isNamespace() || Ctx->isRecord())) {
         // Look for declarations of this name in this scope.
         DeclContext::lookup_const_iterator I, E;
         for (llvm::tie(I, E) = Ctx->lookup(Context, Name); I != E; ++I) {
@@ -807,8 +807,187 @@
       << DS.getSourceRange();
     return 0;
   }
+  
+  TagDecl *Tag 
+    = dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
+  if (RecordDecl *Record = dyn_cast_or_null<RecordDecl>(Tag)) {
+    if (!Record->getDeclName() && Record->isDefinition() && 
+        !Record->isInvalidDecl())
+      return BuildAnonymousStructOrUnion(S, DS, Record);
+  }
+
+  return Tag;
+}
+
+/// InjectAnonymousStructOrUnionMembers - Inject the members of the
+/// anonymous struct or union AnonRecord into the owning context Owner
+/// and scope S. This routine will be invoked just after we realize
+/// that an unnamed union or struct is actually an anonymous union or
+/// struct, e.g.,
+///
+/// @code
+/// union {
+///   int i;
+///   float f;
+/// }; // InjectAnonymousStructOrUnionMembers called here to inject i and
+///    // f into the surrounding scope.x
+/// @endcode
+///
+/// This routine is recursive, injecting the names of nested anonymous
+/// structs/unions into the owning context and scope as well.
+bool Sema::InjectAnonymousStructOrUnionMembers(Scope *S, DeclContext *Owner,
+                                               RecordDecl *AnonRecord) {
+  bool Invalid = false;
+  for (RecordDecl::field_iterator F = AnonRecord->field_begin(),
+                               FEnd = AnonRecord->field_end();
+       F != FEnd; ++F) {
+    if ((*F)->getDeclName()) {
+      Decl *PrevDecl = LookupDecl((*F)->getDeclName(), Decl::IDNS_Ordinary,
+                                  S, Owner, false, false, false);
+      if (PrevDecl && !isa<TagDecl>(PrevDecl)) {
+        // C++ [class.union]p2:
+        //   The names of the members of an anonymous union shall be
+        //   distinct from the names of any other entity in the
+        //   scope in which the anonymous union is declared.
+        unsigned diagKind 
+          = AnonRecord->isUnion()? diag::err_anonymous_union_member_redecl
+                                 : diag::err_anonymous_struct_member_redecl;
+        Diag((*F)->getLocation(), diagKind)
+          << (*F)->getDeclName();
+        Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+        Invalid = true;
+      } else {
+        // C++ [class.union]p2:
+        //   For the purpose of name lookup, after the anonymous union
+        //   definition, the members of the anonymous union are
+        //   considered to have been defined in the scope in which the
+        //   anonymous union is declared.
+        Owner->insert(Context, *F);
+        S->AddDecl(*F);
+        IdResolver.AddDecl(*F);
+      }
+    } else if (const RecordType *InnerRecordType
+                 = (*F)->getType()->getAsRecordType()) {
+      RecordDecl *InnerRecord = InnerRecordType->getDecl();
+      if (InnerRecord->isAnonymousStructOrUnion())
+        Invalid = Invalid || 
+          InjectAnonymousStructOrUnionMembers(S, Owner, InnerRecord);
+    }
+  }
+
+  return Invalid;
+}
+
+/// ActOnAnonymousStructOrUnion - Handle the declaration of an
+/// anonymous structure or union. Anonymous unions are a C++ feature
+/// (C++ [class.union]) and a GNU C extension; anonymous structures
+/// are a GNU C and GNU C++ extension. 
+Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
+                                                RecordDecl *Record) {
+  DeclContext *Owner = Record->getDeclContext();
+
+  // Diagnose whether this anonymous struct/union is an extension.
+  if (Record->isUnion() && !getLangOptions().CPlusPlus)
+    Diag(Record->getLocation(), diag::ext_anonymous_union);
+  else if (!Record->isUnion())
+    Diag(Record->getLocation(), diag::ext_anonymous_struct);
+  
+  // C and C++ require different kinds of checks for anonymous
+  // structs/unions.
+  bool Invalid = false;
+  if (getLangOptions().CPlusPlus) {
+    const char* PrevSpec = 0;
+    // C++ [class.union]p3:
+    //   Anonymous unions declared in a named namespace or in the
+    //   global namespace shall be declared static.
+    if (DS.getStorageClassSpec() != DeclSpec::SCS_static &&
+        (isa<TranslationUnitDecl>(Owner) ||
+         (isa<NamespaceDecl>(Owner) && 
+          cast<NamespaceDecl>(Owner)->getDeclName()))) {
+      Diag(Record->getLocation(), diag::err_anonymous_union_not_static);
+      Invalid = true;
 
-  return dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
+      // Recover by adding 'static'.
+      DS.SetStorageClassSpec(DeclSpec::SCS_static, SourceLocation(), PrevSpec);
+    } 
+    // C++ [class.union]p3:
+    //   A storage class is not allowed in a declaration of an
+    //   anonymous union in a class scope.
+    else if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified &&
+             isa<RecordDecl>(Owner)) {
+      Diag(DS.getStorageClassSpecLoc(), 
+           diag::err_anonymous_union_with_storage_spec);
+      Invalid = true;
+
+      // Recover by removing the storage specifier.
+      DS.SetStorageClassSpec(DeclSpec::SCS_unspecified, SourceLocation(),
+                             PrevSpec);
+    }
+  } else {
+    // FIXME: Check GNU C semantics
+  }
+
+  if (!Record->isUnion() && !Owner->isRecord()) {
+    Diag(Record->getLocation(), diag::err_anonymous_struct_not_member);
+    Invalid = true;
+  }
+
+  // Create a declaration for this anonymous struct/union. 
+  ScopedDecl *Anon = 0;
+  if (RecordDecl *OwningClass = dyn_cast<RecordDecl>(Owner)) {
+    Anon = FieldDecl::Create(Context, OwningClass, Record->getLocation(),
+                             /*IdentifierInfo=*/0, 
+                             Context.getTypeDeclType(Record),
+                             /*BitWidth=*/0, /*Mutable=*/false,
+                             /*PrevDecl=*/0);
+  } else {
+    VarDecl::StorageClass SC;
+    switch (DS.getStorageClassSpec()) {
+    default: assert(0 && "Unknown storage class!");
+    case DeclSpec::SCS_unspecified:    SC = VarDecl::None; break;
+    case DeclSpec::SCS_extern:         SC = VarDecl::Extern; break;
+    case DeclSpec::SCS_static:         SC = VarDecl::Static; break;
+    case DeclSpec::SCS_auto:           SC = VarDecl::Auto; break;
+    case DeclSpec::SCS_register:       SC = VarDecl::Register; break;
+    case DeclSpec::SCS_private_extern: SC = VarDecl::PrivateExtern; break;
+    case DeclSpec::SCS_mutable:
+      // mutable can only appear on non-static class members, so it's always
+      // an error here
+      Diag(Record->getLocation(), diag::err_mutable_nonmember);
+      Invalid = true;
+      SC = VarDecl::None;
+      break;
+    }
+
+    Anon = VarDecl::Create(Context, Owner, Record->getLocation(),
+                           /*IdentifierInfo=*/0, 
+                           Context.getTypeDeclType(Record),
+                           SC, /*FIXME:LastDeclarator=*/0,
+                           DS.getSourceRange().getBegin());
+  }
+
+  // Add the anonymous struct/union object to the current
+  // context. We'll be referencing this object when we refer to one of
+  // its members.
+  Owner->addDecl(Context, Anon);
+
+  // Inject the members of the anonymous struct/union into the owning
+  // context and into the identifier resolver chain for name lookup
+  // purposes.
+  Invalid = Invalid || InjectAnonymousStructOrUnionMembers(S, Owner, Record);
+
+  // Mark this as an anonymous struct/union type. Note that we do not
+  // do this until after we have already checked and injected the
+  // members of this anonymous struct/union type, because otherwise
+  // the members could be injected twice: once by DeclContext when it
+  // builds its lookup table, and once by
+  // InjectAnonymousStructOrUnionMembers. 
+  Record->setAnonymousStructOrUnion(true);
+
+  if (Invalid)
+    Anon->setInvalidDecl();
+
+  return Anon;
 }
 
 bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType) {  
@@ -1170,7 +1349,7 @@
     FunctionDecl *NewFD;
     if (D.getKind() == Declarator::DK_Constructor) {
       // This is a C++ constructor declaration.
-      assert(DC->isCXXRecord() &&
+      assert(DC->isRecord() &&
              "Constructors can only be declared in a member context");
 
       InvalidDecl = InvalidDecl || CheckConstructorDeclarator(D, R, SC);
@@ -1186,7 +1365,7 @@
         NewFD->setInvalidDecl();
     } else if (D.getKind() == Declarator::DK_Destructor) {
       // This is a C++ destructor declaration.
-      if (DC->isCXXRecord()) {
+      if (DC->isRecord()) {
         InvalidDecl = InvalidDecl || CheckDestructorDeclarator(D, R, SC);
 
         NewFD = CXXDestructorDecl::Create(Context,
@@ -1210,7 +1389,7 @@
         NewFD->setInvalidDecl();
       }
     } else if (D.getKind() == Declarator::DK_Conversion) {
-      if (!DC->isCXXRecord()) {
+      if (!DC->isRecord()) {
         Diag(D.getIdentifierLoc(),
              diag::err_conv_function_not_member);
         return 0;
@@ -1224,7 +1403,7 @@
         if (InvalidDecl)
           NewFD->setInvalidDecl();
       }
-    } else if (DC->isCXXRecord()) {
+    } else if (DC->isRecord()) {
       // This is a C++ method declaration.
       NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(DC),
                                     D.getIdentifierLoc(), Name, R,
@@ -1473,7 +1652,7 @@
       return 0;
     }
 
-    if (DC->isCXXRecord()) {
+    if (DC->isRecord()) {
       // This is a static data member for a C++ class.
       NewVD = CXXClassVarDecl::Create(Context, cast<CXXRecordDecl>(DC),
                                       D.getIdentifierLoc(), II,
@@ -2716,6 +2895,7 @@
   // declaration of the same entity, the two will be linked via
   // PrevDecl.
   TagDecl *New;
+
   if (Kind == TagDecl::TK_enum) {
     // FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.:
     // enum X { A, B, C } D;    D should chain to X.
@@ -2769,6 +2949,12 @@
     
     // Add it to the decl chain.
     PushOnScopeChains(New, S);
+  } else if (getLangOptions().CPlusPlus) {
+    // FIXME: We also want to do this for C, but if this tag is
+    // defined within a structure CurContext will point to the context
+    // enclosing the structure, and we would end up inserting the tag
+    // type into the wrong place.
+    CurContext->addDecl(Context, New);
   }
 
   return New;
@@ -2869,8 +3055,6 @@
   // FIXME: Chain fielddecls together.
   FieldDecl *NewFD;
 
-  // FIXME: We don't want CurContext for C, do we? No, we'll need some
-  // other way to determine the current RecordDecl.
   NewFD = FieldDecl::Create(Context, Record,
                             Loc, II, T, BitWidth,
                             D.getDeclSpec().getStorageClassSpec() ==

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Jan  6 18:43:41 2009
@@ -371,6 +371,143 @@
     return new DeclRefExpr(D, Ty, Loc, TypeDependent, ValueDependent);
 }
 
+/// getObjectForAnonymousRecordDecl - Retrieve the (unnamed) field or
+/// variable corresponding to the anonymous union or struct whose type
+/// is Record.
+static ScopedDecl *getObjectForAnonymousRecordDecl(RecordDecl *Record) {
+  assert(Record->isAnonymousStructOrUnion() && 
+         "Record must be an anonymous struct or union!");
+  
+  // FIXME: Once ScopedDecls are directly linked together, this will
+  // be an O(1) operation rather than a slow walk through DeclContext's
+  // vector (which itself will be eliminated). DeclGroups might make
+  // this even better.
+  DeclContext *Ctx = Record->getDeclContext();
+  for (DeclContext::decl_iterator D = Ctx->decls_begin(), 
+                               DEnd = Ctx->decls_end();
+       D != DEnd; ++D) {
+    if (*D == Record) {
+      // The object for the anonymous struct/union directly
+      // follows its type in the list of declarations.
+      ++D;
+      assert(D != DEnd && "Missing object for anonymous record");
+      assert(!cast<ScopedDecl>(*D)->getDeclName() && "Decl should be unnamed");
+      return *D;
+    }
+  }
+
+  assert(false && "Missing object for anonymous record");
+  return 0;
+}
+
+Sema::ExprResult 
+Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc,
+                                               FieldDecl *Field,
+                                               Expr *BaseObjectExpr,
+                                               SourceLocation OpLoc) {
+  assert(Field->getDeclContext()->isRecord() &&
+         cast<RecordDecl>(Field->getDeclContext())->isAnonymousStructOrUnion()
+         && "Field must be stored inside an anonymous struct or union");
+
+  // Construct the sequence of field member references
+  // we'll have to perform to get to the field in the anonymous
+  // union/struct. The list of members is built from the field
+  // outward, so traverse it backwards to go from an object in
+  // the current context to the field we found.
+  llvm::SmallVector<FieldDecl *, 4> AnonFields;
+  AnonFields.push_back(Field);
+  VarDecl *BaseObject = 0;
+  DeclContext *Ctx = Field->getDeclContext();
+  do {
+    RecordDecl *Record = cast<RecordDecl>(Ctx);
+    ScopedDecl *AnonObject = getObjectForAnonymousRecordDecl(Record);
+    if (FieldDecl *AnonField = dyn_cast<FieldDecl>(AnonObject))
+      AnonFields.push_back(AnonField);
+    else {
+      BaseObject = cast<VarDecl>(AnonObject);
+      break;
+    }
+    Ctx = Ctx->getParent();
+  } while (Ctx->isRecord() && 
+           cast<RecordDecl>(Ctx)->isAnonymousStructOrUnion());
+  
+  // Build the expression that refers to the base object, from
+  // which we will build a sequence of member references to each
+  // of the anonymous union objects and, eventually, the field we
+  // found via name lookup.
+  bool BaseObjectIsPointer = false;
+  unsigned ExtraQuals = 0;
+  if (BaseObject) {
+    // BaseObject is an anonymous struct/union variable (and is,
+    // therefore, not part of another non-anonymous record).
+    delete BaseObjectExpr;
+
+    BaseObjectExpr = new DeclRefExpr(BaseObject, BaseObject->getType(),
+                                     SourceLocation());
+    ExtraQuals 
+      = Context.getCanonicalType(BaseObject->getType()).getCVRQualifiers();
+  } else if (BaseObjectExpr) {
+    // The caller provided the base object expression. Determine
+    // whether its a pointer and whether it adds any qualifiers to the
+    // anonymous struct/union fields we're looking into.
+    QualType ObjectType = BaseObjectExpr->getType();
+    if (const PointerType *ObjectPtr = ObjectType->getAsPointerType()) {
+      BaseObjectIsPointer = true;
+      ObjectType = ObjectPtr->getPointeeType();
+    }
+    ExtraQuals = Context.getCanonicalType(ObjectType).getCVRQualifiers();
+  } else {
+    // We've found a member of an anonymous struct/union that is
+    // inside a non-anonymous struct/union, so in a well-formed
+    // program our base object expression is "this".
+    if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) {
+      if (!MD->isStatic()) {
+        QualType AnonFieldType 
+          = Context.getTagDeclType(
+                     cast<RecordDecl>(AnonFields.back()->getDeclContext()));
+        QualType ThisType = Context.getTagDeclType(MD->getParent());
+        if ((Context.getCanonicalType(AnonFieldType) 
+               == Context.getCanonicalType(ThisType)) ||
+            IsDerivedFrom(ThisType, AnonFieldType)) {
+          // Our base object expression is "this".
+          BaseObjectExpr = new CXXThisExpr(SourceLocation(),
+                                           MD->getThisType(Context));
+          BaseObjectIsPointer = true;
+        }
+      } else {
+        return Diag(Loc, diag::err_invalid_member_use_in_static_method)
+          << Field->getDeclName();
+      }
+      ExtraQuals = MD->getTypeQualifiers();
+    }
+
+    if (!BaseObjectExpr) 
+      return Diag(Loc, diag::err_invalid_non_static_member_use)
+        << Field->getDeclName();
+  }
+
+  // Build the implicit member references to the field of the
+  // anonymous struct/union.
+  Expr *Result = BaseObjectExpr;
+  for (llvm::SmallVector<FieldDecl *, 4>::reverse_iterator
+         FI = AnonFields.rbegin(), FIEnd = AnonFields.rend();
+       FI != FIEnd; ++FI) {
+    QualType MemberType = (*FI)->getType();
+    if (!(*FI)->isMutable()) {
+      unsigned combinedQualifiers 
+        = MemberType.getCVRQualifiers() | ExtraQuals;
+      MemberType = MemberType.getQualifiedType(combinedQualifiers);
+    }
+    Result = new MemberExpr(Result, BaseObjectIsPointer, *FI,
+                            OpLoc, MemberType);
+    BaseObjectIsPointer = false;
+    ExtraQuals = Context.getCanonicalType(MemberType).getCVRQualifiers();
+    OpLoc = SourceLocation();
+  }
+
+  return Result;  
+}
+
 /// ActOnDeclarationNameExpr - The parser has read some kind of name
 /// (e.g., a C++ id-expression (C++ [expr.prim]p1)). This routine
 /// performs lookup on that name and returns an expression that refers
@@ -467,6 +604,12 @@
         return Diag(Loc, diag::err_undeclared_var_use) << Name;
     }
   }
+
+  // We may have found a field within an anonymous union or struct
+  // (C++ [class.union]).
+  if (FieldDecl *FD = dyn_cast<FieldDecl>(D))
+    if (cast<RecordDecl>(FD->getDeclContext())->isAnonymousStructOrUnion())
+      return BuildAnonymousStructUnionMemberReference(Loc, FD);
   
   if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) {
     if (!MD->isStatic()) {
@@ -508,8 +651,8 @@
             }
         }
       }
-      
-      if (Ctx && Ctx->isCXXRecord()) {
+
+      if (Ctx && Ctx->isRecord()) {
         QualType CtxType = Context.getTagDeclType(cast<CXXRecordDecl>(Ctx));
         QualType ThisType = Context.getTagDeclType(MD->getParent());
         if ((Context.getCanonicalType(CtxType) 
@@ -623,7 +766,7 @@
       for (DeclContext *DC = static_cast<DeclContext*>(SS->getScopeRep()); 
            DC; DC = DC->getParent()) {
         // FIXME: could stop early at namespace scope.
-        if (DC->isCXXRecord()) {
+        if (DC->isRecord()) {
           CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
           if (Context.getTypeDeclType(Record)->isDependentType()) {
             TypeDependent = true;
@@ -1308,6 +1451,12 @@
                << &Member << BaseExpr->getSourceRange();
 
     if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) {
+      // We may have found a field within an anonymous union or struct
+      // (C++ [class.union]).
+      if (cast<RecordDecl>(FD->getDeclContext())->isAnonymousStructOrUnion())
+        return BuildAnonymousStructUnionMemberReference(MemberLoc, FD, 
+                                                        BaseExpr, OpLoc);
+
       // Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref]
       // FIXME: Handle address space modifiers
       QualType MemberType = FD->getType();

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Jan  6 18:43:41 2009
@@ -2169,7 +2169,7 @@
 
       // Ignore member functions. 
       if (ScopedDecl *SD = dyn_cast<ScopedDecl>(*I)) {
-        if (SD->getDeclContext()->isCXXRecord())
+        if (SD->getDeclContext()->isRecord())
           continue;
       } 
 

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=61840&r1=61839&r2=61840&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Tue Jan  6 18:43:41 2009
@@ -538,7 +538,7 @@
         ((D.getContext() != Declarator::MemberContext &&
           (!D.getCXXScopeSpec().isSet() ||
            !static_cast<DeclContext*>(D.getCXXScopeSpec().getScopeRep())
-             ->isCXXRecord())) ||
+              ->isRecord())) ||
          D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
       if (D.isFunctionDeclarator())
         Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);





More information about the cfe-commits mailing list