(hasDefinition() && isPolymorphic());
}
+ /// \brief Controls when vtordisps will be emitted if this record is used as a
+ /// virtual base.
+ MSVtorDispAttr::Mode getMSVtorDispMode() const;
+
/// \brief Determine whether this lambda expression was known to be dependent
/// at the time it was created, even if its context does not appear to be
/// dependent.
}];
}
+def MSVtorDisp : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly.
+ let Spellings = [];
+ let Args = [UnsignedArgument<"vdm">];
+ let SemaHandler = 0;
+
+ let AdditionalMembers = [{
+ enum Mode {
+ Never,
+ ForVBaseOverride,
+ ForVFTable
+ };
+
+ Mode getVtorDispMode() const { return Mode(vdm); }
+ }];
+}
+
def Unaligned : IgnoredAttr {
let Spellings = [Keyword<"__unaligned">];
}
"missing ')' after '#pragma %0' - ignoring">, InGroup<IgnoredPragmas>;
def warn_pragma_expected_identifier : Warning<
"expected identifier in '#pragma %0' - ignored">, InGroup<IgnoredPragmas>;
+def warn_pragma_expected_integer : Warning<
+ "expected integer between %0 and %1 inclusive in '#pragma %2' - ignored">,
+ InGroup<IgnoredPragmas>;
def warn_pragma_ms_struct : Warning<
"incorrect use of '#pragma ms_struct on|off' - ignored">,
InGroup<IgnoredPragmas>;
"invalid alignment option in '#pragma %select{align|options align}0' - ignored">,
InGroup<IgnoredPragmas>;
// - #pragma pack
-def warn_pragma_pack_invalid_action : Warning<
- "unknown action for '#pragma pack' - ignored">,
+def warn_pragma_invalid_action : Warning<
+ "unknown action for '#pragma %0' - ignored">,
InGroup<IgnoredPragmas>;
def warn_pragma_pack_malformed : Warning<
"expected integer or identifier in '#pragma pack' - ignored">,
def warn_pragma_pack_show : Warning<"value of #pragma pack(show) == %0">;
def warn_pragma_pack_pop_identifer_and_alignment : Warning<
"specifying both a name and alignment to 'pop' is undefined">;
-def warn_pragma_pack_pop_failed : Warning<"#pragma pack(pop, ...) failed: %0">,
+def warn_pragma_pop_failed : Warning<"#pragma %0(pop, ...) failed: %1">,
InGroup<IgnoredPragmas>;
def warn_pragma_ms_struct_failed : Warning<"#pramga ms_struct can not be used with dynamic classes or structures">,
InGroup<IgnoredPragmas>;
"if non-zero, warn about parameter or return Warn if parameter/return value is larger in bytes than this setting. 0 is no check.")
VALUE_LANGOPT(MSCVersion, 32, 0,
"version of Microsoft Visual C/C++")
+VALUE_LANGOPT(VtorDispMode, 2, 1, "How many vtordisps to insert")
LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling")
// handles them.
ANNOTATION(pragma_ms_pointers_to_members)
+// Annotation for #pragma vtordisp...
+// The lexer produces these so that they only take effect when the parser
+// handles them.
+ANNOTATION(pragma_ms_vtordisp)
+
// Annotation for #pragma OPENCL EXTENSION...
// The lexer produces these so that they only take effect when the parser
// handles them.
HelpText<"Enable C++1y sized global deallocation functions">;
def fobjc_subscripting_legacy_runtime : Flag<["-"], "fobjc-subscripting-legacy-runtime">,
HelpText<"Allow Objective-C array and dictionary subscripting in legacy runtime">;
+def vtordisp_mode_EQ : Joined<["-"], "vtordisp-mode=">,
+ HelpText<"Control vtordisp placement on win32 targets">;
//===----------------------------------------------------------------------===//
// Header Search Options
def _SLASH_WX_ : CLFlag<"WX-">, HelpText<"Do not treat warnings as errors">,
Alias<W_Joined>, AliasArgs<["no-error"]>;
def _SLASH_w_flag : CLFlag<"w">, HelpText<"Disable all warnings">, Alias<w>;
+def _SLASH_vd : CLJoined<"vd">, HelpText<"Control vtordisp placement">,
+ Alias<vtordisp_mode_EQ>;
def _SLASH_Z7 : CLFlag<"Z7">, Alias<gline_tables_only>;
def _SLASH_Zi : CLFlag<"Zi">, HelpText<"Enable debug information">,
Alias<gline_tables_only>;
def _SLASH_Qvec_report : CLJoined<"Qvec-report">;
def _SLASH_u : CLFlag<"u">;
def _SLASH_V : CLFlag<"V">;
-def _SLASH_vd : CLJoined<"vd">;
def _SLASH_volatile : CLFlag<"volatile">;
def _SLASH_WL : CLFlag<"WL">;
def _SLASH_Wp64 : CLFlag<"Wp64">;
while (Result.getKind() == tok::comment);
}
+ /// \brief Parses a simple integer literal to get its numeric value. Floating
+ /// point literals and user defined literals are rejected. Used primarily to
+ /// handle pragmas that accept integer arguments.
+ bool parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value);
+
/// Disables macro expansion everywhere except for preprocessor directives.
void SetMacroExpansionOnlyInDirectives() {
DisableMacroExpansion = true;
OwningPtr<PragmaHandler> MSCommentHandler;
OwningPtr<PragmaHandler> MSDetectMismatchHandler;
OwningPtr<PragmaHandler> MSPointersToMembers;
+ OwningPtr<PragmaHandler> MSVtorDisp;
/// Whether the '>' token acts as an operator or not. This will be
/// true except when we are parsing an expression within a C++
void HandlePragmaMSPointersToMembers();
+ void HandlePragmaMSVtorDisp();
+
/// \brief Handle the annotation token produced for
/// #pragma align...
void HandlePragmaAlign();
LangOptions::PragmaMSPointersToMembersKind
MSPointerToMemberRepresentationMethod;
+ enum PragmaVtorDispKind {
+ PVDK_Push, //< #pragma vtordisp(push, mode)
+ PVDK_Set, //< #pragma vtordisp(mode)
+ PVDK_Pop, //< #pragma vtordisp(pop)
+ PVDK_Reset //< #pragma vtordisp()
+ };
+
+ /// \brief Whether to insert vtordisps prior to virtual bases in the Microsoft
+ /// C++ ABI. Possible values are 0, 1, and 2, which mean:
+ ///
+ /// 0: Suppress all vtordisps
+ /// 1: Insert vtordisps in the presence of vbase overrides and non-trivial
+ /// structors
+ /// 2: Always insert vtordisps to support RTTI on partially constructed
+ /// objects
+ ///
+ /// The stack always has at least one element in it.
+ SmallVector<MSVtorDispAttr::Mode, 2> VtorDispModeStack;
+
/// \brief Source location for newly created implicit MSInheritanceAttrs
SourceLocation ImplicitMSInheritanceAttrLoc;
LangOptions::PragmaMSPointersToMembersKind Kind,
SourceLocation PragmaLoc);
+ /// \brief Called on well formed \#pragma vtordisp().
+ void ActOnPragmaMSVtorDisp(PragmaVtorDispKind Kind, SourceLocation PragmaLoc,
+ MSVtorDispAttr::Mode Value);
+
/// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch
void ActOnPragmaDetectMismatch(StringRef Name, StringRef Value);
return IA->getSemanticSpelling();
}
+MSVtorDispAttr::Mode CXXRecordDecl::getMSVtorDispMode() const {
+ if (MSVtorDispAttr *VDA = getAttr<MSVtorDispAttr>())
+ return VDA->getVtorDispMode();
+ return MSVtorDispAttr::Mode(getASTContext().getLangOpts().VtorDispMode);
+}
+
// Returns the number of pointer and integer slots used to represent a member
// pointer in the MS C++ ABI.
//
llvm::SmallPtrSet<const CXXRecordDecl *, 2>
MicrosoftRecordLayoutBuilder::computeVtorDispSet(const CXXRecordDecl *RD) {
llvm::SmallPtrSet<const CXXRecordDecl *, 2> HasVtordispSet;
+
+ // /vd0 or #pragma vtordisp(0): Never use vtordisps when used as a vbase.
+ if (RD->getMSVtorDispMode() == MSVtorDispAttr::Never)
+ return HasVtordispSet;
+
+ // /vd2 or #pragma vtordisp(2): Always use vtordisps for virtual bases with
+ // vftables.
+ if (RD->getMSVtorDispMode() == MSVtorDispAttr::ForVFTable) {
+ for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(),
+ E = RD->vbases_end();
+ I != E; ++I) {
+ const CXXRecordDecl *BaseDecl = I->getType()->getAsCXXRecordDecl();
+ const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl);
+ if (Layout.hasExtendableVFPtr())
+ HasVtordispSet.insert(BaseDecl);
+ }
+ return HasVtordispSet;
+ }
+
+ // /vd1 or #pragma vtordisp(1): Try to guess based on whether we think it's
+ // possible for a partially constructed object with virtual base overrides to
+ // escape a non-trivial constructor.
+ assert(RD->getMSVtorDispMode() == MSVtorDispAttr::ForVBaseOverride);
+
// If any of our bases need a vtordisp for this type, so do we. Check our
// direct bases for vtordisp requirements.
for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(),
CmdArgs.push_back("-fms-memptr-rep=virtual");
}
+ if (Arg *A = Args.getLastArg(options::OPT_vtordisp_mode_EQ))
+ A->render(Args, CmdArgs);
+
if (!Args.hasArg(options::OPT_fdiagnostics_format_EQ)) {
CmdArgs.push_back("-fdiagnostics-format");
if (Args.hasArg(options::OPT__SLASH_fallback))
Opts.MicrosoftExt = Opts.MSVCCompat || Args.hasArg(OPT_fms_extensions);
Opts.AsmBlocks = Args.hasArg(OPT_fasm_blocks) || Opts.MicrosoftExt;
Opts.MSCVersion = getLastArgIntValue(Args, OPT_fmsc_version, 0, Diags);
+ Opts.VtorDispMode = getLastArgIntValue(Args, OPT_vtordisp_mode_EQ, 1, Diags);
Opts.Borland = Args.hasArg(OPT_fborland_extensions);
Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings);
Opts.ConstStrings = Args.hasFlag(OPT_fconst_strings, OPT_fno_const_strings,
}
};
-// Returns -1 on failure.
-static int LexSimpleInt(Preprocessor &PP, Token &Tok) {
- assert(Tok.is(tok::numeric_constant));
- SmallString<8> IntegerBuffer;
- bool NumberInvalid = false;
- StringRef Spelling = PP.getSpelling(Tok, IntegerBuffer, &NumberInvalid);
- if (NumberInvalid)
- return -1;
- NumericLiteralParser Literal(Spelling, Tok.getLocation(), PP);
- if (Literal.hadError || !Literal.isIntegerLiteral() || Literal.hasUDSuffix())
- return -1;
- llvm::APInt APVal(32, 0);
- if (Literal.GetIntegerValue(APVal))
- return -1;
- PP.Lex(Tok);
- return int(APVal.getLimitedValue(INT_MAX));
-}
-
/// "\#pragma warning(...)". MSVC's diagnostics do not map cleanly to clang's
/// diagnostics, so we don't really implement this pragma. We parse it and
/// ignore it to avoid -Wunknown-pragma warnings.
PP.Lex(Tok);
if (Tok.is(tok::comma)) {
PP.Lex(Tok);
- if (Tok.is(tok::numeric_constant))
- Level = LexSimpleInt(PP, Tok);
+ uint64_t Value;
+ if (Tok.is(tok::numeric_constant) &&
+ PP.parseSimpleIntegerLiteral(Tok, Value))
+ Level = int(Value);
if (Level < 0 || Level > 4) {
PP.Diag(Tok, diag::warn_pragma_warning_push_level);
return;
SmallVector<int, 4> Ids;
PP.Lex(Tok);
while (Tok.is(tok::numeric_constant)) {
- int Id = LexSimpleInt(PP, Tok);
- if (Id <= 0) {
+ uint64_t Value;
+ if (!PP.parseSimpleIntegerLiteral(Tok, Value) || Value == 0 ||
+ Value > INT_MAX) {
PP.Diag(Tok, diag::warn_pragma_warning_expected_number);
return;
}
- Ids.push_back(Id);
+ Ids.push_back(int(Value));
}
if (Callbacks)
Callbacks->PragmaWarning(DiagLoc, Specifier, Ids);
return true;
}
+bool Preprocessor::parseSimpleIntegerLiteral(Token &Tok, uint64_t &Value) {
+ assert(Tok.is(tok::numeric_constant));
+ SmallString<8> IntegerBuffer;
+ bool NumberInvalid = false;
+ StringRef Spelling = getSpelling(Tok, IntegerBuffer, &NumberInvalid);
+ if (NumberInvalid)
+ return false;
+ NumericLiteralParser Literal(Spelling, Tok.getLocation(), *this);
+ if (Literal.hadError || !Literal.isIntegerLiteral() || Literal.hasUDSuffix())
+ return false;
+ llvm::APInt APVal(64, 0);
+ if (Literal.GetIntegerValue(APVal))
+ return false;
+ Lex(Tok);
+ Value = APVal.getLimitedValue();
+ return true;
+}
+
void Preprocessor::addCommentHandler(CommentHandler *Handler) {
assert(Handler && "NULL comment handler");
assert(std::find(CommentHandlers.begin(), CommentHandlers.end(), Handler) ==
Actions.ActOnPragmaMSPointersToMembers(RepresentationMethod, PragmaLoc);
}
+void Parser::HandlePragmaMSVtorDisp() {
+ assert(Tok.is(tok::annot_pragma_ms_vtordisp));
+ uintptr_t Value = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue());
+ Sema::PragmaVtorDispKind Kind =
+ static_cast<Sema::PragmaVtorDispKind>((Value >> 16) & 0xFFFF);
+ MSVtorDispAttr::Mode Mode = MSVtorDispAttr::Mode(Value & 0xFFFF);
+ SourceLocation PragmaLoc = ConsumeToken(); // The annotation token.
+ Actions.ActOnPragmaMSVtorDisp(Kind, PragmaLoc, Mode);
+}
// #pragma GCC visibility comes in two variants:
// 'push' '(' [visibility] ')'
} else if (II->isStr("pop")) {
Kind = Sema::PPK_Pop;
} else {
- PP.Diag(Tok.getLocation(), diag::warn_pragma_pack_invalid_action);
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action) << "pack";
return;
}
PP.Lex(Tok);
PP.EnterToken(AnnotTok);
}
+/// \brief Handle '#pragma vtordisp'
+// The grammar for this pragma is as follows:
+//
+// <vtordisp-mode> ::= ('off' | 'on' | '0' | '1' | '2' )
+//
+// #pragma vtordisp '(' ['push' ','] vtordisp-mode ')'
+// #pragma vtordisp '(' 'pop' ')'
+// #pragma vtordisp '(' ')'
+void PragmaMSVtorDisp::HandlePragma(Preprocessor &PP,
+ PragmaIntroducerKind Introducer,
+ Token &Tok) {
+ SourceLocation VtorDispLoc = Tok.getLocation();
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(VtorDispLoc, diag::warn_pragma_expected_lparen) << "vtordisp";
+ return;
+ }
+ PP.Lex(Tok);
+
+ Sema::PragmaVtorDispKind Kind = Sema::PVDK_Set;
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II) {
+ if (II->isStr("push")) {
+ // #pragma vtordisp(push, mode)
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::comma)) {
+ PP.Diag(VtorDispLoc, diag::warn_pragma_expected_punc) << "vtordisp";
+ return;
+ }
+ PP.Lex(Tok);
+ Kind = Sema::PVDK_Push;
+ // not push, could be on/off
+ } else if (II->isStr("pop")) {
+ // #pragma vtordisp(pop)
+ PP.Lex(Tok);
+ Kind = Sema::PVDK_Pop;
+ }
+ // not push or pop, could be on/off
+ } else {
+ if (Tok.is(tok::r_paren)) {
+ // #pragma vtordisp()
+ Kind = Sema::PVDK_Reset;
+ }
+ }
+
+
+ uint64_t Value;
+ if (Kind == Sema::PVDK_Push || Kind == Sema::PVDK_Set) {
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II && II->isStr("off")) {
+ PP.Lex(Tok);
+ Value = 0;
+ } else if (II && II->isStr("on")) {
+ PP.Lex(Tok);
+ Value = 1;
+ } else if (Tok.is(tok::numeric_constant) &&
+ PP.parseSimpleIntegerLiteral(Tok, Value)) {
+ if (Value > 2) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_integer)
+ << 0 << 2 << "vtordisp";
+ return;
+ }
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action)
+ << "vtordisp";
+ return;
+ }
+ }
+
+ // Finish the pragma: ')' $
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(VtorDispLoc, diag::warn_pragma_expected_rparen) << "vtordisp";
+ return;
+ }
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "vtordisp";
+ return;
+ }
+
+ // Enter the annotation.
+ Token AnnotTok;
+ AnnotTok.startToken();
+ AnnotTok.setKind(tok::annot_pragma_ms_vtordisp);
+ AnnotTok.setLocation(VtorDispLoc);
+ AnnotTok.setAnnotationValue(
+ reinterpret_cast<void *>(static_cast<uintptr_t>((Kind << 16) | Value)));
+ PP.EnterToken(AnnotTok);
+}
+
/// \brief Handle the Microsoft \#pragma detect_mismatch extension.
///
/// The syntax is:
Token &FirstToken);
};
+class PragmaMSVtorDisp : public PragmaHandler {
+public:
+ explicit PragmaMSVtorDisp() : PragmaHandler("vtordisp") {}
+ virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+ Token &FirstToken);
+};
+
} // end namespace clang
#endif
PP.AddPragmaHandler(MSDetectMismatchHandler.get());
MSPointersToMembers.reset(new PragmaMSPointersToMembers());
PP.AddPragmaHandler(MSPointersToMembers.get());
+ MSVtorDisp.reset(new PragmaMSVtorDisp());
+ PP.AddPragmaHandler(MSVtorDisp.get());
}
CommentSemaHandler.reset(new ActionCommentHandler(actions));
MSDetectMismatchHandler.reset();
PP.RemovePragmaHandler(MSPointersToMembers.get());
MSPointersToMembers.reset();
+ PP.RemovePragmaHandler(MSVtorDisp.get());
+ MSVtorDisp.reset();
}
PP.RemovePragmaHandler("STDC", FPContractHandler.get());
case tok::annot_pragma_ms_pointers_to_members:
HandlePragmaMSPointersToMembers();
return DeclGroupPtrTy();
+ case tok::annot_pragma_ms_vtordisp:
+ HandlePragmaMSVtorDisp();
+ return DeclGroupPtrTy();
case tok::semi:
// Either a C++11 empty-declaration or attribute-declaration.
SingleDecl = Actions.ActOnEmptyDeclaration(getCurScope(),
CurContext(0), OriginalLexicalContext(0),
PackContext(0), MSStructPragmaOn(false),
MSPointerToMemberRepresentationMethod(
- pp.getLangOpts().getMSPointerToMemberRepresentationMethod()),
+ LangOpts.getMSPointerToMemberRepresentationMethod()),
+ VtorDispModeStack(1, MSVtorDispAttr::Mode(LangOpts.VtorDispMode)),
VisContext(0),
IsBuildingRecoveryCallExpr(false),
ExprNeedsCleanups(false), LateTemplateParser(0), OpaqueParser(0),
}
void Sema::AddMsStructLayoutForRecord(RecordDecl *RD) {
- if (!MSStructPragmaOn)
- return;
- RD->addAttr(MsStructAttr::CreateImplicit(Context));
+ if (MSStructPragmaOn)
+ RD->addAttr(MsStructAttr::CreateImplicit(Context));
+
+ // FIXME: We should merge AddAlignmentAttributesForRecord with
+ // AddMsStructLayoutForRecord into AddPragmaAttributesForRecord, which takes
+ // all active pragmas and applies them as attributes to class definitions.
+ if (VtorDispModeStack.back() != getLangOpts().VtorDispMode)
+ RD->addAttr(
+ MSVtorDispAttr::CreateImplicit(Context, VtorDispModeStack.back()));
}
void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind,
// If a name was specified then failure indicates the name
// wasn't found. Otherwise failure indicates the stack was
// empty.
- Diag(PragmaLoc, diag::warn_pragma_pack_pop_failed)
- << (Name ? "no record matching name" : "stack empty");
+ Diag(PragmaLoc, diag::warn_pragma_pop_failed)
+ << "pack" << (Name ? "no record matching name" : "stack empty");
// FIXME: Warn about popping named records as MSVC does.
} else {
ImplicitMSInheritanceAttrLoc = PragmaLoc;
}
+void Sema::ActOnPragmaMSVtorDisp(PragmaVtorDispKind Kind,
+ SourceLocation PragmaLoc,
+ MSVtorDispAttr::Mode Mode) {
+ switch (Kind) {
+ case PVDK_Set:
+ VtorDispModeStack.back() = Mode;
+ break;
+ case PVDK_Push:
+ VtorDispModeStack.push_back(Mode);
+ break;
+ case PVDK_Reset:
+ VtorDispModeStack.clear();
+ VtorDispModeStack.push_back(MSVtorDispAttr::Mode(LangOpts.VtorDispMode));
+ break;
+ case PVDK_Pop:
+ VtorDispModeStack.pop_back();
+ if (VtorDispModeStack.empty()) {
+ Diag(PragmaLoc, diag::warn_pragma_pop_failed) << "vtordisp"
+ << "stack empty";
+ VtorDispModeStack.push_back(MSVtorDispAttr::Mode(LangOpts.VtorDispMode));
+ }
+ break;
+ }
+}
+
void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope,
SourceLocation PragmaLoc) {
-// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32 -fdump-record-layouts -fsyntax-only %s 2>&1 \
+// RUN: %clang_cc1 -fno-rtti -fms-extensions -emit-llvm-only -triple i686-pc-win32 -fdump-record-layouts -fsyntax-only %s 2>&1 \
// RUN: | FileCheck %s
// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple x86_64-pc-win32 -fdump-record-layouts -fsyntax-only %s 2>/dev/null \
// RUN: | FileCheck %s -check-prefix CHECK-X64
// CHECK-X64-NEXT: | [sizeof=40, align=8
// CHECK-X64-NEXT: | nvsize=8, nvalign=8]
+namespace pragma_test1 {
+// No overrides means no vtordisps by default.
+struct A { virtual ~A(); virtual void foo(); int a; };
+struct B : virtual A { virtual ~B(); virtual void bar(); int b; };
+struct C : virtual B { int c; };
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT: 0 | struct pragma_test1::C
+// CHECK-NEXT: 0 | (C vbtable pointer)
+// CHECK-NEXT: 4 | int c
+// CHECK-NEXT: 8 | struct pragma_test1::A (virtual base)
+// CHECK-NEXT: 8 | (A vftable pointer)
+// CHECK-NEXT: 12 | int a
+// CHECK-NEXT: 16 | struct pragma_test1::B (virtual base)
+// CHECK-NEXT: 16 | (B vftable pointer)
+// CHECK-NEXT: 20 | (B vbtable pointer)
+// CHECK-NEXT: 24 | int b
+// CHECK-NEXT: | [sizeof=28, align=4
+// CHECK-NEXT: | nvsize=8, nvalign=4]
+}
+
+namespace pragma_test2 {
+struct A { virtual ~A(); virtual void foo(); int a; };
+#pragma vtordisp(push,2)
+struct B : virtual A { virtual ~B(); virtual void bar(); int b; };
+struct C : virtual B { int c; };
+#pragma vtordisp(pop)
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT: 0 | struct pragma_test2::C
+// CHECK-NEXT: 0 | (C vbtable pointer)
+// CHECK-NEXT: 4 | int c
+// CHECK-NEXT: 8 | (vtordisp for vbase A)
+// CHECK-NEXT: 12 | struct pragma_test2::A (virtual base)
+// CHECK-NEXT: 12 | (A vftable pointer)
+// CHECK-NEXT: 16 | int a
+// By adding a virtual method and vftable to B, now we need a vtordisp.
+// CHECK-NEXT: 20 | (vtordisp for vbase B)
+// CHECK-NEXT: 24 | struct pragma_test2::B (virtual base)
+// CHECK-NEXT: 24 | (B vftable pointer)
+// CHECK-NEXT: 28 | (B vbtable pointer)
+// CHECK-NEXT: 32 | int b
+// CHECK-NEXT: | [sizeof=36, align=4
+// CHECK-NEXT: | nvsize=8, nvalign=4]
+}
+
+namespace pragma_test3 {
+struct A { virtual ~A(); virtual void foo(); int a; };
+#pragma vtordisp(push,2)
+struct B : virtual A { virtual ~B(); virtual void foo(); int b; };
+struct C : virtual B { int c; };
+#pragma vtordisp(pop)
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT: 0 | struct pragma_test3::C
+// CHECK-NEXT: 0 | (C vbtable pointer)
+// CHECK-NEXT: 4 | int c
+// CHECK-NEXT: 8 | (vtordisp for vbase A)
+// CHECK-NEXT: 12 | struct pragma_test3::A (virtual base)
+// CHECK-NEXT: 12 | (A vftable pointer)
+// CHECK-NEXT: 16 | int a
+// No vtordisp before B! It doesn't have its own vftable.
+// CHECK-NEXT: 20 | struct pragma_test3::B (virtual base)
+// CHECK-NEXT: 20 | (B vbtable pointer)
+// CHECK-NEXT: 24 | int b
+// CHECK-NEXT: | [sizeof=28, align=4
+// CHECK-NEXT: | nvsize=8, nvalign=4]
+}
+
+namespace pragma_test4 {
+struct A {
+ A();
+ virtual void foo();
+ int a;
+};
+
+// Make sure the pragma applies to class template decls before they've been
+// instantiated.
+#pragma vtordisp(push,2)
+template <typename T>
+struct B : virtual A {
+ B();
+ virtual ~B();
+ virtual void bar();
+ T b;
+};
+#pragma vtordisp(pop)
+
+struct C : virtual B<int> { int c; };
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT: 0 | struct pragma_test4::C
+// CHECK-NEXT: 0 | (C vbtable pointer)
+// CHECK-NEXT: 4 | int c
+// Pragma applies to B, which has vbase A.
+// CHECK-NEXT: 8 | (vtordisp for vbase A)
+// CHECK-NEXT: 12 | struct pragma_test4::A (virtual base)
+// CHECK-NEXT: 12 | (A vftable pointer)
+// CHECK-NEXT: 16 | int a
+// Pragma does not apply to C, and B doesn't usually need a vtordisp in C.
+// CHECK-NEXT: 20 | struct pragma_test4::B<int> (virtual base)
+// CHECK-NEXT: 20 | (B vftable pointer)
+// CHECK-NEXT: 24 | (B vbtable pointer)
+// CHECK-NEXT: 28 | int b
+// CHECK-NEXT: | [sizeof=32, align=4
+// CHECK-NEXT: | nvsize=8, nvalign=4]
+}
+
int a[
sizeof(A)+
sizeof(C)+
sizeof(D)+
sizeof(CT)+
-sizeof(XC)];
+sizeof(XC)+
+sizeof(pragma_test1::C)+
+sizeof(pragma_test2::C)+
+sizeof(pragma_test3::C)+
+sizeof(pragma_test4::C)];
--- /dev/null
+// RUN: %clang_cc1 -std=c++11 -fms-extensions -fms-compatibility -fsyntax-only -triple=i386-pc-win32 -verify %s
+
+struct A { int a; };
+
+#pragma vtordisp(pop) // expected-warning {{#pragma vtordisp(pop, ...) failed: stack empty}}
+#pragma vtordisp(push, 0)
+#pragma vtordisp(push, 1)
+#pragma vtordisp(push, 2)
+struct B : virtual A { int b; };
+#pragma vtordisp(pop)
+#pragma vtordisp(pop)
+#pragma vtordisp(pop)
+#pragma vtordisp(pop) // expected-warning {{#pragma vtordisp(pop, ...) failed: stack empty}}
+
+#pragma vtordisp(push, 3) // expected-warning {{expected integer between 0 and 2 inclusive in '#pragma vtordisp' - ignored}}
+#pragma vtordisp()
+
+#define ONE 1
+#pragma vtordisp(push, ONE)
+#define TWO 1
+#pragma vtordisp(push, TWO)
+
+// Test a reset.
+#pragma vtordisp()
+#pragma vtordisp(pop) // expected-warning {{#pragma vtordisp(pop, ...) failed: stack empty}}
+
+#pragma vtordisp( // expected-warning {{unknown action for '#pragma vtordisp' - ignored}}
+#pragma vtordisp(asdf) // expected-warning {{unknown action for '#pragma vtordisp' - ignored}}
+#pragma vtordisp(,) // expected-warning {{unknown action for '#pragma vtordisp' - ignored}}
+#pragma vtordisp // expected-warning {{missing '(' after '#pragma vtordisp' - ignoring}}
+#pragma vtordisp(3) // expected-warning {{expected integer between 0 and 2 inclusive in '#pragma vtordisp' - ignored}}
+#pragma vtordisp(), stuff // expected-warning {{extra tokens}}
+
+struct C {
+// FIXME: Our implementation based on token insertion makes it impossible for
+// the pragma to appear everywhere we should support it.
+//#pragma vtordisp()
+ struct D : virtual A {
+ };
+};
--- /dev/null
+// RUN: %clang_cc1 -triple i686-pc-win32 -std=c++11 -vtordisp-mode=0 -DVTORDISP_MODE=0 %s -verify
+// RUN: %clang_cc1 -triple i686-pc-win32 -std=c++11 -vtordisp-mode=1 -DVTORDISP_MODE=1 %s -verify
+// RUN: %clang_cc1 -triple i686-pc-win32 -std=c++11 -vtordisp-mode=2 -DVTORDISP_MODE=2 %s -verify
+
+// expected-no-diagnostics
+
+struct A {
+ A();
+ virtual void foo();
+};
+
+// At /vd1, there is a vtordisp before A.
+struct B : virtual A {
+ B();
+ virtual void foo();
+ virtual void bar();
+};
+
+// At /vd2, there is a vtordisp before B, but only because it has its own
+// vftable.
+struct C : virtual B {
+ C();
+};
+
+// There are two vfptrs, two vbptrs, and some number of vtordisps.
+static_assert(sizeof(C) == 2 * 4 + 2 * 4 + 4 * VTORDISP_MODE, "size mismatch");