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:16:14 PDT 2016


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.
+  }];
+}
+
 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;
+  }
+
+  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;
+
   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}}

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}}




More information about the cfe-commits mailing list