On Mon, May 23, 2016 at 1:36 PM, David Majnemer <david.majne...@gmail.com> wrote: > > > On Mon, May 23, 2016 at 10:28 AM, Aaron Ballman <aa...@aaronballman.com> > wrote: >> >> On Mon, May 23, 2016 at 1:16 PM, David Majnemer via cfe-commits >> <cfe-commits@lists.llvm.org> wrote: >> > Author: majnemer >> > Date: Mon May 23 12:16:12 2016 >> > New Revision: 270457 >> > >> > URL: http://llvm.org/viewvc/llvm-project?rev=270457&view=rev >> > Log: >> > [MS ABI] Implement __declspec(empty_bases) and >> > __declspec(layout_version) >> > >> > The layout_version attribute is pretty straightforward: use the layout >> > rules from version XYZ of MSVC when used like >> > struct __declspec(layout_version(XYZ)) S {}; >> > >> > The empty_bases attribute is more interesting. It tries to get the C++ >> > empty base optimization to fire more often by tweaking the MSVC ABI >> > rules in subtle ways: >> > 1. Disable the leading and trailing zero-sized object flags if a class >> > is marked __declspec(empty_bases) and is empty. >> > >> > This means that given: >> > struct __declspec(empty_bases) A {}; >> > struct __declspec(empty_bases) B {}; >> > struct C : A, B {}; >> > >> > 'C' will have size 1 and nvsize 0 despite not being annotated >> > __declspec(empty_bases). >> > >> > 2. When laying out virtual or non-virtual bases, disable the injection >> > of padding between classes if the most derived class is marked >> > __declspec(empty_bases). >> > >> > This means that given: >> > struct A {}; >> > struct B {}; >> > struct __declspec(empty_bases) C : A, B {}; >> > >> > 'C' will have size 1 and nvsize 0. >> > >> > 3. When calculating the offset of a non-virtual base, choose offset zero >> > if the most derived class is marked __declspec(empty_bases) and the >> > base is empty _and_ has an nvsize of 0. >> > >> > Because of the ABI rules, this does not mean that empty bases >> > reliably get placed at offset 0! >> > >> > For example: >> > struct A {}; >> > struct B {}; >> > struct __declspec(empty_bases) C : A, B { virtual ~C(); }; >> > >> > 'C' will be pointer sized to account for the vfptr at offset 0. >> > 'A' and 'B' will _not_ be at offset 0 despite being empty! >> > Instead, they will be located right after the vfptr. >> > >> > This occurs due to the interaction betweeen non-virtual base layout >> > and virtual function pointer injection: injection occurs after the >> > nv-bases and shifts them down by the size of a pointer. >> > >> > Added: >> > cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp >> > cfe/trunk/test/SemaCXX/ms-empty_bases.cpp >> > cfe/trunk/test/SemaCXX/ms-layout_version.cpp >> > Modified: >> > cfe/trunk/include/clang/AST/RecordLayout.h >> > cfe/trunk/include/clang/Basic/Attr.td >> > cfe/trunk/include/clang/Basic/AttrDocs.td >> > cfe/trunk/lib/AST/RecordLayout.cpp >> > cfe/trunk/lib/AST/RecordLayoutBuilder.cpp >> > cfe/trunk/lib/Sema/SemaDeclAttr.cpp >> > >> > Modified: cfe/trunk/include/clang/AST/RecordLayout.h >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecordLayout.h?rev=270457&r1=270456&r2=270457&view=diff >> > >> > ============================================================================== >> > --- cfe/trunk/include/clang/AST/RecordLayout.h (original) >> > +++ cfe/trunk/include/clang/AST/RecordLayout.h Mon May 23 12:16:12 2016 >> > @@ -104,10 +104,10 @@ private: >> > /// a primary base class. >> > bool HasExtendableVFPtr : 1; >> > >> > - /// HasZeroSizedSubObject - True if this class contains a zero >> > sized member >> > - /// or base or a base with a zero sized member or base. Only used >> > for >> > - /// MS-ABI. >> > - bool HasZeroSizedSubObject : 1; >> > + /// EndsWithZeroSizedObject - True if this class contains a zero >> > sized >> > + /// member or base or a base with a zero sized member or base. >> > + /// Only used for MS-ABI. >> > + bool EndsWithZeroSizedObject : 1; >> > >> > /// \brief True if this class is zero sized or first base is zero >> > sized or >> > /// has this property. Only used for MS-ABI. >> > @@ -154,7 +154,7 @@ private: >> > const CXXRecordDecl *PrimaryBase, >> > bool IsPrimaryBaseVirtual, >> > const CXXRecordDecl *BaseSharingVBPtr, >> > - bool HasZeroSizedSubObject, >> > + bool EndsWithZeroSizedObject, >> > bool LeadsWithZeroSizedBase, >> > const BaseOffsetsMapTy& BaseOffsets, >> > const VBaseOffsetsMapTy& VBaseOffsets); >> > @@ -283,8 +283,8 @@ public: >> > return RequiredAlignment; >> > } >> > >> > - bool hasZeroSizedSubObject() const { >> > - return CXXInfo && CXXInfo->HasZeroSizedSubObject; >> > + bool endsWithZeroSizedObject() const { >> > + return CXXInfo && CXXInfo->EndsWithZeroSizedObject; >> > } >> > >> > bool leadsWithZeroSizedBase() const { >> > >> > Modified: cfe/trunk/include/clang/Basic/Attr.td >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=270457&r1=270456&r2=270457&view=diff >> > >> > ============================================================================== >> > --- cfe/trunk/include/clang/Basic/Attr.td (original) >> > +++ cfe/trunk/include/clang/Basic/Attr.td Mon May 23 12:16:12 2016 >> > @@ -745,6 +745,12 @@ def Destructor : InheritableAttr { >> > let Documentation = [Undocumented]; >> > } >> > >> > +def EmptyBases : InheritableAttr, >> > TargetSpecificAttr<TargetMicrosoftCXXABI> { >> > + let Spellings = [Declspec<"empty_bases">]; >> > + let Subjects = SubjectList<[CXXRecord]>; >> > + let Documentation = [EmptyBasesDocs]; >> > +} >> > + >> > def EnableIf : InheritableAttr { >> > let Spellings = [GNU<"enable_if">]; >> > let Subjects = SubjectList<[Function]>; >> > @@ -868,6 +874,13 @@ def Restrict : InheritableAttr { >> > let Documentation = [Undocumented]; >> > } >> > >> > +def LayoutVersion : InheritableAttr, >> > TargetSpecificAttr<TargetMicrosoftCXXABI> { >> > + let Spellings = [Declspec<"layout_version">]; >> > + let Args = [UnsignedArgument<"Version">]; >> > + let Subjects = SubjectList<[CXXRecord]>; >> > + let Documentation = [LayoutVersionDocs]; >> > +} >> > + >> > def MaxFieldAlignment : InheritableAttr { >> > // This attribute has no spellings as it is only ever created >> > implicitly. >> > let Spellings = []; >> > >> > Modified: cfe/trunk/include/clang/Basic/AttrDocs.td >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=270457&r1=270456&r2=270457&view=diff >> > >> > ============================================================================== >> > --- cfe/trunk/include/clang/Basic/AttrDocs.td (original) >> > +++ cfe/trunk/include/clang/Basic/AttrDocs.td Mon May 23 12:16:12 2016 >> > @@ -1553,6 +1553,24 @@ manipulating bits of the enumerator when >> > }]; >> > } >> > >> > +def EmptyBasesDocs : Documentation { >> > + let Category = DocCatType; >> > + let Content = [{ >> > +The empty_bases attribute permits the compiler to utilize the >> > +empty-base-optimization more frequently. >> > +It is only supported when using the Microsoft C++ ABI. >> > + }]; >> > +} >> > + >> > +def LayoutVersionDocs : Documentation { >> > + let Category = DocCatType; >> > + let Content = [{ >> > +The layout_version attribute requests that the compiler utilize the >> > class >> > +layout rules of a particular compiler version. >> > +It is only supported when using the Microsoft C++ ABI. >> > + }]; >> >> Can you link to MSDN documentation for these two attributes to provide >> a bit more information? If not, it would be good to point out that >> both of these only apply to struct, class, and union types, and any >> other constraints that may be of interest to the user. > > > Microsoft hasn't updated the list of documented declspecs for quite some > time. I'll amend our documentation.
Thank you! > >> >> >> > +} >> > + >> > def MSInheritanceDocs : Documentation { >> > let Category = DocCatType; >> > let Heading = "__single_inhertiance, __multiple_inheritance, >> > __virtual_inheritance"; >> > >> > Modified: cfe/trunk/lib/AST/RecordLayout.cpp >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RecordLayout.cpp?rev=270457&r1=270456&r2=270457&view=diff >> > >> > ============================================================================== >> > --- cfe/trunk/lib/AST/RecordLayout.cpp (original) >> > +++ cfe/trunk/lib/AST/RecordLayout.cpp Mon May 23 12:16:12 2016 >> > @@ -58,7 +58,7 @@ ASTRecordLayout::ASTRecordLayout(const A >> > const CXXRecordDecl *PrimaryBase, >> > bool IsPrimaryBaseVirtual, >> > const CXXRecordDecl *BaseSharingVBPtr, >> > - bool HasZeroSizedSubObject, >> > + bool EndsWithZeroSizedObject, >> > bool LeadsWithZeroSizedBase, >> > const BaseOffsetsMapTy& BaseOffsets, >> > const VBaseOffsetsMapTy& VBaseOffsets) >> > @@ -82,7 +82,7 @@ ASTRecordLayout::ASTRecordLayout(const A >> > CXXInfo->VBPtrOffset = vbptroffset; >> > CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr; >> > CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr; >> > - CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject; >> > + CXXInfo->EndsWithZeroSizedObject = EndsWithZeroSizedObject; >> > CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase; >> > >> > >> > >> > Modified: cfe/trunk/lib/AST/RecordLayoutBuilder.cpp >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RecordLayoutBuilder.cpp?rev=270457&r1=270456&r2=270457&view=diff >> > >> > ============================================================================== >> > --- cfe/trunk/lib/AST/RecordLayoutBuilder.cpp (original) >> > +++ cfe/trunk/lib/AST/RecordLayoutBuilder.cpp Mon May 23 12:16:12 2016 >> > @@ -2228,7 +2228,8 @@ public: >> > /// laid out. >> > void initializeCXXLayout(const CXXRecordDecl *RD); >> > void layoutNonVirtualBases(const CXXRecordDecl *RD); >> > - void layoutNonVirtualBase(const CXXRecordDecl *BaseDecl, >> > + void layoutNonVirtualBase(const CXXRecordDecl *RD, >> > + const CXXRecordDecl *BaseDecl, >> > const ASTRecordLayout &BaseLayout, >> > const ASTRecordLayout >> > *&PreviousBaseLayout); >> > void injectVFPtr(const CXXRecordDecl *RD); >> > @@ -2334,7 +2335,7 @@ MicrosoftRecordLayoutBuilder::getAdjuste >> > if (!MaxFieldAlignment.isZero()) >> > Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment); >> > // Track zero-sized subobjects here where it's already available. >> > - EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject(); >> > + EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); >> > // Respect required alignment, this is necessary because we may have >> > adjusted >> > // the alignment in the case of pragam pack. Note that the required >> > alignment >> > // doesn't actually apply to the struct alignment at this point. >> > @@ -2369,7 +2370,7 @@ MicrosoftRecordLayoutBuilder::getAdjuste >> > if (auto RT = >> > >> > FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) { >> > auto const &Layout = Context.getASTRecordLayout(RT->getDecl()); >> > - EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject(); >> > + EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject(); >> > FieldRequiredAlignment = std::max(FieldRequiredAlignment, >> > Layout.getRequiredAlignment()); >> > } >> > @@ -2502,7 +2503,7 @@ MicrosoftRecordLayoutBuilder::layoutNonV >> > LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase(); >> > } >> > // Lay out the base. >> > - layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout); >> > + layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout); >> > } >> > // Figure out if we need a fresh VFPtr for this class. >> > if (!PrimaryBase && RD->isDynamicClass()) >> > @@ -2531,7 +2532,7 @@ MicrosoftRecordLayoutBuilder::layoutNonV >> > LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase(); >> > } >> > // Lay out the base. >> > - layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout); >> > + layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout); >> > VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize(); >> > } >> > // Set our VBPtroffset if we know it at this point. >> > @@ -2543,15 +2544,32 @@ MicrosoftRecordLayoutBuilder::layoutNonV >> > } >> > } >> > >> > +static bool recordUsesEBO(const RecordDecl *RD) { >> > + if (!isa<CXXRecordDecl>(RD)) >> > + return false; >> > + if (RD->hasAttr<EmptyBasesAttr>()) >> > + return true; >> > + if (auto *LVA = RD->getAttr<LayoutVersionAttr>()) >> > + // TODO: Double check with the next version of MSVC. >> > + if (LVA->getVersion() <= LangOptions::MSVC2015) >> > + return false; >> > + // TODO: Some later version of MSVC will change the default behavior >> > of the >> > + // compiler to enable EBO by default. When this happens, we will >> > need an >> > + // additional isCompatibleWithMSVC check. >> > + return false; >> > +} >> > + >> > void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase( >> > + const CXXRecordDecl *RD, >> > const CXXRecordDecl *BaseDecl, >> > const ASTRecordLayout &BaseLayout, >> > const ASTRecordLayout *&PreviousBaseLayout) { >> > // Insert padding between two bases if the left first one is zero >> > sized or >> > // contains a zero sized subobject and the right is zero sized or one >> > leads >> > // with a zero sized base. >> > - if (PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() >> > && >> > - BaseLayout.leadsWithZeroSizedBase()) >> > + bool MDCUsesEBO = recordUsesEBO(RD); >> > + if (PreviousBaseLayout && >> > PreviousBaseLayout->endsWithZeroSizedObject() && >> > + BaseLayout.leadsWithZeroSizedBase() && !MDCUsesEBO) >> > Size++; >> > ElementInfo Info = getAdjustedElementInfo(BaseLayout); >> > CharUnits BaseOffset; >> > @@ -2560,14 +2578,23 @@ void MicrosoftRecordLayoutBuilder::layou >> > bool FoundBase = false; >> > if (UseExternalLayout) { >> > FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset); >> > - if (FoundBase) >> > + if (FoundBase) { >> > assert(BaseOffset >= Size && "base offset already allocated"); >> > + Size = BaseOffset; >> > + } >> > } >> > >> > - if (!FoundBase) >> > - BaseOffset = Size.alignTo(Info.Alignment); >> > + if (!FoundBase) { >> > + if (MDCUsesEBO && BaseDecl->isEmpty() && >> > + BaseLayout.getNonVirtualSize() == CharUnits::Zero()) { >> > + BaseOffset = CharUnits::Zero(); >> > + } else { >> > + // Otherwise, lay the base out at the end of the MDC. >> > + BaseOffset = Size = Size.alignTo(Info.Alignment); >> > + } >> > + } >> > Bases.insert(std::make_pair(BaseDecl, BaseOffset)); >> > - Size = BaseOffset + BaseLayout.getNonVirtualSize(); >> > + Size += BaseLayout.getNonVirtualSize(); >> > PreviousBaseLayout = &BaseLayout; >> > } >> > >> > @@ -2746,8 +2773,9 @@ void MicrosoftRecordLayoutBuilder::layou >> > // with a zero sized base. The padding between virtual bases is 4 >> > // bytes (in both 32 and 64 bits modes) and always involves >> > rounding up to >> > // the required alignment, we don't know why. >> > - if ((PreviousBaseLayout && >> > PreviousBaseLayout->hasZeroSizedSubObject() && >> > - BaseLayout.leadsWithZeroSizedBase()) || HasVtordisp) { >> > + if ((PreviousBaseLayout && >> > PreviousBaseLayout->endsWithZeroSizedObject() && >> > + BaseLayout.leadsWithZeroSizedBase() && !recordUsesEBO(RD)) || >> > + HasVtordisp) { >> > Size = Size.alignTo(VtorDispAlignment) + VtorDispSize; >> > Alignment = std::max(VtorDispAlignment, Alignment); >> > } >> > @@ -2785,8 +2813,10 @@ void MicrosoftRecordLayoutBuilder::final >> > Size = Size.alignTo(RoundingAlignment); >> > } >> > if (Size.isZero()) { >> > - EndsWithZeroSizedObject = true; >> > - LeadsWithZeroSizedBase = true; >> > + if (!recordUsesEBO(RD) || !cast<CXXRecordDecl>(RD)->isEmpty()) { >> > + EndsWithZeroSizedObject = true; >> > + LeadsWithZeroSizedBase = true; >> > + } >> > // Zero-sized structures have size equal to their alignment if a >> > // __declspec(align) came into play. >> > if (RequiredAlignment >= MinEmptyStructSize) >> > @@ -2919,7 +2949,7 @@ ASTContext::getASTRecordLayout(const Rec >> > NewEntry = new (*this) ASTRecordLayout( >> > *this, Builder.Size, Builder.Alignment, >> > Builder.RequiredAlignment, >> > Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || >> > Builder.PrimaryBase, >> > - Builder.VBPtrOffset, Builder.NonVirtualSize, >> > + Builder.VBPtrOffset, Builder.DataSize, >> > Builder.FieldOffsets.data(), Builder.FieldOffsets.size(), >> > Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(), >> > Builder.PrimaryBase, false, Builder.SharedVBPtrBase, >> > >> > Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=270457&r1=270456&r2=270457&view=diff >> > >> > ============================================================================== >> > --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original) >> > +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon May 23 12:16:12 2016 >> > @@ -4968,6 +4968,24 @@ static void handleX86ForceAlignArgPointe >> > >> > Attr.getAttributeSpellingListIndex())); >> > } >> > >> > +static void handleLayoutVersion(Sema &S, Decl *D, const AttributeList >> > &Attr) { >> > + uint32_t Version; >> > + Expr *VersionExpr = static_cast<Expr *>(Attr.getArgAsExpr(0)); >> > + if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), Version)) >> > + return; >> > + >> > + // TODO: Investigate what happens with the next major version of >> > MSVC. >> > + if (Version != LangOptions::MSVC2015) { >> > + S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) >> > + << Attr.getName() << Version << VersionExpr->getSourceRange(); >> > + return; >> > + } >> >> So this cannot accept minor version numbers? (Presumably because minor >> and patch versions should not be changing layouts, I suppose.) > > > It cannot. Makes sense, thank you. > >> >> >> > + >> > + D->addAttr(::new (S.Context) >> > + LayoutVersionAttr(Attr.getRange(), S.Context, Version, >> > + >> > Attr.getAttributeSpellingListIndex())); >> > +} >> > + >> > DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range, >> > unsigned AttrSpellingListIndex) >> > { >> > if (D->hasAttr<DLLExportAttr>()) { >> > @@ -5749,6 +5767,12 @@ static void ProcessDeclAttribute(Sema &S >> > break; >> > >> > // Microsoft attributes: >> > + case AttributeList::AT_EmptyBases: >> > + handleSimpleAttribute<EmptyBasesAttr>(S, D, Attr); >> > + break; >> > + case AttributeList::AT_LayoutVersion: >> > + handleLayoutVersion(S, D, Attr); >> > + break; >> > case AttributeList::AT_MSNoVTable: >> > handleSimpleAttribute<MSNoVTableAttr>(S, D, Attr); >> > break; >> > @@ -5767,6 +5791,7 @@ static void ProcessDeclAttribute(Sema &S >> > case AttributeList::AT_Thread: >> > handleDeclspecThreadAttr(S, D, Attr); >> > break; >> > + >> >> Spurious newline added. > > > Spurious newline intended. the abi_tag attribute is most certainly _not_ a > Microsoft attribute. Ah, drive-by newline, I like it. > >> >> >> > case AttributeList::AT_AbiTag: >> > handleAbiTagAttr(S, D, Attr); >> > break; >> > >> > Added: cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp?rev=270457&view=auto >> > >> > ============================================================================== >> > --- cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp (added) >> > +++ cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp Mon May 23 >> > 12:16:12 2016 >> > @@ -0,0 +1,266 @@ >> > +// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32 >> > -fms-extensions -fdump-record-layouts -fsyntax-only %s 2>/dev/null \ >> > +// RUN: | FileCheck %s >> > +// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple x86_64-pc-win32 >> > -fms-extensions -fdump-record-layouts -fsyntax-only %s 2>/dev/null \ >> > +// RUN: | FileCheck %s >> > + >> > +namespace test1 { >> > + >> > +struct A { >> > + int a; >> > +}; >> > +struct B { >> > + int b; >> > +}; >> > +struct C {}; >> > +struct __declspec(align(16)) D {}; >> > +struct __declspec(empty_bases) X : A, D, B, C { >> > +}; >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test1::A >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test1::D (empty) >> > +// CHECK-NEXT: | [sizeof=16, align=16, >> > +// CHECK-NEXT: | nvsize=0, nvalign=16] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test1::B >> > +// CHECK-NEXT: 0 | int b >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test1::C (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test1::X >> > +// CHECK-NEXT: 0 | struct test1::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 0 | struct test1::D (base) (empty) >> > +// CHECK-NEXT: 0 | struct test1::C (base) (empty) >> > +// CHECK-NEXT: 4 | struct test1::B (base) >> > +// CHECK-NEXT: 4 | int b >> > +// CHECK-NEXT: | [sizeof=16, align=16, >> > +// CHECK-NEXT: | nvsize=16, nvalign=16] >> > + >> > +int _ = sizeof(X); >> > +} >> > + >> > +namespace test2 { >> > +struct A { >> > + int a; >> > +}; >> > +struct __declspec(empty_bases) B {}; >> > +struct C : A { >> > + B b; >> > +}; >> > + >> > +struct D {}; >> > +struct E { >> > + int e; >> > +}; >> > +struct F : D, E {}; >> > + >> > +struct G : C, F {}; >> > + >> > +int _ = sizeof(G); >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::A >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::B (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::C >> > +// CHECK-NEXT: 0 | struct test2::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 4 | struct test2::B b (empty) >> > +// CHECK-NEXT: | [sizeof=8, align=4, >> > +// CHECK-NEXT: | nvsize=8, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::D (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::E >> > +// CHECK-NEXT: 0 | int e >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::F >> > +// CHECK-NEXT: 0 | struct test2::D (base) (empty) >> > +// CHECK-NEXT: 0 | struct test2::E (base) >> > +// CHECK-NEXT: 0 | int e >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test2::G >> > +// CHECK-NEXT: 0 | struct test2::C (base) >> > +// CHECK-NEXT: 0 | struct test2::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 4 | struct test2::B b (empty) >> > +// CHECK-NEXT: 8 | struct test2::F (base) >> > +// CHECK-NEXT: 8 | struct test2::D (base) (empty) >> > +// CHECK-NEXT: 8 | struct test2::E (base) >> > +// CHECK-NEXT: 8 | int e >> > +// CHECK-NEXT: | [sizeof=12, align=4, >> > +// CHECK-NEXT: | nvsize=12, nvalign=4] >> > +} >> > + >> > +namespace test3 { >> > +struct A { >> > + int a; >> > +}; >> > +struct B {}; >> > +struct C : A { >> > + B b; >> > +}; >> > + >> > +struct D {}; >> > +struct E { >> > + int e; >> > +}; >> > +struct F : D, E {}; >> > + >> > +struct __declspec(empty_bases) G : C, F {}; >> > + >> > +int _ = sizeof(G); >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::A >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::B (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::C >> > +// CHECK-NEXT: 0 | struct test3::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 4 | struct test3::B b (empty) >> > +// CHECK-NEXT: | [sizeof=8, align=4, >> > +// CHECK-NEXT: | nvsize=8, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::D (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::E >> > +// CHECK-NEXT: 0 | int e >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::F >> > +// CHECK-NEXT: 0 | struct test3::D (base) (empty) >> > +// CHECK-NEXT: 0 | struct test3::E (base) >> > +// CHECK-NEXT: 0 | int e >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test3::G >> > +// CHECK-NEXT: 0 | struct test3::C (base) >> > +// CHECK-NEXT: 0 | struct test3::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 4 | struct test3::B b (empty) >> > +// CHECK-NEXT: 8 | struct test3::F (base) >> > +// CHECK-NEXT: 8 | struct test3::D (base) (empty) >> > +// CHECK-NEXT: 8 | struct test3::E (base) >> > +// CHECK-NEXT: 8 | int e >> > +// CHECK-NEXT: | [sizeof=12, align=4, >> > +// CHECK-NEXT: | nvsize=12, nvalign=4] >> > +} >> > + >> > +namespace test4 { >> > +struct A { >> > + int a; >> > +}; >> > +struct B {}; >> > +struct C : A { >> > + B b; >> > +}; >> > + >> > +struct __declspec(empty_bases) D {}; >> > +struct E { >> > + int e; >> > +}; >> > +struct F : D, E {}; >> > + >> > +struct G : C, F {}; >> > + >> > +int _ = sizeof(G); >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::A >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::B (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::C >> > +// CHECK-NEXT: 0 | struct test4::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 4 | struct test4::B b (empty) >> > +// CHECK-NEXT: | [sizeof=8, align=4, >> > +// CHECK-NEXT: | nvsize=8, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::D (empty) >> > +// CHECK-NEXT: | [sizeof=1, align=1, >> > +// CHECK-NEXT: | nvsize=0, nvalign=1] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::E >> > +// CHECK-NEXT: 0 | int e >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::F >> > +// CHECK-NEXT: 0 | struct test4::D (base) (empty) >> > +// CHECK-NEXT: 0 | struct test4::E (base) >> > +// CHECK-NEXT: 0 | int e >> > +// CHECK-NEXT: | [sizeof=4, align=4, >> > +// CHECK-NEXT: | nvsize=4, nvalign=4] >> > + >> > +// CHECK: *** Dumping AST Record Layout >> > +// CHECK-NEXT: 0 | struct test4::G >> > +// CHECK-NEXT: 0 | struct test4::C (base) >> > +// CHECK-NEXT: 0 | struct test4::A (base) >> > +// CHECK-NEXT: 0 | int a >> > +// CHECK-NEXT: 4 | struct test4::B b (empty) >> > +// CHECK-NEXT: 8 | struct test4::F (base) >> > +// CHECK-NEXT: 8 | struct test4::D (base) (empty) >> > +// CHECK-NEXT: 8 | struct test4::E (base) >> > +// CHECK-NEXT: 8 | int e >> > +// CHECK-NEXT: | [sizeof=12, align=4, >> > +// CHECK-NEXT: | nvsize=12, nvalign=4] >> > +} >> > >> > Added: cfe/trunk/test/SemaCXX/ms-empty_bases.cpp >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/ms-empty_bases.cpp?rev=270457&view=auto >> > >> > ============================================================================== >> > --- cfe/trunk/test/SemaCXX/ms-empty_bases.cpp (added) >> > +++ cfe/trunk/test/SemaCXX/ms-empty_bases.cpp Mon May 23 12:16:12 2016 >> > @@ -0,0 +1,7 @@ >> > +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify >> > -fms-extensions -Wno-microsoft -std=c++11 >> > + >> > +struct __declspec(empty_bases) S {}; >> > +enum __declspec(empty_bases) E {}; // expected-warning{{'empty_bases' >> > attribute only applies to classes}} >> > +int __declspec(empty_bases) I; // expected-warning{{'empty_bases' >> > attribute only applies to classes}} >> > +typedef struct T __declspec(empty_bases) U; // >> > expected-warning{{'empty_bases' attribute only applies to classes}} >> > +auto z = []() __declspec(empty_bases) { return nullptr; }; // >> > expected-warning{{'empty_bases' attribute only applies to classes}} >> >> Missing a test that ensures the attribute accepts no arguments. > > > Sure, I'll add one. > >> >> >> > >> > Added: cfe/trunk/test/SemaCXX/ms-layout_version.cpp >> > URL: >> > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/ms-layout_version.cpp?rev=270457&view=auto >> > >> > ============================================================================== >> > --- cfe/trunk/test/SemaCXX/ms-layout_version.cpp (added) >> > +++ cfe/trunk/test/SemaCXX/ms-layout_version.cpp Mon May 23 12:16:12 >> > 2016 >> > @@ -0,0 +1,10 @@ >> > +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify >> > -fms-extensions -Wno-microsoft -std=c++11 >> > + >> > +struct __declspec(layout_version(19)) S {}; >> > +enum __declspec(layout_version(19)) E {}; // >> > expected-warning{{'layout_version' attribute only applies to classes}} >> > +int __declspec(layout_version(19)) I; // >> > expected-warning{{'layout_version' attribute only applies to classes}} >> > +typedef struct T __declspec(layout_version(19)) U; // >> > expected-warning{{'layout_version' attribute only applies to classes}} >> > +auto z = []() __declspec(layout_version(19)) { return nullptr; }; // >> > expected-warning{{'layout_version' attribute only applies to classes}} >> > + >> > +struct __declspec(layout_version(18)) X {}; // >> > expected-error{{'layout_version' attribute parameter 18 is out of bounds}} >> > +struct __declspec(layout_version(20)) Y {}; // >> > expected-error{{'layout_version' attribute parameter 20 is out of bounds}} >> >> Missing a test that ensures the attribute still fails when given no >> arguments. > > > Sure, I'll add one. Thanks! _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits