]> granicus.if.org Git - clang/commitdiff
MS ABI: Implement #pragma vtordisp() and clang-cl /vdN
authorReid Kleckner <reid@kleckner.net>
Wed, 12 Feb 2014 23:50:26 +0000 (23:50 +0000)
committerReid Kleckner <reid@kleckner.net>
Wed, 12 Feb 2014 23:50:26 +0000 (23:50 +0000)
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.

Reviewers: majnemer

Differential Revision: http://llvm-reviews.chandlerc.com/D2746

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201274 91177308-0d34-0410-b5e6-96231b3b80d8

25 files changed:
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/Lex/Preprocessor.h
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/Lex/Pragma.cpp
lib/Lex/Preprocessor.cpp
lib/Parse/ParsePragma.cpp
lib/Parse/ParsePragma.h
lib/Parse/Parser.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaAttr.cpp
test/Layout/ms-x86-vtordisp.cpp
test/SemaCXX/pragma-vtordisp.cpp [new file with mode: 0644]
test/SemaCXX/vtordisp-mode.cpp [new file with mode: 0644]

index 93c64805aa9f96065dbe1b681466d202f7d70444..aafb32ae7da4d81091b2c1b1c53a59025d1bfd55 100644 (file)
@@ -1615,6 +1615,10 @@ public:
            (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 b263c1bc1d6171344731e0e5158310847c5b2bb4..e91e1cab36ea6c906966ef1acd6104e6cc6fd11f 100644 (file)
@@ -1431,6 +1431,23 @@ def MSInheritance : InheritableAttr {
   }];
 }
 
+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 de07ee8d8fc9fec79a7d333ed50a4c34e08ea074..d37cef8db2eae47903806c1c79a949acb6d8bdd3 100644 (file)
@@ -770,6 +770,9 @@ def warn_pragma_expected_rparen : Warning<
   "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 @@ def warn_pragma_align_invalid_option : Warning<
   "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 7daff2b784b24dc399d4ef2a2079c12bc138f204..2569fa1cd408b5540ae9cffcb906c96d6f731b6b 100644 (file)
@@ -480,7 +480,7 @@ def warn_pragma_pack_invalid_alignment : Warning<
 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 2fa6850f56a08f2ee2e46a57ecbc1ee658965547..bb2f1e4ec20f07efaae501da2f1da0a9817ebf29 100644 (file)
