Index: cfe/trunk/include/clang/AST/DeclCXX.h =================================================================== --- cfe/trunk/include/clang/AST/DeclCXX.h +++ cfe/trunk/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. + 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. Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -1431,6 +1431,23 @@ }]; } +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">]; } Index: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td @@ -770,6 +770,9 @@ "missing ')' after '#pragma %0' - ignoring">, InGroup; def warn_pragma_expected_identifier : Warning< "expected identifier in '#pragma %0' - ignored">, InGroup; +def warn_pragma_expected_integer : Warning< + "expected integer between %0 and %1 inclusive in '#pragma %2' - ignored">, + InGroup; def warn_pragma_ms_struct : Warning< "incorrect use of '#pragma ms_struct on|off' - ignored">, InGroup; @@ -789,8 +792,8 @@ "invalid alignment option in '#pragma %select{align|options align}0' - ignored">, InGroup; // - #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; def warn_pragma_pack_malformed : Warning< "expected integer or identifier in '#pragma pack' - ignored">, Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/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; def warn_pragma_ms_struct_failed : Warning<"#pramga ms_struct can not be used with dynamic classes or structures">, InGroup; Index: cfe/trunk/include/clang/Basic/LangOptions.def =================================================================== --- cfe/trunk/include/clang/Basic/LangOptions.def +++ cfe/trunk/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: cfe/trunk/include/clang/Basic/TokenKinds.def =================================================================== --- cfe/trunk/include/clang/Basic/TokenKinds.def +++ cfe/trunk/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: cfe/trunk/include/clang/Driver/CC1Options.td =================================================================== --- cfe/trunk/include/clang/Driver/CC1Options.td +++ cfe/trunk/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: cfe/trunk/include/clang/Driver/CLCompatOptions.td =================================================================== --- cfe/trunk/include/clang/Driver/CLCompatOptions.td +++ cfe/trunk/include/clang/Driver/CLCompatOptions.td @@ -117,6 +117,8 @@ def _SLASH_WX_ : CLFlag<"WX-">, HelpText<"Do not treat warnings as errors">, Alias, AliasArgs<["no-error"]>; def _SLASH_w_flag : CLFlag<"w">, HelpText<"Disable all warnings">, Alias; +def _SLASH_vd : CLJoined<"vd">, HelpText<"Control vtordisp placement">, + Alias; def _SLASH_Z7 : CLFlag<"Z7">, Alias; def _SLASH_Zi : CLFlag<"Zi">, HelpText<"Enable debug information">, Alias; @@ -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: cfe/trunk/include/clang/Lex/Preprocessor.h =================================================================== --- cfe/trunk/include/clang/Lex/Preprocessor.h +++ cfe/trunk/include/clang/Lex/Preprocessor.h @@ -803,6 +803,11 @@ 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; Index: cfe/trunk/include/clang/Parse/Parser.h =================================================================== --- cfe/trunk/include/clang/Parse/Parser.h +++ cfe/trunk/include/clang/Parse/Parser.h @@ -156,6 +156,7 @@ OwningPtr MSCommentHandler; OwningPtr MSDetectMismatchHandler; OwningPtr MSPointersToMembers; + OwningPtr 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: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/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 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, + MSVtorDispAttr::Mode Value); + /// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch void ActOnPragmaDetectMismatch(StringRef Name, StringRef Value); Index: cfe/trunk/lib/AST/MicrosoftCXXABI.cpp =================================================================== --- cfe/trunk/lib/AST/MicrosoftCXXABI.cpp +++ cfe/trunk/lib/AST/MicrosoftCXXABI.cpp @@ -109,6 +109,12 @@ return IA->getSemanticSpelling(); } +MSVtorDispAttr::Mode CXXRecordDecl::getMSVtorDispMode() const { + if (MSVtorDispAttr *VDA = getAttr()) + 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. // Index: cfe/trunk/lib/AST/RecordLayoutBuilder.cpp =================================================================== --- cfe/trunk/lib/AST/RecordLayoutBuilder.cpp +++ cfe/trunk/lib/AST/RecordLayoutBuilder.cpp @@ -2731,6 +2731,30 @@ llvm::SmallPtrSet MicrosoftRecordLayoutBuilder::computeVtorDispSet(const CXXRecordDecl *RD) { llvm::SmallPtrSet 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(), Index: cfe/trunk/lib/Driver/Tools.cpp =================================================================== --- cfe/trunk/lib/Driver/Tools.cpp +++ cfe/trunk/lib/Driver/Tools.cpp @@ -4056,6 +4056,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: cfe/trunk/lib/Frontend/CompilerInvocation.cpp =================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp @@ -1310,6 +1310,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: cfe/trunk/lib/Lex/Pragma.cpp =================================================================== --- cfe/trunk/lib/Lex/Pragma.cpp +++ cfe/trunk/lib/Lex/Pragma.cpp @@ -1008,24 +1008,6 @@ } }; -// 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. @@ -1060,8 +1042,10 @@ 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; @@ -1105,12 +1089,13 @@ SmallVector 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); Index: cfe/trunk/lib/Lex/Preprocessor.cpp =================================================================== --- cfe/trunk/lib/Lex/Preprocessor.cpp +++ cfe/trunk/lib/Lex/Preprocessor.cpp @@ -819,6 +819,24 @@ 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) == Index: cfe/trunk/lib/Parse/ParsePragma.h =================================================================== --- cfe/trunk/lib/Parse/ParsePragma.h +++ cfe/trunk/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: cfe/trunk/lib/Parse/ParsePragma.cpp =================================================================== --- cfe/trunk/lib/Parse/ParsePragma.cpp +++ cfe/trunk/lib/Parse/ParsePragma.cpp @@ -189,6 +189,15 @@ Actions.ActOnPragmaMSPointersToMembers(RepresentationMethod, PragmaLoc); } +void Parser::HandlePragmaMSVtorDisp() { + assert(Tok.is(tok::annot_pragma_ms_vtordisp)); + uintptr_t Value = reinterpret_cast(Tok.getAnnotationValue()); + Sema::PragmaVtorDispKind Kind = + static_cast((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] ')' @@ -291,7 +300,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 +912,97 @@ PP.EnterToken(AnnotTok); } +/// \brief Handle '#pragma vtordisp' +// The grammar for this pragma is as follows: +// +// ::= ('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(static_cast((Kind << 16) | Value))); + PP.EnterToken(AnnotTok); +} + /// \brief Handle the Microsoft \#pragma detect_mismatch extension. /// /// The syntax is: Index: cfe/trunk/lib/Parse/Parser.cpp =================================================================== --- cfe/trunk/lib/Parse/Parser.cpp +++ cfe/trunk/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: cfe/trunk/lib/Sema/Sema.cpp =================================================================== --- cfe/trunk/lib/Sema/Sema.cpp +++ cfe/trunk/lib/Sema/Sema.cpp @@ -77,7 +77,8 @@ 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), Index: cfe/trunk/lib/Sema/SemaAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaAttr.cpp +++ cfe/trunk/lib/Sema/SemaAttr.cpp @@ -130,9 +130,15 @@ } 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 +252,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 +300,31 @@ 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) { Index: cfe/trunk/test/Layout/ms-x86-vtordisp.cpp =================================================================== --- cfe/trunk/test/Layout/ms-x86-vtordisp.cpp +++ cfe/trunk/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 +struct B : virtual A { + B(); + virtual ~B(); + virtual void bar(); + T b; +}; +#pragma vtordisp(pop) + +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_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 (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: cfe/trunk/test/SemaCXX/pragma-vtordisp.cpp =================================================================== --- cfe/trunk/test/SemaCXX/pragma-vtordisp.cpp +++ cfe/trunk/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: cfe/trunk/test/SemaCXX/vtordisp-mode.cpp =================================================================== --- cfe/trunk/test/SemaCXX/vtordisp-mode.cpp +++ cfe/trunk/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");