]> granicus.if.org Git - clang/commitdiff
Microsoft C anonymous struct implementation.
authorFrancois Pichet <pichet2000@gmail.com>
Tue, 23 Nov 2010 06:07:27 +0000 (06:07 +0000)
committerFrancois Pichet <pichet2000@gmail.com>
Tue, 23 Nov 2010 06:07:27 +0000 (06:07 +0000)
Documentation: http://msdn.microsoft.com/en-us/library/z2cx9y4f.aspx

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

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaDecl.cpp
test/CodeGen/ms-anonymous-struct.c [new file with mode: 0644]
test/Sema/MicrosoftExtensions.c

index 198f0e16395977510ee438eb3bcacbee7c5db8e3..fd049e37d90f06446600c455d84f81748fa1876f 100644 (file)
@@ -3030,6 +3030,8 @@ def err_anonymous_record_bad_member : Error<
 def err_anonymous_record_nonpublic_member : Error<
   "anonymous %select{struct|union}0 cannot contain a "
   "%select{private|protected}1 data member">;
+def ext_ms_anonymous_struct : ExtWarn<
+  "anonymous structs are a Microsoft extension">, InGroup<Microsoft>;
 
 // C++ local classes
 def err_reference_to_local_var_in_enclosing_function : Error<
index 4769722159e11744b298727d277ccf30dcf557c9..a3299331335880103ff4308f97563fdc48e5aea8 100644 (file)
@@ -787,6 +787,9 @@ public:
                                     AccessSpecifier AS,
                                     RecordDecl *Record);
 
+  Decl *BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS, 
+                                       RecordDecl *Record);
+
   bool isAcceptableTagRedeclaration(const TagDecl *Previous,
                                     TagTypeKind NewTag,
                                     SourceLocation NewTagLoc,
index 2b970a34c450bc7a6d380807347cad8a15f19a3b..a3b8b8ec30f87ce1da0d473ea53794640e56836e 100644 (file)
@@ -1646,12 +1646,24 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
       Diag(DS.getSourceRange().getBegin(), diag::ext_no_declarators)
         << DS.getSourceRange();
     }
+  }
 
