summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/AST/RecordLayout.h14
-rw-r--r--clang/include/clang/Basic/Attr.td13
-rw-r--r--clang/include/clang/Basic/AttrDocs.td18
-rw-r--r--clang/lib/AST/RecordLayout.cpp4
-rw-r--r--clang/lib/AST/RecordLayoutBuilder.cpp62
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp25
-rw-r--r--clang/test/Layout/ms-x86-declspec-empty_bases.cpp266
-rw-r--r--clang/test/SemaCXX/ms-empty_bases.cpp7
-rw-r--r--clang/test/SemaCXX/ms-layout_version.cpp10
9 files changed, 394 insertions, 25 deletions
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index 667f23520eb..4361f48c205 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -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 {
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8bc5dfcadc7..e90c124cbd7 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -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 = [];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 3432b0e7668..e1c551648d4 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1553,6 +1553,24 @@ manipulating bits of the enumerator when issuing warnings.
}];
}
+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";
diff --git a/clang/lib/AST/RecordLayout.cpp b/clang/lib/AST/RecordLayout.cpp
index b2c244e3790..4cee2e06311 100644
--- a/clang/lib/AST/RecordLayout.cpp
+++ b/clang/lib/AST/RecordLayout.cpp
@@ -58,7 +58,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
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 ASTContext &Ctx,
CXXInfo->VBPtrOffset = vbptroffset;
CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr;
CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr;
- CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject;
+ CXXInfo->EndsWithZeroSizedObject = EndsWithZeroSizedObject;
CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase;
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 6a7d5144c8c..d03273f9175 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -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::getAdjustedElementInfo(
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::getAdjustedElementInfo(
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::layoutNonVirtualBases(const CXXRecordDecl *RD) {
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::layoutNonVirtualBases(const CXXRecordDecl *RD) {
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::layoutNonVirtualBases(const CXXRecordDecl *RD) {
}
}
+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::layoutNonVirtualBase(
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::layoutVirtualBases(const CXXRecordDecl *RD) {
// 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::finalizeLayout(const RecordDecl *RD) {
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 RecordDecl *D) const {
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,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index aeacb4a6fe4..a1a1b9e7b5e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4968,6 +4968,24 @@ static void handleX86ForceAlignArgPointerAttr(Sema &S, Decl *D,
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, Scope *scope, Decl *D,
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, Scope *scope, Decl *D,
case AttributeList::AT_Thread:
handleDeclspecThreadAttr(S, D, Attr);
break;
+
case AttributeList::AT_AbiTag:
handleAbiTagAttr(S, D, Attr);
break;
diff --git a/clang/test/Layout/ms-x86-declspec-empty_bases.cpp b/clang/test/Layout/ms-x86-declspec-empty_bases.cpp
new file mode 100644
index 00000000000..cc13a980cb5
--- /dev/null
+++ b/clang/test/Layout/ms-x86-declspec-empty_bases.cpp
@@ -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]
+}
diff --git a/clang/test/SemaCXX/ms-empty_bases.cpp b/clang/test/SemaCXX/ms-empty_bases.cpp
new file mode 100644
index 00000000000..4452d558711
--- /dev/null
+++ b/clang/test/SemaCXX/ms-empty_bases.cpp
@@ -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}}
diff --git a/clang/test/SemaCXX/ms-layout_version.cpp b/clang/test/SemaCXX/ms-layout_version.cpp
new file mode 100644
index 00000000000..b8a10341f23
--- /dev/null
+++ b/clang/test/SemaCXX/ms-layout_version.cpp
@@ -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}}
OpenPOWER on IntegriCloud