[Modules][PCH] Serialize #pragma pack

This patch serializes the state of #pragma pack. It preserves the state of the
pragma from a PCH/from modules in a file that uses that PCH/those modules.

rdar://21359084

Differential Revision: https://reviews.llvm.org/D31241

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@299226 91177308-0d34-0410-b5e6-96231b3b80d8
(cherry picked from commit 40be91d5ff7b8d289fdb0e9df4de724eb4d1a152)
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index b7e33cc..295c527 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -600,6 +600,9 @@
 
       /// \brief Record code for declarations associated with OpenCL extensions.
       OPENCL_EXTENSION_DECLS = 59,
+
+      /// \brief Record code for \#pragma pack options.
+      PACK_PRAGMA_OPTIONS = 61,
     };
 
     /// \brief Record types used within a source manager block.
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index e174517..a457269 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -811,6 +811,17 @@
   int PragmaMSPointersToMembersState = -1;
   SourceLocation PointersToMembersPragmaLocation;
 
+  /// \brief The pragma pack state.
+  Optional<unsigned> PragmaPackCurrentValue;
+  SourceLocation PragmaPackCurrentLocation;
+  struct PragmaPackStackEntry {
+    unsigned Value;
+    SourceLocation Location;
+    StringRef SlotLabel;
+  };
+  llvm::SmallVector<PragmaPackStackEntry, 2> PragmaPackStack;
+  llvm::SmallVector<std::string, 2> PragmaPackStrings;
+
   /// \brief The OpenCL extension settings.
   OpenCLOptions OpenCLExtensions;
 
diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h
index 57b0a65..ad81241 100644
--- a/include/clang/Serialization/ASTWriter.h
+++ b/include/clang/Serialization/ASTWriter.h
@@ -484,6 +484,7 @@
   void WriteOptimizePragmaOptions(Sema &SemaRef);
   void WriteMSStructPragmaOptions(Sema &SemaRef);
   void WriteMSPointersToMembersPragmaOptions(Sema &SemaRef);
+  void WritePackPragmaOptions(Sema &SemaRef);
   void WriteModuleFileExtension(Sema &SemaRef,
                                 ModuleFileExtensionWriter &Writer);
 
diff --git a/lib/Sema/SemaAttr.cpp b/lib/Sema/SemaAttr.cpp
index bad9e70..a3eddcf 100644
--- a/lib/Sema/SemaAttr.cpp
+++ b/lib/Sema/SemaAttr.cpp
@@ -215,6 +215,7 @@
                                        ValueType Value) {
   if (Action == PSK_Reset) {
     CurrentValue = DefaultValue;
+    CurrentPragmaLocation = PragmaLocation;
     return;
   }
   if (Action & PSK_Push)
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 96acb68..a22f7f4 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -3341,6 +3341,28 @@
       }
       ForceCUDAHostDeviceDepth = Record[0];
       break;
+
+    case PACK_PRAGMA_OPTIONS: {
+      if (Record.size() < 3) {
+        Error("invalid pragma pack record");
+        return Failure;
+      }
+      PragmaPackCurrentValue = Record[0];
+      PragmaPackCurrentLocation = ReadSourceLocation(F, Record[1]);
+      unsigned NumStackEntries = Record[2];
+      unsigned Idx = 3;
+      // Reset the stack when importing a new module.
+      PragmaPackStack.clear();
+      for (unsigned I = 0; I < NumStackEntries; ++I) {
+        PragmaPackStackEntry Entry;
+        Entry.Value = Record[Idx++];
+        Entry.Location = ReadSourceLocation(F, Record[Idx++]);
+        PragmaPackStrings.push_back(ReadString(Record, Idx));
+        Entry.SlotLabel = PragmaPackStrings.back();
+        PragmaPackStack.push_back(Entry);
+      }
+      break;
+    }
     }
   }
 }
@@ -7365,6 +7387,34 @@
         PointersToMembersPragmaLocation);
   }
   SemaObj->ForceCUDAHostDeviceDepth = ForceCUDAHostDeviceDepth;
