]> granicus.if.org Git - clang/commitdiff
Implement support for anonymous structs and unions in C. Both C and
authorDouglas Gregor <dgregor@apple.com>
Mon, 12 Jan 2009 22:49:06 +0000 (22:49 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 12 Jan 2009 22:49:06 +0000 (22:49 +0000)
C++ handle anonymous structs/unions in the same way. Addresses several
bugs:

  <rdar://problem/6259534>
  <rdar://problem/6481130>
  <rdar://problem/6483159>

The test case in PR clang/1750 now passes with -fsyntax-only, but
CodeGen for inline assembler still fails.

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

Driver/clang.cpp
include/clang/Basic/DiagnosticKinds.def
lib/Parse/DeclSpec.cpp
lib/Parse/ParseDecl.cpp
lib/Sema/SemaDecl.cpp
test/Parser/cxx-class.cpp
test/Sema/anonymous-struct-union.c [new file with mode: 0644]
test/SemaCXX/anonymous-union.cpp
test/SemaObjC/property-9.m

index 6b3507e3a6c1000455a69b1d145427e4064d4d99..51b1335f9c43b2426ab97a25a0140416805ea158 100644 (file)
@@ -714,9 +714,6 @@ static void InitializeDiagnostics(Diagnostic &Diags) {
     Diags.setDiagnosticMapping(diag::warn_implicit_function_decl,
                                diag::MAP_IGNORE);
   
-  if (MSExtensions) // MS allows unnamed struct/union fields.
-    Diags.setDiagnosticMapping(diag::w_no_declarators, diag::MAP_IGNORE);
-
   // If -pedantic-errors is set, turn extensions that warn by default into
   // errors. 
   if (ErrorOnExtensions) {
index bac6f242e0364423a68636a09c59043e73a480d9..ee0cad4d13f966451a5d367087a957e0e2d7de5a 100644 (file)
@@ -270,8 +270,6 @@ DIAG(err_pp_I_dash_not_supported, ERROR,
 // Parser Diagnostics
 //===----------------------------------------------------------------------===//
 
-DIAG(w_no_declarators, WARNING,
-     "declaration does not declare anything")
 DIAG(w_asm_qualifier_ignored, WARNING,
      "ignored %0 qualifier on asm")
 
@@ -1496,7 +1494,9 @@ DIAG(err_anonymous_union_not_static, ERROR,
 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")
+     "anonymous %select{structs|structs and classes}0 must be %select{struct or union|class}0 members")
+DIAG(err_anonymous_union_not_member, ERROR,
+     "anonymous unions must be struct or union members")
 DIAG(err_anonymous_union_member_redecl, ERROR,
      "member of anonymous union redeclares %0")
 DIAG(err_anonymous_struct_member_redecl, ERROR,
index 258dc87c446bf3ebff92b3bc4615f6f55393c0ec..4459c804f3768693a860b3b99cf9691f64dae24d 100644 (file)
@@ -319,5 +319,5 @@ bool DeclSpec::isMissingDeclaratorOk() {
        || tst == TST_struct
        || tst == TST_class
        || tst == TST_enum
-      ) && getTypeRep() != 0;
+          ) && getTypeRep() != 0 && StorageClassSpec != DeclSpec::SCS_typedef;
 }
index 3ec1764612a70ba908a7e3b67da97b8aa9198a52..8d4705a7bf21a4ec0e7ed15c57f4c253e9e23333 100644 (file)
@@ -888,9 +888,10 @@ ParseStructDeclaration(DeclSpec &DS,
   SourceLocation DSStart = Tok.getLocation();
   ParseSpecifierQualifierList(DS);
   
-  // If there are no declarators, issue a warning.
+  // If there are no declarators, this is a free-standing declaration
+  // specifier. Let the actions module cope with it.
   if (Tok.is(tok::semi)) {
-    Diag(DSStart, diag::w_no_declarators);
+    Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
     return;
   }
 
index f3f0a08bee2c25a757a0e91b68e35f424b5cfc55..6aeda8bc0b5e25e6c6a6015369c11b03afcf1ea3 100644 (file)
@@ -861,7 +861,20 @@ bool Sema::CheckParmsForFunctionDef(FunctionDecl *FD) {
 /// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with
 /// no declarator (e.g. "struct foo;") is parsed.
 Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
-  // FIXME: Isn't that more of a parser diagnostic than a sema diagnostic?
+  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() &&
+        DS.getStorageClassSpec() != DeclSpec::SCS_typedef)
+      return BuildAnonymousStructOrUnion(S, DS, Record);
+
+    // Microsoft allows unnamed struct/union fields. Don't complain
+    // about them.
+    // FIXME: Should we support Microsoft's extensions in this area?
+    if (Record->getDeclName() && getLangOptions().Microsoft)
+      return Tag;
+  }
+
   if (!DS.isMissingDeclaratorOk()) {
     // FIXME: This diagnostic is emitted even when various previous
     // errors occurred (see e.g. test/Sema/decl-invalid.c). However,
@@ -872,14 +885,6 @@ Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
     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;
 }
 
@@ -1031,10 +1036,16 @@ Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
     }
   } else {
     // FIXME: Check GNU C semantics
+    if (Record->isUnion() && !Owner->isRecord()) {
+      Diag(Record->getLocation(), diag::err_anonymous_union_not_member)
+        << (int)getLangOptions().CPlusPlus;
+      Invalid = true;
+    }
   }
 
   if (!Record->isUnion() && !Owner->isRecord()) {
-    Diag(Record->getLocation(), diag::err_anonymous_struct_not_member);
+    Diag(Record->getLocation(), diag::err_anonymous_struct_not_member)
+      << (int)getLangOptions().CPlusPlus;
     Invalid = true;
   }
 
@@ -1084,7 +1095,8 @@ Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
   // 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);
+  if (InjectAnonymousStructOrUnionMembers(S, Owner, Record))
+    Invalid = true;
 
   // Mark this as an anonymous struct/union type. Note that we do not
   // do this until after we have already checked and injected the
@@ -2890,6 +2902,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
   case DeclSpec::TST_enum:   Kind = TagDecl::TK_enum; break;
   }
   