@@ -176,6 +176,7 @@ BENIGN_LANGOPT(NumLargeByValueCopy, 32, 0,
         "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 05c2fa2b27c76996d08fee75ee765903c666cd0c..eddfd7e4af97020201d1a6ce9cd88d5d9b4d1e91 100644 (file)
@@ -678,6 +678,11 @@ ANNOTATION(pragma_fp_contract)
 // 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 a62e112d5b559ba8be2a88f80287bab7adf92d32..c4d04e9e0730a4611dc3069d3d8df383e9436af2 100644 (file)
@@ -474,6 +474,8 @@ def fsized_deallocation : Flag<["-"], "fsized-deallocation">,
   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 ec828b0b34518753163219a9536a8fa7a0aef835..4f00a267eae887a05a520a8a8631d14d3fb45f5d 100644 (file)
@@ -117,6 +117,8 @@ def _SLASH_WX : CLFlag<"WX">, HelpText<"Treat warnings as errors">,
 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_Qpar : CLFlag<"Qpar">;
 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 213406769313c5ad20568ff2e80bbf306138f3bb..8ea67ab532fcd5358eb314de415bc346c2b6a366 100644 (file)
@@ -803,6 +803,11 @@ public:
     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 8b9053fa09808a2ef85493cef9c2685c8156f66f..a7ef5b087baa57645eb22d749699ca8143f78b06 100644 (file)
@@ -156,6 +156,7 @@ class Parser : public CodeCompletionHandler {
   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 @@ private:
 
   void HandlePragmaMSPointersToMembers();
 
+  void HandlePragmaMSVtorDisp();
+
   /// \brief Handle the annotation token produced for
   /// #pragma align...
   void HandlePragmaAlign();
index 2f72306d23a0cb1f279908372aad787a4ae61e24..7ca40a6b3c82b657a87a04a4f4b88747e094ddbd 100644 (file)
@@ -266,6 +266,25 @@ public:
   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;
 
@@ -6984,6 +7003,10 @@ public:
       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 91ffc63b770972573fd2f9d1eced774764a65fe6..f8e03aed20508c35ff7fefdbc796fe4fa445d170 100644 (file)
@@ -109,6 +109,12 @@ CXXRecordDecl::getMSInheritanceModel() const {
   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.
 //
index 1f64b4be50762aeba0328f093b3a6207cf310eaa..5624ce7836ffb5e081dbd96b327d323d4082bdfa 100644 (file)
@@ -2731,6 +2731,30 @@ RequiresVtordisp(const llvm::SmallPtrSet<const CXXRecordDecl *, 2> &HasVtordisp,
 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(),
index 73437f68f35f6b92c07af765f9a3021506243f95..acd15ea38a621163d25db8d0524086a4eadca8fc 100644 (file)
@@ -4056,6 +4056,9 @@ void Clang::AddClangCLArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
       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 5b26df037f60c4b24d0dd2593579fdd7c25fbe63..a058a9c86c146d6353bee37f4d8621ada18323b4 100644 (file)
@@ -1310,6 +1310,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   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 b2f047a2fd1bad8df202601a48e84abae427c4c7..1a7f6a5f4835c8eff6634cba7085b6ee46884b9e 100644 (file)
@@ -1008,24 +1008,6 @@ public:
   }
 };
 
-// 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 @@ struct PragmaWarningHandler : public PragmaHandler {
       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 @@ struct PragmaWarningHandler : public PragmaHandler {
         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);
index 68201b39f6922af4184fd454e4ca8b7eb9426bb5..9ffc83ceffb43a46bfc570a6c89f56508b561ade 100644 (file)
@@ -819,6 +819,24 @@ bool Preprocessor::FinishLexStringLiteral(Token &Result, std::string &String,
   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 8cf2b3f1d2678340572b88ea01bdee182937d5d1..6d92edb1dfbd09ff13d9ac34ce8785f8cae49bf4 100644 (file)
@@ -189,6 +189,15 @@ void Parser::HandlePragmaMSPointersToMembers() {
   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] ')'
@@ -291,7 +300,7 @@ void PragmaPackHandler::HandlePragma(Preprocessor &PP,
       } 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 @@ void PragmaMSPointersToMembers::HandlePragma(Preprocessor &PP,
   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:
index 734bc8d6349263f8a602c1cf3e007ae1bf18450b..73db57250424346168f5eb10d08cdf4dbf7b4121 100644 (file)
@@ -141,6 +141,13 @@ public:
                             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 a52248f2f9159a2497811b025672560e831f1bc0..ffe9ae833633c56b6063eb10f10a8e64de7dc19f 100644 (file)
@@ -110,6 +110,8 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
     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 @@ Parser::~Parser() {
     MSDetectMismatchHandler.reset();
     PP.RemovePragmaHandler(MSPointersToMembers.get());
     MSPointersToMembers.reset();
+    PP.RemovePragmaHandler(MSVtorDisp.get());
+    MSVtorDisp.reset();
   }
 
   PP.RemovePragmaHandler("STDC", FPContractHandler.get());
@@ -709,6 +713,9 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
   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 871e1c97d77f2988633c6f69bdcb522987c09d7f..416388c294c0791c66169b6a966ee03b3950543f 100644 (file)
@@ -77,7 +77,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
     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 4da14ec1d1d68099eaafb007ac21416dbb6b0c44..6c6ba18018ffc688b71b4a66d5e249511ae36a4c 100644 (file)
@@ -130,9 +130,15 @@ void Sema::AddAlignmentAttributesForRecord(RecordDecl *RD) {
 }
 
 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 @@ void Sema::ActOnPragmaPack(PragmaPackKind Kind, IdentifierInfo *Name,
       // 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 @@ void Sema::ActOnPragmaMSPointersToMembers(
   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 52a8fe273505f7abe0433c265cdaea8dbb5f1823..ad4902e05f9838909ecc3c4967d8c0912e56585c 100644 (file)
@@ -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 @@ struct XC : virtual XB {
 // 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)];
diff --git a/test/SemaCXX/pragma-vtordisp.cpp b/test/SemaCXX/pragma-vtordisp.cpp
new file mode 100644 (file)
index 0000000..49841c5
--- /dev/null
@@ -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 {
+  };
+};
diff --git a/test/SemaCXX/vtordisp-mode.cpp b/test/SemaCXX/vtordisp-mode.cpp
new file mode 100644 (file)
index 0000000..dc91534
--- /dev/null
@@ -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");