+
+  if (PragmaPackCurrentValue) {
+    // The bottom of the stack might have a default value. It must be adjusted
+    // to the current value to ensure that the packing state is preserved after
+    // popping entries that were included/imported from a PCH/module.
+    bool DropFirst = false;
+    if (!PragmaPackStack.empty() &&
+        PragmaPackStack.front().Location.isInvalid()) {
+      assert(PragmaPackStack.front().Value == SemaObj->PackStack.DefaultValue &&
+             "Expected a default alignment value");
+      SemaObj->PackStack.Stack.emplace_back(
+          PragmaPackStack.front().SlotLabel, SemaObj->PackStack.CurrentValue,
+          SemaObj->PackStack.CurrentPragmaLocation);
+      DropFirst = true;
+    }
+    for (const auto &Entry :
+         llvm::makeArrayRef(PragmaPackStack).drop_front(DropFirst ? 1 : 0))
+      SemaObj->PackStack.Stack.emplace_back(Entry.SlotLabel, Entry.Value,
+                                            Entry.Location);
+    if (PragmaPackCurrentLocation.isInvalid()) {
+      assert(*PragmaPackCurrentValue == SemaObj->PackStack.DefaultValue &&
+             "Expected a default alignment value");
+      // Keep the current values.
+    } else {
+      SemaObj->PackStack.CurrentValue = *PragmaPackCurrentValue;
+      SemaObj->PackStack.CurrentPragmaLocation = PragmaPackCurrentLocation;
+    }
+  }
 }
 
 IdentifierInfo *ASTReader::get(StringRef Name) {
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 26d35db..df4e58f 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -4125,6 +4125,20 @@
   Stream.EmitRecord(POINTERS_TO_MEMBERS_PRAGMA_OPTIONS, Record);
 }
 
+/// \brief Write the state of 'pragma pack' at the end of the module.
+void ASTWriter::WritePackPragmaOptions(Sema &SemaRef) {
+  RecordData Record;
+  Record.push_back(SemaRef.PackStack.CurrentValue);
+  AddSourceLocation(SemaRef.PackStack.CurrentPragmaLocation, Record);
+  Record.push_back(SemaRef.PackStack.Stack.size());
+  for (const auto &StackEntry : SemaRef.PackStack.Stack) {
+    Record.push_back(StackEntry.Value);
+    AddSourceLocation(StackEntry.PragmaLocation, Record);
+    AddString(StackEntry.StackSlotLabel, Record);
+  }
+  Stream.EmitRecord(PACK_PRAGMA_OPTIONS, Record);
+}
+
 void ASTWriter::WriteModuleFileExtension(Sema &SemaRef,
                                          ModuleFileExtensionWriter &Writer) {
   // Enter the extension block.
@@ -4812,6 +4826,7 @@
     WriteMSStructPragmaOptions(SemaRef);
     WriteMSPointersToMembersPragmaOptions(SemaRef);
   }
+  WritePackPragmaOptions(SemaRef);
 
   // Some simple statistics
   RecordData::value_type Record[] = {
diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map
index 2beb942..dcb60be 100644
--- a/test/Modules/Inputs/module.map
+++ b/test/Modules/Inputs/module.map
@@ -265,6 +265,22 @@
   header "diag_pragma.h"
 }
 
+module pragma_pack_set {
+  header "pragma_pack_set.h"
+}
+
+module pragma_pack_push {
+  header "pragma_pack_push.h"
+}
+
+module pragma_pack_empty {
+  header "empty.h"
+}
+
+module pragma_pack_reset_push {
+  header "pragma_pack_reset_push.h"
+}
+
 module dummy {
   header "dummy.h"
 }