+  DeclContext *SearchDC = CurContext;
   DeclContext *DC = CurContext;
   DeclContext *LexicalContext = CurContext;
   ScopedDecl *PrevDecl = 0;
@@ -2924,8 +2937,8 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
       // 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();
+      while (isa<RecordDecl>(SearchDC) || isa<EnumDecl>(SearchDC))
+        SearchDC = SearchDC->getParent();
     }
   }
 
@@ -2943,7 +2956,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
       // If this is a use of a previous tag, or if the tag is already declared
       // in the same scope (so that the definition/declaration completes or
       // rementions the tag), reuse the decl.
-      if (TK == TK_Reference || isDeclInScope(PrevDecl, DC, S)) {
+      if (TK == TK_Reference || isDeclInScope(PrevDecl, SearchDC, S)) {
         // Make sure that this wasn't declared as an enum and now used as a
         // struct or something similar.
         if (PrevTagDecl->getTagKind() != Kind) {
@@ -2991,7 +3004,7 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
       // PrevDecl. If it's NULL, we have a new definition.
     } else {
       // PrevDecl is a namespace.
-      if (isDeclInScope(PrevDecl, DC, S)) {
+      if (isDeclInScope(PrevDecl, SearchDC, S)) {
         // The tag name clashes with a namespace name, issue an error and
         // recover by making this tag be anonymous.
         Diag(NameLoc, diag::err_redefinition_different_kind) << Name;
@@ -3024,6 +3037,8 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagType, TagKind TK,
     // C structs and unions.
 
     // Find the context where we'll be declaring the tag.
+    // FIXME: We would like to maintain the current DeclContext as the
+    // lexical context, 
     while (DC->isRecord())
       DC = DC->getParent();
     LexicalContext = DC;
@@ -3110,11 +3125,7 @@ CreateNewDecl:
       CurContext = OldContext;
     } else 
       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.
+  } else {
     LexicalContext->addDecl(Context, New);
   }
 
index 39b787fc8dcd8765a5863d204be2bc1e43c94603..7ad63446ab559ee4666a3e6e7ad89ce5fcea3862 100644 (file)
@@ -8,7 +8,7 @@ protected:
 
   struct S {};
   enum {};
-  int; // expected-error {{error: declaration does not declare anything}}
+  int; // expected-error {{declaration does not declare anything}}
   int : 1, : 2;
 
 public:
diff --git a/test/Sema/anonymous-struct-union.c b/test/Sema/anonymous-struct-union.c
new file mode 100644 (file)
index 0000000..72790c9
--- /dev/null
@@ -0,0 +1,82 @@
+// RUN: clang -fsyntax-only -verify %s
+struct X {
+  union {
+    float f3;
+    double d2;
+  } named;
+
+  union {
+    int i;
+    float f;
+    
+    union {
+      float f2;
+      double d;
+    };
+  };
+
+  struct {
+    int a;
+    float b;
+  };
+};
+
+void test_unqual_references(struct X x, const struct X xc) {
+  x.i = 0;
+  x.f = 0.0;
+  x.f2 = x.f;
+  x.d = x.f;
+  x.f3 = 0; // expected-error{{no member named 'f3'}}
+  x.a = 0;
+
+  xc.d = 0.0; // expected-error{{read-only variable is not assignable}}
+  xc.f = 0; // expected-error{{read-only variable is not assignable}}
+  xc.a = 0; // expected-error{{read-only variable is not assignable}}
+}
+
+
+struct Redecl {
+  int x; // expected-note{{previous declaration is here}}
+  struct y { };
+
+  union {
+    int x; // expected-error{{member of anonymous union redeclares 'x'}}
+    float y;
+    double z; // expected-note{{previous declaration is here}}
+    double zz; // expected-note{{previous declaration is here}}
+  };
+
+  int z; // expected-error{{duplicate member 'z'}}
+  void zz(); // expected-error{{duplicate member 'zz'}} \
+            //  expected-error{{field 'zz' declared as a function}}
+};
+
+union { // expected-error{{anonymous unions must be struct or union members}}
+  int int_val;
+  float float_val;
+};
+
+static union { // expected-error{{anonymous unions must be struct or union members}}
+  int int_val2;
+  float float_val2;
+};
+
+void f() {
+  int_val2 = 0; // expected-error{{use of undeclared identifier}}
+  float_val2 = 0.0; // expected-error{{use of undeclared identifier}}
+}
+
+void g() {
+  union { // expected-error{{anonymous unions must be struct or union members}}
+    int i;
+    float f2;
+  };
+  i = 0; // expected-error{{use of undeclared identifier}}
+  f2 = 0.0; // expected-error{{use of undeclared identifier}}
+}
+
+// <rdar://problem/6483159>
+struct s0 { union { int f0; }; };
+
+// <rdar://problem/6481130>
+typedef struct { }; // expected-error{{declaration does not declare anything}}
index 872c45c468979cd19dcdfe7238d84742b1ec5226..5860ed4af7889808cb4fd96846432c943b17e7b2 100644 (file)
@@ -108,3 +108,6 @@ struct BadMembers {
   protected: float x2; // expected-error{{anonymous union cannot contain a protected data member}}
   };
 };
+
+// <rdar://problem/6481130>
+typedef union { }; // expected-error{{declaration does not declare anything}}
index 9275b51d25b55c426f571e586e1ef8e1d13d9fed..9aafcc9ef61b5549f24b0f4f2c8305827585b3de 100644 (file)
@@ -43,9 +43,9 @@ typedef signed char BOOL;
  int _awesome;
 }
 
-@property (readonly) int; // expected-warning {{declaration does not declare anything}}
+@property (readonly) int; // expected-error {{declaration does not declare anything}}
 @property (readonly) ; // expected-error {{type name requires a specifier or qualifier}} \
-                          expected-warning {{declaration does not declare anything}}
+                          expected-error {{declaration does not declare anything}}
 @property (readonly) int : 4; // expected-error {{property requires fields to be named}}