-    // 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;
+  // Check for Microsoft C extension: anonymous struct.
+  if (getLangOptions().Microsoft && !getLangOptions().CPlusPlus &&
+      CurContext->isRecord() &&
+      DS.getStorageClassSpec() == DeclSpec::SCS_unspecified) {
+    // Handle 2 kinds of anonymous struct:
+    //   struct STRUCT;
+    // and
+    //   STRUCT_TYPE;  <- where STRUCT_TYPE is a typedef struct.
+    RecordDecl *Record = dyn_cast_or_null<RecordDecl>(Tag);
+    if ((Record && Record->getDeclName() && !Record->isDefinition()) ||
+        (DS.getTypeSpecType() == DeclSpec::TST_typename &&
+         DS.getRepAsType().get()->isStructureType())) {
+      Diag(DS.getSourceRange().getBegin(), diag::ext_ms_anonymous_struct)
+        << DS.getSourceRange();
+      return BuildMicrosoftCAnonymousStruct(S, DS, Record);
+    }
   }
   
   if (getLangOptions().CPlusPlus && 
@@ -1749,18 +1761,23 @@ static bool InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S,
                                                 DeclContext *Owner,
                                                 RecordDecl *AnonRecord,
                                                 AccessSpecifier AS,
-                              llvm::SmallVector<NamedDecl*, 2> &Chaining) {
+                              llvm::SmallVector<NamedDecl*, 2> &Chaining,
+                                                      bool MSAnonStruct) {
   unsigned diagKind
     = AnonRecord->isUnion() ? diag::err_anonymous_union_member_redecl
                             : diag::err_anonymous_struct_member_redecl;
 
   bool Invalid = false;
-  for (RecordDecl::field_iterator F = AnonRecord->field_begin(),
-                               FEnd = AnonRecord->field_end();
-       F != FEnd; ++F) {
-    if ((*F)->getDeclName()) {
-      if (CheckAnonMemberRedeclaration(SemaRef, S, Owner, (*F)->getDeclName(),
-                                       (*F)->getLocation(), diagKind)) {
+
+  // Look every FieldDecl and IndirectFieldDecl with a name.
+  for (RecordDecl::decl_iterator D = AnonRecord->decls_begin(),
+                               DEnd = AnonRecord->decls_end();
+       D != DEnd; ++D) {
+    if ((isa<FieldDecl>(*D) || isa<IndirectFieldDecl>(*D)) &&
+        cast<NamedDecl>(*D)->getDeclName()) {
+      ValueDecl *VD = cast<ValueDecl>(*D);
+      if (CheckAnonMemberRedeclaration(SemaRef, S, Owner, VD->getDeclName(),
+                                       VD->getLocation(), diagKind)) {
         // 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
@@ -1772,7 +1789,14 @@ static bool InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S,
         //   definition, the members of the anonymous union are
         //   considered to have been defined in the scope in which the
         //   anonymous union is declared.
-        Chaining.push_back(*F);
+        unsigned OldChainingSize = Chaining.size();
+        if (IndirectFieldDecl *IF = dyn_cast<IndirectFieldDecl>(VD))
+          for (IndirectFieldDecl::chain_iterator PI = IF->chain_begin(),
+               PE = IF->chain_end(); PI != PE; ++PI)
+            Chaining.push_back(*PI);
+        else
+          Chaining.push_back(VD);
+
         assert(Chaining.size() >= 2);
         NamedDecl **NamedChain =
           new (SemaRef.Context)NamedDecl*[Chaining.size()];
@@ -1780,8 +1804,8 @@ static bool InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S,
           NamedChain[i] = Chaining[i];
 
         IndirectFieldDecl* IndirectField =
-          IndirectFieldDecl::Create(SemaRef.Context, Owner, F->getLocation(),
-                                    F->getIdentifier(), F->getType(),
+          IndirectFieldDecl::Create(SemaRef.Context, Owner, VD->getLocation(),
+                                    VD->getIdentifier(), VD->getType(),
                                     NamedChain, Chaining.size());
 
         IndirectField->setAccess(AS);
@@ -1789,20 +1813,10 @@ static bool InjectAnonymousStructOrUnionMembers(Sema &SemaRef, Scope *S,
         SemaRef.PushOnScopeChains(IndirectField, S);
 
         // That includes picking up the appropriate access specifier.
-        if (AS != AS_none) (*F)->setAccess(AS);
+        if (AS != AS_none) IndirectField->setAccess(AS);
 
-        Chaining.pop_back();
+        Chaining.resize(OldChainingSize);
       }
-    } else if (const RecordType *InnerRecordType
-                 = (*F)->getType()->getAs<RecordType>()) {
-      RecordDecl *InnerRecord = InnerRecordType->getDecl();
-
-      Chaining.push_back(*F);
-      if (InnerRecord->isAnonymousStructOrUnion())
-        Invalid = Invalid ||
-          InjectAnonymousStructOrUnionMembers(SemaRef, S, Owner,
-                                              InnerRecord, AS, Chaining);
-      Chaining.pop_back();
     }
   }
 
@@ -1847,7 +1861,7 @@ StorageClassSpecToFunctionDeclStorageClass(DeclSpec::SCS StorageClassSpec) {
   llvm_unreachable("unknown storage class specifier");
 }
 
-/// ActOnAnonymousStructOrUnion - Handle the declaration of an
+/// BuildAnonymousStructOrUnion - 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.
@@ -2020,7 +2034,8 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
   llvm::SmallVector<NamedDecl*, 2> Chain;
   Chain.push_back(Anon);
 
-  if (InjectAnonymousStructOrUnionMembers(*this, S, Owner, Record, AS, Chain))
+  if (InjectAnonymousStructOrUnionMembers(*this, S, Owner, Record, AS,
+                                          Chain, false))
     Invalid = true;
 
   // Mark this as an anonymous struct/union type. Note that we do not
@@ -2037,6 +2052,57 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
   return Anon;
 }
 
+/// BuildMicrosoftCAnonymousStruct - Handle the declaration of an
+/// Microsoft C anonymous structure.
+/// Ref: http://msdn.microsoft.com/en-us/library/z2cx9y4f.aspx
+/// Example:
+///
+/// struct A { int a; };
+/// struct B { struct A; int b; };
+///
+/// void foo() {
+///   B var;
+///   var.a = 3; 
+/// }
+///
+Decl *Sema::BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS,
+                                           RecordDecl *Record) {
+  
+  // If there is no Record, get the record via the typedef.
+  if (!Record)
+    Record = DS.getRepAsType().get()->getAsStructureType()->getDecl();
+
+  // Mock up a declarator.
+  Declarator Dc(DS, Declarator::TypeNameContext);
+  TypeSourceInfo *TInfo = GetTypeForDeclarator(Dc, S);
+  assert(TInfo && "couldn't build declarator info for anonymous struct");
+
+  // Create a declaration for this anonymous struct.
+  NamedDecl* Anon = FieldDecl::Create(Context,
+                             cast<RecordDecl>(CurContext),
+                             DS.getSourceRange().getBegin(),
+                             /*IdentifierInfo=*/0,
+                             Context.getTypeDeclType(Record),
+                             TInfo,
+                             /*BitWidth=*/0, /*Mutable=*/false);
+  Anon->setImplicit();
+
+  // Add the anonymous struct object to the current context.
+  CurContext->addDecl(Anon);
+
+  // Inject the members of the anonymous struct into the current
+  // context and into the identifier resolver chain for name lookup
+  // purposes.
+  llvm::SmallVector<NamedDecl*, 2> Chain;
+  Chain.push_back(Anon);
+
+  if (InjectAnonymousStructOrUnionMembers(*this, S, CurContext,
+                                          Record->getDefinition(),
+                                          AS_none, Chain, true))
+    Anon->setInvalidDecl();
+
+  return Anon;
+}
 
 /// GetNameForDeclarator - Determine the full declaration name for the
 /// given Declarator.
diff --git a/test/CodeGen/ms-anonymous-struct.c b/test/CodeGen/ms-anonymous-struct.c
new file mode 100644 (file)
index 0000000..280a40c
--- /dev/null
@@ -0,0 +1,99 @@
+// RUN: %clang_cc1 -fms-extensions -emit-llvm -o - %s | FileCheck %s\r
+\r
+// CHECK: %struct.nested1 = type { i32, i32 }\r
+typedef struct nested1 {\r
+    long a1;\r
+    long b1;\r
+} NESTED1;\r
+\r
+// CHECK: %struct.nested2 = type { i32, %struct.nested1, i32 }\r
+struct nested2 {\r
+    long a;\r
+    NESTED1; \r
+    long b;\r
+};\r
+\r
+// CHECK: %struct.test = type { i32, %struct.nested2, i32 }\r
+struct test {\r
+    int    x;\r
+    struct nested2; \r
+    int    y;\r
+};\r
+\r
+\r
+void foo()\r
+{\r
+  // CHECK: %var = alloca %struct.test, align 4\r
+  struct test var;\r
+\r
+  // CHECK: getelementptr inbounds %struct.test* %var, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 0\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var.a;\r
+\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 2\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var.b;\r
+\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 0\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var.a1;\r
+\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}var, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var.b1;\r
+\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 0\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var.x;\r
+\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 2\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var.y;\r
+}\r
+\r
+void foo2(struct test* var)\r
+{\r
+  // CHECK: alloca %struct.test*, align 4\r
+  // CHECK-NEXT: store %struct.test* %var, %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: load %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 0\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var->a;\r
+\r
+  // CHECK-NEXT: load %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 2\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var->b;\r
+\r
+  // CHECK-NEXT: load %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 0\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var->a1;\r
+\r
+  // CHECK-NEXT: load %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 1\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var->b1;\r
+\r
+  // CHECK-NEXT: load %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 0\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var->x;\r
+\r
+  // CHECK-NEXT: load %struct.test** %{{.*}}, align 4\r
+  // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 2\r
+  // CHECK-NEXT: load i32* %{{.*}}, align 4\r
+  var->y;\r
+}\r
index 47071a383d6d0100b6f15172bde45ec14394207d..0ef72855684d9d7789b57606e861159d5604b5a1 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -fsyntax-only -Wmicrosoft -verify -fms-extensions
+// RUN: %clang_cc1 %s -fsyntax-only -Wno-unused-value -Wmicrosoft -verify -fms-extensions
 
 
 struct A
@@ -27,7 +27,43 @@ enum ENUM1* var2 = 0;
 
 
 enum ENUM2 {
-       ENUM2_a = (enum ENUM2) 4,
-       ENUM2_b = 0x9FFFFFFF, // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
-       ENUM2_c = 0x100000000 // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
+  ENUM2_a = (enum ENUM2) 4,
+  ENUM2_b = 0x9FFFFFFF, // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
+  ENUM2_c = 0x100000000 // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
 };
+
+
+
+
+typedef struct notnested {
+  long bad1;
+  long bad2;
+} NOTNESTED;
+
+
+typedef struct nested1 {
+  long a;
+  struct notnested var1;
+  NOTNESTED var2;
+} NESTED1;
+
+struct nested2 {
+  long b;
+  NESTED1;  // expected-warning {{anonymous structs are a Microsoft extension}}
+};
+
+struct test {
+  int c;
+  struct nested2;   // expected-warning {{anonymous structs are a Microsoft extension}}
+};
+
+void foo()
+{
+  struct test var;
+  var.a;
+  var.b;
+  var.c;
+  var.bad1;   // expected-error {{no member named 'bad1' in 'struct test'}}
+  var.bad2;   // expected-error {{no member named 'bad2' in 'struct test'}}
+}
+