Add Sema implementation of #pragma pack stack. 
 - Follows the MSVC (original) implementation, including support of
   pack(show) (useful for testing).
 - Implements support for named pack records which gcc seems to
   ignore (or implements incorrectly).
 - Not currently wired to anything, only functionality change is the
   type checking of the pragma.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@57476 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 6ee4ae1..fee732f 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -25,6 +25,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/HeaderSearch.h" 
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringExtras.h"
 using namespace clang;
 
 Sema::TypeTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S) {
@@ -2559,3 +2560,97 @@
   // FIXME: Add all the various semantics of linkage specifications
   return LinkageSpecDecl::Create(Context, Loc, Language, dcl);
 }
+
+void Sema::ActOnPragmaPack(PragmaPackKind Kind, IdentifierInfo *Name, 
+                           ExprTy *alignment, SourceLocation PragmaLoc, 
+                           SourceLocation LParenLoc, SourceLocation RParenLoc) {
+  Expr *Alignment = static_cast<Expr *>(alignment);
+
+  // If specified then alignment must be a "small" power of two.
+  unsigned AlignmentVal = 0;
+  if (Alignment) {
+    llvm::APSInt Val;
+    if (!Alignment->isIntegerConstantExpr(Val, Context) ||
+        !Val.isPowerOf2() ||
+        Val.getZExtValue() > 16) {
+      Diag(PragmaLoc, diag::warn_pragma_pack_invalid_alignment);
+      delete Alignment;
+      return; // Ignore
+    }
+
+    AlignmentVal = (unsigned) Val.getZExtValue();
+  }
+
+  switch (Kind) {
+  case Action::PPK_Default: // pack([n])
+    PackContext.setAlignment(AlignmentVal);
+    break;
+
+  case Action::PPK_Show: // pack(show)
+    // Show the current alignment, making sure to show the right value
+    // for the default.
+    AlignmentVal = PackContext.getAlignment();
+    // FIXME: This should come from the target.
+    if (AlignmentVal == 0)
+      AlignmentVal = 8;
+    Diag(PragmaLoc, diag::warn_pragma_pack_show, llvm::utostr(AlignmentVal));
+    break;
+
+  case Action::PPK_Push: // pack(push [, id] [, [n])
+    PackContext.push(Name);
+    // Set the new alignment if specified.
+    if (Alignment)
+      PackContext.setAlignment(AlignmentVal);    
+    break;
+
+  case Action::PPK_Pop: // pack(pop [, id] [,  n])
+    // MSDN, C/C++ Preprocessor Reference > Pragma Directives > pack:
+    // "#pragma pack(pop, identifier, n) is undefined"
+    if (Alignment && Name)
+      Diag(PragmaLoc, diag::warn_pragma_pack_pop_identifer_and_alignment);    
+    
+    // Do the pop.
+    if (!PackContext.pop(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");
+
+      // FIXME: Warn about popping named records as MSVC does.
+    } else {
+      // Pop succeeded, set the new alignment if specified.
+      if (Alignment)
+        PackContext.setAlignment(AlignmentVal);
+    }
+    break;
+
+  default:
+    assert(0 && "Invalid #pragma pack kind.");
+  }
+}
+
+bool PragmaPackStack::pop(IdentifierInfo *Name) {
+  if (Stack.empty())
+    return false;
+
+  // If name is empty just pop top.
+  if (!Name) {
+    Alignment = Stack.back().first;
+    Stack.pop_back();
+    return true;
+  } 
+
+  // Otherwise, find the named record.
+  for (unsigned i = Stack.size(); i != 0; ) {
+    --i;
+    if (strcmp(Stack[i].second.c_str(), Name->getName()) == 0) {
+      // Found it, pop up to and including this record.
+      Alignment = Stack[i].first;
+      Stack.erase(Stack.begin() + i, Stack.end());
+      return true;
+    }
+  }
+
+  return false;
+}