From: Francois Pichet Date: Tue, 23 Nov 2010 06:07:27 +0000 (+0000) Subject: Microsoft C anonymous struct implementation. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8e161ed8e63bd9d3783a987d837b72b2b96c4512;p=clang Microsoft C anonymous struct implementation. 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 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 198f0e1639..fd049e37d9 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; // C++ local classes def err_reference_to_local_var_in_enclosing_function : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4769722159..a329933133 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 2b970a34c4..a3b8b8ec30 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(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 &Chaining) { + llvm::SmallVector &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(*D) || isa(*D)) && + cast(*D)->getDeclName()) { + ValueDecl *VD = cast(*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(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()) { - 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 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(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 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 index 0000000000..280a40cca9 --- /dev/null +++ b/test/CodeGen/ms-anonymous-struct.c @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -fms-extensions -emit-llvm -o - %s | FileCheck %s + +// CHECK: %struct.nested1 = type { i32, i32 } +typedef struct nested1 { + long a1; + long b1; +} NESTED1; + +// CHECK: %struct.nested2 = type { i32, %struct.nested1, i32 } +struct nested2 { + long a; + NESTED1; + long b; +}; + +// CHECK: %struct.test = type { i32, %struct.nested2, i32 } +struct test { + int x; + struct nested2; + int y; +}; + + +void foo() +{ + // CHECK: %var = alloca %struct.test, align 4 + struct test var; + + // CHECK: getelementptr inbounds %struct.test* %var, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 0 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var.a; + + // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 2 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var.b; + + // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 0 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var.a1; + + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}var, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var.b1; + + // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 0 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var.x; + + // CHECK-NEXT: getelementptr inbounds %struct.test* %var, i32 0, i32 2 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var.y; +} + +void foo2(struct test* var) +{ + // CHECK: alloca %struct.test*, align 4 + // CHECK-NEXT: store %struct.test* %var, %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: load %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 0 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var->a; + + // CHECK-NEXT: load %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 2 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var->b; + + // CHECK-NEXT: load %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 0 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var->a1; + + // CHECK-NEXT: load %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested2* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: getelementptr inbounds %struct.nested1* %{{.*}}, i32 0, i32 1 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var->b1; + + // CHECK-NEXT: load %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 0 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var->x; + + // CHECK-NEXT: load %struct.test** %{{.*}}, align 4 + // CHECK-NEXT: getelementptr inbounds %struct.test* %{{.*}}, i32 0, i32 2 + // CHECK-NEXT: load i32* %{{.*}}, align 4 + var->y; +} diff --git a/test/Sema/MicrosoftExtensions.c b/test/Sema/MicrosoftExtensions.c index 47071a383d..0ef7285568 100644 --- a/test/Sema/MicrosoftExtensions.c +++ b/test/Sema/MicrosoftExtensions.c @@ -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'}} +} +