diff --git a/test/Modules/Inputs/pragma_pack_push.h b/test/Modules/Inputs/pragma_pack_push.h
new file mode 100644
index 0000000..4fc66e5
--- /dev/null
+++ b/test/Modules/Inputs/pragma_pack_push.h
@@ -0,0 +1,6 @@
+
+#pragma pack (push, 4)
+#pragma pack (push, 2)
+#pragma pack (push, 1)
+#pragma pack (pop)
+
diff --git a/test/Modules/Inputs/pragma_pack_reset_push.h b/test/Modules/Inputs/pragma_pack_reset_push.h
new file mode 100644
index 0000000..18f50a0
--- /dev/null
+++ b/test/Modules/Inputs/pragma_pack_reset_push.h
@@ -0,0 +1,4 @@
+
+#pragma pack ()
+#pragma pack (push, 4)
+
diff --git a/test/Modules/Inputs/pragma_pack_set.h b/test/Modules/Inputs/pragma_pack_set.h
new file mode 100644
index 0000000..b123c11
--- /dev/null
+++ b/test/Modules/Inputs/pragma_pack_set.h
@@ -0,0 +1,3 @@
+
+#pragma pack (1)
+
diff --git a/test/Modules/pragma-pack.c b/test/Modules/pragma-pack.c
new file mode 100644
index 0000000..9497384
--- /dev/null
+++ b/test/Modules/pragma-pack.c
@@ -0,0 +1,35 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fmodules -fimplicit-module-maps -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=pragma_pack_set %S/Inputs/module.map
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fmodules -fimplicit-module-maps -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=pragma_pack_push %S/Inputs/module.map
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fmodules -fimplicit-module-maps -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=pragma_pack_empty %S/Inputs/module.map
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fmodules -fimplicit-module-maps -x objective-c -emit-module -fmodules-cache-path=%t -fmodule-name=pragma_pack_reset_push %S/Inputs/module.map
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fmodules -fimplicit-module-maps -x objective-c -verify -fmodules-cache-path=%t -I %S/Inputs %s
+// FIXME: When we have a syntax for modules in C, use that.
+
+@import pragma_pack_set;
+
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 1}}
+#pragma pack (pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+
+@import pragma_pack_push;
+
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 2}}
+#pragma pack (pop)
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 4}}
+#pragma pack (pop)
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 1}}
+#pragma pack (pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+
+#pragma pack (16)
+
+@import pragma_pack_empty;
+
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 16}}
+#pragma pack (pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+
+@import pragma_pack_reset_push;
+
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 4}}
+#pragma pack (pop)
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 8}}
+
diff --git a/test/PCH/pragma-pack.c b/test/PCH/pragma-pack.c
new file mode 100644
index 0000000..47a5570
--- /dev/null
+++ b/test/PCH/pragma-pack.c
@@ -0,0 +1,90 @@
+// Test this without pch.
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -include %s -verify -fsyntax-only -DSET
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -include %s -verify -fsyntax-only -DRESET
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -include %s -verify -fsyntax-only -DPUSH
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -include %s -verify -fsyntax-only -DPUSH_POP
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -include %s -verify -fsyntax-only -DPUSH_POP_LABEL
+
+// Test with pch.
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DSET -emit-pch -o %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DSET -verify -include-pch %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DRESET -emit-pch -o %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DRESET -verify -include-pch %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DPUSH -emit-pch -o %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DPUSH -verify -include-pch %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DPUSH_POP -emit-pch -o %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DPUSH_POP -verify -include-pch %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DPUSH_POP_LABEL -emit-pch -o %t
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -DPUSH_POP_LABEL -verify -include-pch %t
+
+#ifndef HEADER
+#define HEADER
+
+#ifdef SET
+#pragma pack(1)
+#endif
+
+#ifdef RESET
+#pragma pack(2)
+#pragma pack ()
+#endif
+
+#ifdef PUSH
+#pragma pack(1)
+#pragma pack (push, 2)
+#endif
+
+#ifdef PUSH_POP
+#pragma pack (push, 4)
+#pragma pack (push, 2)
+#pragma pack (pop)
+#endif
+
+#ifdef PUSH_POP_LABEL
+#pragma pack (push, a, 4)
+#pragma pack (push, b, 1)
+#pragma pack (push, c, 2)
+#pragma pack (pop, b)
+#endif
+
+#else
+
+#ifdef SET
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 1}}
+#pragma pack(pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+#endif
+
+#ifdef RESET
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}}
+#pragma ()
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}}
+#endif
+
+#ifdef PUSH
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 2}}
+#pragma pack(pop)
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 1}}
+#pragma pack ()
+#pragma pack (show) // expected-warning {{value of #pragma pack(show) == 8}}
+#pragma pack(pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+#endif
+
+#ifdef PUSH_POP
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 4}}
+#pragma pack(pop)
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}}
+#pragma pack(pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+#endif
+
+#ifdef PUSH_POP_LABEL
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 4}}
+#pragma pack(pop, c)
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 4}}
+#pragma pack(pop, a)
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}}
+#pragma pack(pop)  // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+#pragma pack(pop, b) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}}
+#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}}
+#endif
+
+#endif