- Use an enum in MSVtorDispAttr

Hi majnemer, rsmith,

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

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2746?vs=7007&id=7033#toc

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
  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.
+  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: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ 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: 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<MSVtorDispAttr::Mode, 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,
+                             MSVtorDispAttr::Mode 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();
 }
 
+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: lib/AST/RecordLayoutBuilder.cpp
===================================================================
--- lib/AST/RecordLayoutBuilder.cpp
+++ lib/AST/RecordLayoutBuilder.cpp
@@ -2731,6 +2731,30 @@
 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: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ 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: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ 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: lib/Parse/ParsePragma.cpp
===================================================================
--- lib/Parse/ParsePragma.cpp
+++ lib/Parse/ParsePragma.cpp
@@ -189,6 +189,43 @@
   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.
+  MSVtorDispAttr::Mode Mode = MSVtorDispAttr::Never;
+  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());
+      unsigned Value = ~0U;
+      if (Int)
+        Value = Int->getValue().getLimitedValue(16);
+      switch (Value) {
+      case 0: Mode = MSVtorDispAttr::Never; break;
+      case 1: Mode = MSVtorDispAttr::ForVBaseOverride; break;
+      case 2: Mode = MSVtorDispAttr::ForVFTable; break;
+      default:
+        PP.Diag(NumLoc, diag::warn_pragma_expected_integer)
+            << 0 << 2 << "vtordisp";
+        return;
+      }
+    } else {
+      const IdentifierInfo *Val = Tok.getIdentifierInfo();
+      ConsumeToken();
+      assert(Val);
+      Mode = llvm::StringSwitch<MSVtorDispAttr::Mode>(Val->getName())
+                 .Case("on", MSVtorDispAttr::ForVBaseOverride)
+                 .Case("off", MSVtorDispAttr::Never);
+    }
+  }
+  Actions.ActOnPragmaMSVtorDisp(Kind, PragmaLoc, Mode);
+}
 
 // #pragma GCC visibility comes in two variants:
 //   'push' '(' [visibility] ')'
@@ -291,7 +328,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 +940,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, MSVtorDispAttr::Mode(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,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: 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

Reply via email to