Hi majnemer, rsmith,
These features are new in VS 2013 and are necessary in order to layout
std::ostream correctly. Currently we have an ABI incompatibility when
self-hosting with the 2013 stdlib in our convertible_fwd_ostream wrapper
in gtest.
This change adds another implicit attribute, MSVtorDispAttr, because
implicit attributes are currently the best way to make sure the
information stays on class templates through instantiation.
http://llvm-reviews.chandlerc.com/D2746
Files:
include/clang/AST/DeclCXX.h
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/LangOptions.def
include/clang/Basic/TokenKinds.def
include/clang/Driver/CC1Options.td
include/clang/Driver/CLCompatOptions.td
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/AST/MicrosoftCXXABI.cpp
lib/AST/RecordLayoutBuilder.cpp
lib/Driver/Tools.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Parse/ParsePragma.cpp
lib/Parse/ParsePragma.h
lib/Parse/Parser.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaAttr.cpp
lib/Sema/SemaDecl.cpp
test/Layout/ms-x86-vtordisp.cpp
test/SemaCXX/pragma-vtordisp.cpp
test/SemaCXX/vtordisp-mode.cpp
Index: include/clang/AST/DeclCXX.h
===================================================================
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -1615,6 +1615,10 @@
(hasDefinition() && isPolymorphic());
}
+ /// \brief Controls when vtordisps will be emitted if this record is used as a
+ /// virtual base.
+ unsigned 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.
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1431,6 +1431,13 @@
}];
}
+def MSVtorDisp : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly.
+ let Spellings = [];
+ let Args = [UnsignedArgument<"VtorDispMode">];
+ let SemaHandler = 0;
+}
+
def Unaligned : IgnoredAttr {
let Spellings = [Keyword<"__unaligned">];
}
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -770,6 +770,9 @@
"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>;
@@ -789,8 +792,8 @@
"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">,
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -480,7 +480,7 @@
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>;
Index: include/clang/Basic/LangOptions.def
===================================================================
--- include/clang/Basic/LangOptions.def
+++ include/clang/Basic/LangOptions.def
@@ -176,6 +176,7 @@
"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")
Index: include/clang/Basic/TokenKinds.def
===================================================================
--- include/clang/Basic/TokenKinds.def
+++ include/clang/Basic/TokenKinds.def
@@ -678,6 +678,11 @@
// 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.
Index: include/clang/Driver/CC1Options.td
===================================================================
--- include/clang/Driver/CC1Options.td
+++ include/clang/Driver/CC1Options.td
@@ -474,6 +474,8 @@
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
Index: include/clang/Driver/CLCompatOptions.td
===================================================================
--- include/clang/Driver/CLCompatOptions.td
+++ include/clang/Driver/CLCompatOptions.td
@@ -117,6 +117,8 @@
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>;
@@ -241,7 +243,6 @@
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">;
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -156,6 +156,7 @@
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++
@@ -460,6 +461,8 @@
void HandlePragmaMSPointersToMembers();
+ void HandlePragmaMSVtorDisp();
+
/// \brief Handle the annotation token produced for
/// #pragma align...
void HandlePragmaAlign();
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -266,6 +266,25 @@
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<unsigned, 2> VtorDispModeStack;
+
/// \brief Source location for newly created implicit MSInheritanceAttrs
SourceLocation ImplicitMSInheritanceAttrLoc;
@@ -6984,6 +7003,10 @@
LangOptions::PragmaMSPointersToMembersKind Kind,
SourceLocation PragmaLoc);
+ /// \brief Called on well formed \#pragma vtordisp().
+ void ActOnPragmaMSVtorDisp(PragmaVtorDispKind Kind, SourceLocation PragmaLoc,
+ unsigned Value);
+
/// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch
void ActOnPragmaDetectMismatch(StringRef Name, StringRef Value);
Index: lib/AST/MicrosoftCXXABI.cpp
===================================================================
--- lib/AST/MicrosoftCXXABI.cpp
+++ lib/AST/MicrosoftCXXABI.cpp
@@ -109,6 +109,12 @@
return IA->getSemanticSpelling();
}
+unsigned CXXRecordDecl::getMSVtorDispMode() const {
+ if (MSVtorDispAttr *VDA = getAttr<MSVtorDispAttr>())
+ return VDA->getVtorDispMode();
+ return getASTContext().getLangOpts().VtorDispMode;
+}
+
// Returns the number of pointer and integer slots used to represent a member
// pointer in the MS C++ ABI.
//
Index: lib/AST/RecordLayoutBuilder.cpp
===================================================================
--- lib/AST/RecordLayoutBuilder.cpp
+++ lib/AST/RecordLayoutBuilder.cpp
@@ -2719,6 +2719,29 @@
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() == 0)
+ return HasVtordispSet;
+
+ // /vd2 or #pragma vtordisp(2): Always use vtordisps for virtual bases with
+ // vftables.
+ if (RD->getMSVtorDispMode() == 2) {
+ 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.
+
// 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(),
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -4048,6 +4048,9 @@
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))
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1306,6 +1306,7 @@
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,
Index: lib/Parse/ParsePragma.cpp
===================================================================
--- lib/Parse/ParsePragma.cpp
+++ lib/Parse/ParsePragma.cpp
@@ -189,6 +189,38 @@
Actions.ActOnPragmaMSPointersToMembers(RepresentationMethod, PragmaLoc);
}
+void Parser::HandlePragmaMSVtorDisp() {
+ assert(Tok.is(tok::annot_pragma_ms_vtordisp));
+ Sema::PragmaVtorDispKind Kind = static_cast<Sema::PragmaVtorDispKind>(
+ reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+ SourceLocation PragmaLoc = ConsumeToken(); // The annotation token.
+ unsigned Value = ~0U;
+ if (Kind == Sema::PVDK_Set || Kind == Sema::PVDK_Push) {
+ if (Tok.is(tok::numeric_constant)) {
+ ExprResult Num = Actions.ActOnNumericConstant(Tok);
+ SourceLocation NumLoc = Tok.getLocation();
+ ConsumeToken();
+ if (Num.isInvalid())
+ return;
+ IntegerLiteral *Int = dyn_cast<IntegerLiteral>(Num.get());
+ if (Int)
+ Value = Int->getValue().getLimitedValue(16);
+ if (Value > 2) {
+ PP.Diag(NumLoc, diag::warn_pragma_expected_integer)
+ << 0 << 2 << "vtordisp";
+ return;
+ }
+ } else {
+ const IdentifierInfo *Val = Tok.getIdentifierInfo();
+ ConsumeToken();
+ assert(Val);
+ Value = llvm::StringSwitch<unsigned>(Val->getName())
+ .Case("on", 1)
+ .Case("off", 0);
+ }
+ }
+ Actions.ActOnPragmaMSVtorDisp(Kind, PragmaLoc, Value);
+}
// #pragma GCC visibility comes in two variants:
// 'push' '(' [visibility] ')'
@@ -291,7 +323,7 @@
} 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);
@@ -903,6 +935,95 @@
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;
+ }
+ }
+
+
+ Token Value;
+ if (Kind == Sema::PVDK_Push || Kind == Sema::PVDK_Set) {
+ const IdentifierInfo *II = Tok.getIdentifierInfo();
+ if (II && II->isStr("off")) {
+ Value = Tok;
+ } else if (II && II->isStr("on")) {
+ Value = Tok;
+ } else if (Tok.is(tok::numeric_constant)) {
+ Value = Tok;
+ } else {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_action)
+ << "vtordisp";
+ return;
+ }
+ PP.Lex(Tok);
+ }
+
+ // 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 value token, if relevant.
+ if (Kind == Sema::PVDK_Push || Kind == Sema::PVDK_Set)
+ PP.EnterToken(Value);
+
+ // 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)));
+ PP.EnterToken(AnnotTok);
+}
+
/// \brief Handle the Microsoft \#pragma detect_mismatch extension.
///
/// The syntax is:
Index: lib/Parse/ParsePragma.h
===================================================================
--- lib/Parse/ParsePragma.h
+++ lib/Parse/ParsePragma.h
@@ -141,6 +141,13 @@
Token &FirstToken);
};
+class PragmaMSVtorDisp : public PragmaHandler {
+public:
+ explicit PragmaMSVtorDisp() : PragmaHandler("vtordisp") {}
+ virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+ Token &FirstToken);
+};
+
} // end namespace clang
#endif
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -110,6 +110,8 @@
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));
@@ -487,6 +489,8 @@
MSDetectMismatchHandler.reset();
PP.RemovePragmaHandler(MSPointersToMembers.get());
MSPointersToMembers.reset();
+ PP.RemovePragmaHandler(MSVtorDisp.get());
+ MSVtorDisp.reset();
}
PP.RemovePragmaHandler("STDC", FPContractHandler.get());
@@ -709,6 +713,9 @@
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(),
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -77,7 +77,8 @@
CurContext(0), OriginalLexicalContext(0),
PackContext(0), MSStructPragmaOn(false),
MSPointerToMemberRepresentationMethod(
- pp.getLangOpts().getMSPointerToMemberRepresentationMethod()),
+ LangOpts.getMSPointerToMemberRepresentationMethod()),
+ VtorDispModeStack(1, LangOpts.VtorDispMode),
VisContext(0),
IsBuildingRecoveryCallExpr(false),
ExprNeedsCleanups(false), LateTemplateParser(0), OpaqueParser(0),
Index: lib/Sema/SemaAttr.cpp
===================================================================
--- lib/Sema/SemaAttr.cpp
+++ lib/Sema/SemaAttr.cpp
@@ -130,9 +130,17 @@
}
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,
@@ -246,8 +254,8 @@
// 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 {
@@ -294,6 +302,30 @@
ImplicitMSInheritanceAttrLoc = PragmaLoc;
}
+void Sema::ActOnPragmaMSVtorDisp(PragmaVtorDispKind Kind,
+ SourceLocation PragmaLoc, unsigned Value) {
+ switch (Kind) {
+ case PVDK_Set:
+ VtorDispModeStack.back() = Value;
+ break;
+ case PVDK_Push:
+ VtorDispModeStack.push_back(Value);
+ break;
+ case PVDK_Reset:
+ VtorDispModeStack.clear();
+ VtorDispModeStack.push_back(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(LangOpts.VtorDispMode);
+ }
+ break;
+ }
+}
+
void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope,
SourceLocation PragmaLoc) {
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -11211,8 +11211,9 @@
// Make sure we "complete" the definition even it is invalid.
if (Tag->isBeingDefined()) {
assert(Tag->isInvalidDecl() && "We should already have completed it");
- if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag))
+ if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) {
RD->completeDefinition();
+ }
}
if (isa<CXXRecordDecl>(Tag))
@@ -12138,6 +12139,7 @@
Record->setInvalidDecl();
}
}
+
CXXRecord->completeDefinition(&FinalOverriders);
Completed = true;
}
Index: test/Layout/ms-x86-vtordisp.cpp
===================================================================
--- test/Layout/ms-x86-vtordisp.cpp
+++ test/Layout/ms-x86-vtordisp.cpp
@@ -1,4 +1,4 @@
-// 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
@@ -214,9 +214,125 @@
// 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)];
Index: test/SemaCXX/pragma-vtordisp.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/pragma-vtordisp.cpp
@@ -0,0 +1,40 @@
+// 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 {
+ };
+};
Index: test/SemaCXX/vtordisp-mode.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/vtordisp-mode.cpp
@@ -0,0 +1,26 @@
+// 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");
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits