r270457 - [MS ABI] Implement __declspec(empty_bases) and __declspec(layout_version)
David Majnemer via cfe-commits
cfe-commits at lists.llvm.org
Mon May 23 10:36:14 PDT 2016
On Mon, May 23, 2016 at 10:28 AM, Aaron Ballman <aaron at aaronballman.com>
wrote:
> On Mon, May 23, 2016 at 1:16 PM, David Majnemer via cfe-commits
> <cfe-commits at 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.
>
> > +}
> > +
> > 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.
>
> > +
> > + 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.
>
> > 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.
>
> ~Aaron
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160523/97ba35d2/attachment-0001.html>
More information about the cfe-commits
mailing list