[clang] Added partial support for compiling C++20 modules and header-units without scanning. (PR #147682)

Hassan Sajjad via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 13 10:11:01 PDT 2025


https://github.com/HassanSajjad-302 updated https://github.com/llvm/llvm-project/pull/147682

>From b9b57a4f631df8cb09d16a7b72783f291ffb1be9 Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Mon, 25 Aug 2025 11:45:31 +0500
Subject: [PATCH 01/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/include/clang/Driver/Options.td         |    4 +
 .../include/clang/Frontend/CompilerInstance.h |    2 +
 .../clang/IPC2978/.clang-format-ignore        |    1 +
 clang/include/clang/IPC2978/IPCManagerBS.hpp  |   39 +
 .../clang/IPC2978/IPCManagerCompiler.hpp      |  146 +
 clang/include/clang/IPC2978/Manager.hpp       |  135 +
 clang/include/clang/IPC2978/Messages.hpp      |  120 +
 clang/include/clang/IPC2978/expected.hpp      | 2444 +++++++++++++++++
 clang/include/clang/IPC2978/rapidhash.h       |  574 ++++
 clang/include/clang/Lex/HeaderSearchOptions.h |    5 +
 clang/include/clang/Lex/ModuleLoader.h        |    8 +
 clang/lib/CMakeLists.txt                      |    1 +
 clang/lib/Driver/ToolChains/Clang.cpp         |    5 +
 clang/lib/Frontend/CompilerInstance.cpp       |   46 +
 clang/lib/Frontend/CompilerInvocation.cpp     |    5 +
 clang/lib/Frontend/FrontendAction.cpp         |   11 +
 clang/lib/IPC2978/.clang-format-ignore        |    1 +
 clang/lib/IPC2978/CMakeLists.txt              |    6 +
 clang/lib/IPC2978/IPCManagerBS.cpp            |  341 +++
 clang/lib/IPC2978/IPCManagerCompiler.cpp      |  359 +++
 clang/lib/IPC2978/Manager.cpp                 |  458 +++
 clang/lib/IPC2978/setup.py                    |   58 +
 clang/lib/Lex/CMakeLists.txt                  |    1 +
 clang/lib/Lex/PPDirectives.cpp                |   54 +-
 clang/lib/Parse/ParseAST.cpp                  |   16 +
 clang/unittests/CMakeLists.txt                |    5 +
 clang/unittests/IPC2978/.clang-format-ignore  |    1 +
 clang/unittests/IPC2978/CMakeLists.txt        |    5 +
 clang/unittests/IPC2978/IPC2978Test.cpp       | 1068 +++++++
 29 files changed, 5910 insertions(+), 9 deletions(-)
 create mode 100644 clang/include/clang/IPC2978/.clang-format-ignore
 create mode 100644 clang/include/clang/IPC2978/IPCManagerBS.hpp
 create mode 100644 clang/include/clang/IPC2978/IPCManagerCompiler.hpp
 create mode 100644 clang/include/clang/IPC2978/Manager.hpp
 create mode 100644 clang/include/clang/IPC2978/Messages.hpp
 create mode 100644 clang/include/clang/IPC2978/expected.hpp
 create mode 100644 clang/include/clang/IPC2978/rapidhash.h
 create mode 100644 clang/lib/IPC2978/.clang-format-ignore
 create mode 100644 clang/lib/IPC2978/CMakeLists.txt
 create mode 100644 clang/lib/IPC2978/IPCManagerBS.cpp
 create mode 100644 clang/lib/IPC2978/IPCManagerCompiler.cpp
 create mode 100644 clang/lib/IPC2978/Manager.cpp
 create mode 100644 clang/lib/IPC2978/setup.py
 create mode 100644 clang/unittests/IPC2978/.clang-format-ignore
 create mode 100644 clang/unittests/IPC2978/CMakeLists.txt
 create mode 100644 clang/unittests/IPC2978/IPC2978Test.cpp

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6a46fec1701f3..439926a8b0d23 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9492,3 +9492,7 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
   Group<m_Group>,
   HelpText<"Enable the wasm-opt optimizer (default)">,
   MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+
+def no_scan_ipc : Flag<["-"], "noScanIPC">,
+                  Visibility<[ClangOption, CC1Option]>,
+                  HelpText<"Enable No scan IPC approach">;
\ No newline at end of file
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 64ebb70a6a24c..f4c9843edff5d 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -915,6 +915,8 @@ class CompilerInstance : public ModuleLoader {
 
   bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override;
 
+  Module *loadIPCReceivedHeaderUnit(StringRef FileName) override;
+
   void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) {
     DependencyCollectors.push_back(std::move(Listener));
   }
diff --git a/clang/include/clang/IPC2978/.clang-format-ignore b/clang/include/clang/IPC2978/.clang-format-ignore
new file mode 100644
index 0000000000000..f59ec20aabf58
--- /dev/null
+++ b/clang/include/clang/IPC2978/.clang-format-ignore
@@ -0,0 +1 @@
+*
\ No newline at end of file
diff --git a/clang/include/clang/IPC2978/IPCManagerBS.hpp b/clang/include/clang/IPC2978/IPCManagerBS.hpp
new file mode 100644
index 0000000000000..f9222a55f3fc4
--- /dev/null
+++ b/clang/include/clang/IPC2978/IPCManagerBS.hpp
@@ -0,0 +1,39 @@
+
+#ifndef IPC_MANAGER_BS_HPP
+#define IPC_MANAGER_BS_HPP
+
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/Messages.hpp"
+
+namespace N2978
+{
+
+// IPC Manager BuildSystem
+class IPCManagerBS : Manager
+{
+    friend tl::expected<IPCManagerBS, string> makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath);
+    bool connectedToCompiler = false;
+
+#ifdef _WIN32
+    explicit IPCManagerBS(void *hPipe_);
+#else
+    explicit IPCManagerBS(int fdSocket_);
+#endif
+
+  public:
+    IPCManagerBS(const IPCManagerBS &) = default;
+    IPCManagerBS &operator=(const IPCManagerBS &) = default;
+    IPCManagerBS(IPCManagerBS &&) = default;
+    IPCManagerBS &operator=(IPCManagerBS &&) = default;
+    tl::expected<void, string> receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const;
+    [[nodiscard]] tl::expected<void, string> sendMessage(const BTCModule &moduleFile) const;
+    [[nodiscard]] tl::expected<void, string> sendMessage(const BTCNonModule &nonModule) const;
+    [[nodiscard]] tl::expected<void, string> sendMessage(const BTCLastMessage &lastMessage) const;
+    static tl::expected<ProcessMappingOfBMIFile, string> createSharedMemoryBMIFile(const BMIFile &bmiFile);
+    static tl::expected<void, string> closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile);
+    void closeConnection() const;
+};
+
+tl::expected<IPCManagerBS, string> makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath);
+} // namespace N2978
+#endif // IPC_MANAGER_BS_HPP
diff --git a/clang/include/clang/IPC2978/IPCManagerCompiler.hpp b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
new file mode 100644
index 0000000000000..d58a118849c2a
--- /dev/null
+++ b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
@@ -0,0 +1,146 @@
+
+#ifndef IPC_MANAGER_COMPILER_HPP
+#define IPC_MANAGER_COMPILER_HPP
+
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/expected.hpp"
+
+using std::string_view;
+namespace N2978
+{
+
+// IPC Manager Compiler
+class IPCManagerCompiler : Manager
+{
+    template <typename T> tl::expected<T, string> receiveMessage() const;
+    // This is not exposed. sendCTBLastMessage calls this.
+    [[nodiscard]] tl::expected<void, string> receiveBTCLastMessage() const;
+
+  public:
+    CTBLastMessage lastMessage{};
+#ifdef _WIN32
+    explicit IPCManagerCompiler(void *hPipe_);
+#else
+    explicit IPCManagerCompiler(int fdSocket_);
+#endif
+    [[nodiscard]] tl::expected<BTCModule, string> receiveBTCModule(const CTBModule &moduleName) const;
+    [[nodiscard]] tl::expected<BTCNonModule, string> receiveBTCNonModule(const CTBNonModule &nonModule) const;
+    [[nodiscard]] tl::expected<void, string> sendCTBLastMessage(const CTBLastMessage &lastMessage) const;
+    [[nodiscard]] tl::expected<void, string> sendCTBLastMessage(const CTBLastMessage &lastMessage,
+                                                                const string &bmiFile, const string &filePath) const;
+    static tl::expected<ProcessMappingOfBMIFile, string> readSharedMemoryBMIFile(const BMIFile &file);
+    static tl::expected<void, string> closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile);
+    void closeConnection() const;
+};
+
+template <typename T> tl::expected<T, string> IPCManagerCompiler::receiveMessage() const
+{
+    // Read from the pipe.
+    char buffer[BUFFERSIZE];
+    uint32_t bytesRead;
+    if (const auto &r = readInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    else
+    {
+        bytesRead = *r;
+    }
+
+    uint32_t bytesProcessed = 0;
+
+    if constexpr (std::is_same_v<T, BTCModule>)
+    {
+        const auto &r = readProcessMappingOfBMIFileFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+
+        const auto &r2 = readVectorOfModuleDepFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        BTCModule moduleFile;
+        moduleFile.requested = *r;
+        moduleFile.deps = *r2;
+        if (bytesRead == bytesProcessed)
+        {
+            return moduleFile;
+        }
+    }
+    else if constexpr (std::is_same_v<T, BTCNonModule>)
+    {
+        const auto &r = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+
+        const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        const auto &r3 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r3)
+        {
+            return tl::unexpected(r3.error());
+        }
+
+        const auto &r4 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r4)
+        {
+            return tl::unexpected(r4.error());
+        }
+
+        const auto &r5 = readVectorOfHuDepFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r5)
+        {
+            return tl::unexpected(r5.error());
+        }
+
+        BTCNonModule nonModule;
+        nonModule.isHeaderUnit = *r;
+        nonModule.filePath = *r2;
+        nonModule.user = *r3;
+        nonModule.fileSize = *r4;
+        nonModule.deps = *r5;
+
+        if (bytesRead == bytesProcessed)
+        {
+            return nonModule;
+        }
+    }
+    else
+    {
+        static_assert(false && "Unknown type\n");
+    }
+
+    if (bytesRead != bytesProcessed)
+    {
+        return tl::unexpected(getErrorString(bytesRead, bytesProcessed));
+    }
+    string str = __FILE__;
+    str += ':';
+    str += std::to_string(__LINE__);
+    return tl::unexpected(getErrorString("N2978 IPC API internal error" + str));
+}
+[[nodiscard]] tl::expected<IPCManagerCompiler, string> makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath);
+inline IPCManagerCompiler *managerCompiler;
+inline CTBLastMessage lastMessage;
+
+// Equality operator for use in unordered_map
+bool operator==(const CTBNonModule &lhs, const CTBNonModule &rhs);
+// Hash function for CTBNonModule
+struct CTBNonModuleHash
+{
+    uint64_t operator()(const CTBNonModule &ctb) const;
+};
+
+inline std::unordered_map<CTBNonModule, BTCNonModule, CTBNonModuleHash> respnses;
+} // namespace N2978
+#endif // IPC_MANAGER_COMPILER_HPP
diff --git a/clang/include/clang/IPC2978/Manager.hpp b/clang/include/clang/IPC2978/Manager.hpp
new file mode 100644
index 0000000000000..580e1878b3ef3
--- /dev/null
+++ b/clang/include/clang/IPC2978/Manager.hpp
@@ -0,0 +1,135 @@
+
+#ifndef MANAGER_HPP
+#define MANAGER_HPP
+
+#include "clang/IPC2978/Messages.hpp"
+#include "clang/IPC2978/expected.hpp"
+
+#include <string>
+#include <vector>
+
+using std::string, std::vector, std::string_view;
+
+#define BUFFERSIZE 4096
+
+#ifdef _WIN32
+// The following variable is used in CreateNamedFunction.
+#define PIPE_TIMEOUT 5000
+#endif
+
+namespace tl
+{
+template <typename T, typename U> class expected;
+}
+
+namespace N2978
+{
+
+enum class ErrorCategory : uint8_t
+{
+    NONE,
+
+    // error-category for API errors
+    READ_FILE_ZERO_BYTES_READ,
+    INCORRECT_BTC_LAST_MESSAGE,
+    UNKNOWN_CTB_TYPE,
+};
+
+string getErrorString();
+string getErrorString(uint32_t bytesRead_, uint32_t bytesProcessed_);
+string getErrorString(ErrorCategory errorCategory_);
+// to facilitate error propagation.
+inline string getErrorString(string err)
+{
+    return err;
+}
+
+struct ProcessMappingOfBMIFile
+{
+    string_view file;
+#ifdef _WIN32
+    void *mapping;
+    void *view;
+#else
+    void *mapping;
+    uint32_t mappingSize;
+#endif
+};
+
+class Manager
+{
+  public:
+#ifdef _WIN32
+    void *hPipe = nullptr;
+#else
+    int fdSocket = 0;
+#endif
+
+    tl::expected<uint32_t, string> readInternal(char (&buffer)[BUFFERSIZE]) const;
+    tl::expected<void, string> writeInternal(const vector<char> &buffer) const;
+
+    static vector<char> getBufferWithType(CTB type);
+    static void writeUInt32(vector<char> &buffer, uint32_t value);
+    static void writeString(vector<char> &buffer, const string &str);
+    static void writeProcessMappingOfBMIFile(vector<char> &buffer, const BMIFile &file);
+    static void writeModuleDep(vector<char> &buffer, const ModuleDep &dep);
+    static void writeHuDep(vector<char> &buffer, const HuDep &dep);
+    static void writeVectorOfStrings(vector<char> &buffer, const vector<string> &strs);
+    static void writeVectorOfProcessMappingOfBMIFiles(vector<char> &buffer, const vector<BMIFile> &files);
+    static void writeVectorOfModuleDep(vector<char> &buffer, const vector<ModuleDep> &deps);
+    static void writeVectorOfHuDep(vector<char> &buffer, const vector<HuDep> &deps);
+
+    tl::expected<bool, string> readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                uint32_t &bytesProcessed) const;
+    tl::expected<uint32_t, string> readUInt32FromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                      uint32_t &bytesProcessed) const;
+    tl::expected<string, string> readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                    uint32_t &bytesProcessed) const;
+    tl::expected<BMIFile, string> readProcessMappingOfBMIFileFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                                      uint32_t &bytesProcessed) const;
+    tl::expected<vector<string>, string> readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                                    uint32_t &bytesProcessed) const;
+    tl::expected<ModuleDep, string> readModuleDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                          uint32_t &bytesProcessed) const;
+    tl::expected<vector<ModuleDep>, string> readVectorOfModuleDepFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                          uint32_t &bytesRead,
+                                                                          uint32_t &bytesProcessed) const;
+    tl::expected<HuDep, string> readHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                  uint32_t &bytesProcessed) const;
+    tl::expected<vector<HuDep>, string> readVectorOfHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                                  uint32_t &bytesProcessed) const;
+    tl::expected<void, string> readNumberOfBytes(char *output, uint32_t size, char (&buffer)[BUFFERSIZE],
+                                                 uint32_t &bytesRead, uint32_t &bytesProcessed) const;
+};
+
+template <typename T, typename... Args> constexpr T *construct_at(T *p, Args &&...args)
+{
+    return ::new (static_cast<void *>(p)) T(std::forward<Args>(args)...);
+}
+
+template <typename T> T &getInitializedObjectFromBuffer(char (&buffer)[320])
+{
+    T &t = reinterpret_cast<T &>(buffer);
+    construct_at(&t);
+    return t;
+}
+
+inline std::string to16charHexString(const uint64_t v)
+{
+    static auto lut = "0123456789abcdef";
+    std::string out;
+    out.resize(16);
+    for (int i = 0; i < 8; ++i)
+    {
+        // extract byte in big-endian order:
+        const auto byte = static_cast<uint8_t>(v >> ((7 - i) * 8));
+        // high nibble:
+        out[2 * i] = lut[byte >> 4];
+        // low nibble:
+        out[2 * i + 1] = lut[byte & 0xF];
+    }
+    return out;
+}
+
+} // namespace N2978
+#endif // MANAGER_HPP
diff --git a/clang/include/clang/IPC2978/Messages.hpp b/clang/include/clang/IPC2978/Messages.hpp
new file mode 100644
index 0000000000000..ca27679120095
--- /dev/null
+++ b/clang/include/clang/IPC2978/Messages.hpp
@@ -0,0 +1,120 @@
+#ifndef MESSAGES_HPP
+#define MESSAGES_HPP
+
+#include <cstdint>
+#include <signal.h>
+#include <string>
+#include <vector>
+
+using std::string, std::vector;
+
+namespace N2978
+{
+
+// CTB --> Compiler to Build-System
+// BTC --> Build-System to Compiler
+
+// string is 4 bytes that hold the size of the char array, followed by the array.
+// vector is 4 bytes that hold the size of the array, followed by the array.
+// All fields are sent in declaration order, even if meaningless.
+
+// Compiler to Build System
+// This is the first byte of the compiler to build-system message.
+enum class CTB : uint8_t
+{
+    MODULE = 0,
+    NON_MODULE = 1,
+    LAST_MESSAGE = 2,
+};
+
+// This is sent when the compiler needs a module.
+struct CTBModule
+{
+    string moduleName;
+};
+
+// This is sent when the compiler needs something else than a module.
+// isHeaderUnit is set when the compiler knows that it is a header-unit.
+struct CTBNonModule
+{
+    bool isHeaderUnit = false;
+    string logicalName;
+};
+
+// This is the last message sent by the compiler.
+struct CTBLastMessage
+{
+    // Whether the compilation succeeded or failed.
+    bool errorOccurred = false;
+    // Following fields are meaningless if the compilation failed.
+    // compiler output
+    string output;
+    // compiler error output.
+    // Any IPC related error output should be reported on stderr.
+    string errorOutput;
+    // exported module name if any.
+    string logicalName;
+    // This is communicated because the receiving process has no
+    // way to learn the shared memory file size on both Windows
+    // and Linux without a filesystem call.
+    // Meaningless if the compilation does not produce BMI.
+    uint32_t fileSize = UINT32_MAX;
+};
+
+// Build System to Compiler
+// Unlike CTB, this is not written as the first byte
+// since the compiler knows what message it will receive.
+enum class BTC : uint8_t
+{
+    MODULE = 0,
+    NON_MODULE = 1,
+    LAST_MESSAGE = 2,
+};
+
+struct BMIFile
+{
+    string filePath;
+    uint32_t fileSize = UINT32_MAX;
+};
+
+struct ModuleDep
+{
+    BMIFile file;
+    string logicalName;
+    bool isHeaderUnit;
+};
+
+// Reply for CTBModule
+struct BTCModule
+{
+    BMIFile requested;
+    vector<ModuleDep> deps;
+};
+
+struct HuDep
+{
+    BMIFile file;
+    string logicalName;
+    // whether header-unit / header-file belongs to user or system directory.
+    bool user = true;
+};
+
+// Reply for CTBNonModule
+struct BTCNonModule
+{
+    bool isHeaderUnit = false;
+    string filePath;
+    // if isHeaderUnit == false, the following three are meaning-less.
+    // whether header-unit / header-file belongs to user or system directory.
+    bool user = true;
+    // if isHeaderUnit == true, fileSize of the requested file.
+    uint32_t fileSize;
+    vector<HuDep> deps;
+};
+
+// Reply for CTBLastMessage if the compilation succeeded.
+struct BTCLastMessage
+{
+};
+} // namespace N2978
+#endif // MESSAGES_HPP
diff --git a/clang/include/clang/IPC2978/expected.hpp b/clang/include/clang/IPC2978/expected.hpp
new file mode 100644
index 0000000000000..1f92b6b3cd85a
--- /dev/null
+++ b/clang/include/clang/IPC2978/expected.hpp
@@ -0,0 +1,2444 @@
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Sy Brand (tartanllama at gmail.com, @TartanLlama)
+//
+// Documentation available at http://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 1
+#define TL_EXPECTED_VERSION_MINOR 1
+#define TL_EXPECTED_VERSION_PATCH 0
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+#define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define TL_EXPECTED_MSVC2015
+#define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
+     !defined(__clang__))
+#define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 &&              \
+     !defined(__clang__))
+#define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 &&              \
+     !defined(__clang__))
+#define TL_EXPECTED_GCC55
+#endif
+
+#if !defined(TL_ASSERT)
+//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
+#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
+#include <cassert>
+#define TL_ASSERT(x) assert(x)
+#else
+#define TL_ASSERT(x)
+#endif
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
+     !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+
+#define TL_EXPECTED_NO_CONSTRR
+// GCC < 5 doesn't support some standard C++11 type traits
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
+  std::has_trivial_copy_constructor<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
+  std::has_trivial_copy_assign<T>
+
+// This one will be different for GCC 5.7 if it's ever supported
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
+  std::is_trivially_destructible<T>
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
+// std::vector for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+namespace detail {
+template <class T>
+struct is_trivially_copy_constructible
+    : std::is_trivially_copy_constructible<T> {};
+#ifdef _GLIBCXX_VECTOR
+template <class T, class A>
+struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
+#endif
+} // namespace detail
+} // namespace tl
+#endif
+
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
+  tl::detail::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
+  std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
+  std::is_trivially_destructible<T>
+#else
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
+  std::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
+  std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
+  std::is_trivially_destructible<T>
+#endif
+
+#if __cplusplus > 201103L
+#define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+#define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) ||                \
+     defined(TL_EXPECTED_GCC49))
+#define TL_EXPECTED_11_CONSTEXPR
+#else
+#define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+template <class T, class E> class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+class monostate {};
+
+struct in_place_t {
+  explicit in_place_t() = default;
+};
+static constexpr in_place_t in_place{};
+#endif
+
+template <class E> class unexpected {
+public:
+  static_assert(!std::is_same<E, void>::value, "E must not be void");
+
+  unexpected() = delete;
+  constexpr explicit unexpected(const E &e) : m_val(e) {}
+
+  constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
+
+  template <class... Args, typename std::enable_if<std::is_constructible<
+                               E, Args &&...>::value>::type * = nullptr>
+  constexpr explicit unexpected(Args &&...args)
+      : m_val(std::forward<Args>(args)...) {}
+  template <
+      class U, class... Args,
+      typename std::enable_if<std::is_constructible<
+          E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
+  constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  constexpr const E &value() const & { return m_val; }
+  TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
+  TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
+  constexpr const E &&value() const && { return std::move(m_val); }
+
+private:
+  E m_val;
+};
+
+#ifdef __cpp_deduction_guides
+template <class E> unexpected(E) -> unexpected<E>;
+#endif
+
+template <class E>
+constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() == rhs.value();
+}
+template <class E>
+constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() != rhs.value();
+}
+template <class E>
+constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() < rhs.value();
+}
+template <class E>
+constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() <= rhs.value();
+}
+template <class E>
+constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() > rhs.value();
+}
+template <class E>
+constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() >= rhs.value();
+}
+
+template <class E>
+unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
+  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
+}
+
+struct unexpect_t {
+  unexpect_t() = default;
+};
+static constexpr unexpect_t unexpect{};
+
+namespace detail {
+template <typename E>
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+  throw std::forward<E>(e);
+#else
+  (void)e;
+#ifdef _MSC_VER
+  __assume(0);
+#else
+  __builtin_unreachable();
+#endif
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template <class T> using remove_const_t = typename std::remove_const<T>::type;
+template <class T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <class T> using decay_t = typename std::decay<T>::type;
+template <bool E, class T = void>
+using enable_if_t = typename std::enable_if<E, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+// std::conjunction from C++17
+template <class...> struct conjunction : std::true_type {};
+template <class B> struct conjunction<B> : B {};
+template <class B, class... Bs>
+struct conjunction<B, Bs...>
+    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+// In C++11 mode, there's an issue in libc++'s std::mem_fn
+// which results in a hard-error when using it in a noexcept expression
+// in some cases. This is a check to workaround the common failing case.
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template <class T>
+struct is_pointer_to_non_const_member_func : std::false_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
+    : std::true_type {};
+
+template <class T> struct is_const_or_const_ref : std::false_type {};
+template <class T> struct is_const_or_const_ref<T const &> : std::true_type {};
+template <class T> struct is_const_or_const_ref<T const> : std::true_type {};
+#endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template <
+    typename Fn, typename... Args,
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
+                             is_const_or_const_ref<Args...>::value)>,
+#endif
+    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+  return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Fn, typename... Args,
+          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+  return std::forward<Fn>(f)(std::forward<Args>(args)...);
+}
+
+// std::invoke_result from C++17
+template <class F, class, class... Us> struct invoke_result_impl;
+
+template <class F, class... Us>
+struct invoke_result_impl<
+    F,
+    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
+    Us...> {
+  using type =
+      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+};
+
+template <class F, class... Us>
+using invoke_result = invoke_result_impl<F, void, Us...>;
+
+template <class F, class... Us>
+using invoke_result_t = typename invoke_result<F, Us...>::type;
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template <class T, class U = T> struct is_swappable : std::true_type {};
+
+template <class T, class U = T> struct is_nothrow_swappable : std::true_type {};
+#else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests {
+// if swap ADL finds this then it would call std::swap otherwise (same
+// signature)
+struct tag {};
+
+template <class T> tag swap(T &, T &);
+template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
+
+// helper functions to test if an unqualified swap is possible, and if it
+// becomes std::swap
+template <class, class> std::false_type can_swap(...) noexcept(false);
+template <class T, class U,
+          class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
+std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
+                                                    std::declval<U &>())));
+
+template <class, class> std::false_type uses_std(...);
+template <class T, class U>
+std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
+uses_std(int);
+
+template <class T>
+struct is_std_swap_noexcept
+    : std::integral_constant<bool,
+                             std::is_nothrow_move_constructible<T>::value &&
+                                 std::is_nothrow_move_assignable<T>::value> {};
+
+template <class T, std::size_t N>
+struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
+
+template <class T, class U>
+struct is_adl_swap_noexcept
+    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
+} // namespace swap_adl_tests
+
+template <class T, class U = T>
+struct is_swappable
+    : std::integral_constant<
+          bool,
+          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
+              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
+               (std::is_move_assignable<T>::value &&
+                std::is_move_constructible<T>::value))> {};
+
+template <class T, std::size_t N>
+struct is_swappable<T[N], T[N]>
+    : std::integral_constant<
+          bool,
+          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
+              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
+                   0))::value ||
+               is_swappable<T, T>::value)> {};
+
+template <class T, class U = T>
+struct is_nothrow_swappable
+    : std::integral_constant<
+          bool,
+          is_swappable<T, U>::value &&
+              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
+               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
+#endif
+#endif
+
+// Trait for checking if a type is a tl::expected
+template <class T> struct is_expected_impl : std::false_type {};
+template <class T, class E>
+struct is_expected_impl<expected<T, E>> : std::true_type {};
+template <class T> using is_expected = is_expected_impl<decay_t<T>>;
+
+template <class T, class E, class U>
+using expected_enable_forward_value = detail::enable_if_t<
+    std::is_constructible<T, U &&>::value &&
+    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
+    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
+
+template <class T, class E, class U, class G, class UR, class GR>
+using expected_enable_from_other = detail::enable_if_t<
+    std::is_constructible<T, UR>::value &&
+    std::is_constructible<E, GR>::value &&
+    !std::is_constructible<T, expected<U, G> &>::value &&
+    !std::is_constructible<T, expected<U, G> &&>::value &&
+    !std::is_constructible<T, const expected<U, G> &>::value &&
+    !std::is_constructible<T, const expected<U, G> &&>::value &&
+    !std::is_convertible<expected<U, G> &, T>::value &&
+    !std::is_convertible<expected<U, G> &&, T>::value &&
+    !std::is_convertible<const expected<U, G> &, T>::value &&
+    !std::is_convertible<const expected<U, G> &&, T>::value>;
+
+template <class T, class U>
+using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
+
+template <class T>
+using is_copy_constructible_or_void =
+    is_void_or<T, std::is_copy_constructible<T>>;
+
+template <class T>
+using is_move_constructible_or_void =
+    is_void_or<T, std::is_move_constructible<T>>;
+
+template <class T>
+using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
+
+template <class T>
+using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
+
+} // namespace detail
+
+namespace detail {
+struct no_init_t {};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template <class T, class E, bool = std::is_trivially_destructible<T>::value,
+          bool = std::is_trivially_destructible<E>::value>
+struct expected_storage_base {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (m_has_val) {
+      m_val.~T();
+    } else {
+      m_unexpect.~unexpected<E>();
+    }
+  }
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template <class T, class E> struct expected_storage_base<T, E, true, true> {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() = default;
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// T is trivial, E is not.
+template <class T, class E> struct expected_storage_base<T, E, true, false> {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
+      : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (!m_has_val) {
+      m_unexpect.~unexpected<E>();
+    }
+  }
+
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// E is trivial, T is not.
+template <class T, class E> struct expected_storage_base<T, E, false, true> {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (m_has_val) {
+      m_val.~T();
+    }
+  }
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, true> {
+  #if __GNUC__ <= 5
+  //no constexpr for GCC 4/5 bug
+  #else
+  TL_EXPECTED_MSVC2015_CONSTEXPR
+  #endif
+  expected_storage_base() : m_has_val(true) {}
+
+  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() = default;
+  struct dummy {};
+  union {
+    unexpected<E> m_unexpect;
+    dummy m_val;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, false> {
+  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (!m_has_val) {
+      m_unexpect.~unexpected<E>();
+    }
+  }
+
+  union {
+    unexpected<E> m_unexpect;
+    char m_dummy;
+  };
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct expected_operations_base : expected_storage_base<T, E> {
+  using expected_storage_base<T, E>::expected_storage_base;
+
+  template <class... Args> void construct(Args &&...args) noexcept {
+    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
+    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args> void construct_error(Args &&...args) noexcept {
+    new (std::addressof(this->m_unexpect))
+        unexpected<E>(std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+  // These assign overloads ensure that the most efficient assignment
+  // implementation is used while maintaining the strong exception guarantee.
+  // The problematic case is where rhs has a value, but *this does not.
+  //
+  // This overload handles the case where we can just copy-construct `T`
+  // directly into place without throwing.
+  template <class U = T,
+            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
+                * = nullptr>
+  void assign(const expected_operations_base &rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(rhs.get());
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  // This overload handles the case where we can attempt to create a copy of
+  // `T`, then no-throw move it into place if the copy was successful.
+  template <class U = T,
+            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+                                std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(const expected_operations_base &rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      T tmp = rhs.get();
+      geterr().~unexpected<E>();
+      construct(std::move(tmp));
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  // This overload is the worst-case, where we have to move-construct the
+  // unexpected value into temporary storage, then try to copy the T into place.
+  // If the construction succeeds, then everything is fine, but if it throws,
+  // then we move the old unexpected value back into place before rethrowing the
+  // exception.
+  template <class U = T,
+            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+                                !std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(const expected_operations_base &rhs) {
+    if (!this->m_has_val && rhs.m_has_val) {
+      auto tmp = std::move(geterr());
+      geterr().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        construct(rhs.get());
+      } catch (...) {
+        geterr() = std::move(tmp);
+        throw;
+      }
+#else
+      construct(rhs.get());
+#endif
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  // These overloads do the same as above, but for rvalues
+  template <class U = T,
+            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(expected_operations_base &&rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(std::move(rhs).get());
+    } else {
+      assign_common(std::move(rhs));
+    }
+  }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(expected_operations_base &&rhs) {
+    if (!this->m_has_val && rhs.m_has_val) {
+      auto tmp = std::move(geterr());
+      geterr().~unexpected<E>();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        construct(std::move(rhs).get());
+      } catch (...) {
+        geterr() = std::move(tmp);
+        throw;
+      }
+#else
+      construct(std::move(rhs).get());
+#endif
+    } else {
+      assign_common(std::move(rhs));
+    }
+  }
+
+#else
+
+  // If exceptions are disabled then we can just copy-construct
+  void assign(const expected_operations_base &rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(rhs.get());
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  void assign(expected_operations_base &&rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(std::move(rhs).get());
+    } else {
+      assign_common(std::move(rhs));
+    }
+  }
+
+#endif
+
+  // The common part of move/copy assigning
+  template <class Rhs> void assign_common(Rhs &&rhs) {
+    if (this->m_has_val) {
+      if (rhs.m_has_val) {
+        get() = std::forward<Rhs>(rhs).get();
+      } else {
+        destroy_val();
+        construct_error(std::forward<Rhs>(rhs).geterr());
+      }
+    } else {
+      if (!rhs.m_has_val) {
+        geterr() = std::forward<Rhs>(rhs).geterr();
+      }
+    }
+  }
+
+  bool has_value() const { return this->m_has_val; }
+
+  TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
+  constexpr const T &get() const & { return this->m_val; }
+  TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
+#ifndef TL_EXPECTED_NO_CONSTRR
+  constexpr const T &&get() const && { return std::move(this->m_val); }
+#endif
+
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+    return this->m_unexpect;
+  }
+  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+    return std::move(this->m_unexpect);
+  }
+#ifndef TL_EXPECTED_NO_CONSTRR
+  constexpr const unexpected<E> &&geterr() const && {
+    return std::move(this->m_unexpect);
+  }
+#endif
+
+  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct expected_operations_base<void, E> : expected_storage_base<void, E> {
+  using expected_storage_base<void, E>::expected_storage_base;
+
+  template <class... Args> void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs> void construct_with(Rhs &&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args> void construct_error(Args &&...args) noexcept {
+    new (std::addressof(this->m_unexpect))
+        unexpected<E>(std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  template <class Rhs> void assign(Rhs &&rhs) noexcept {
+    if (!this->m_has_val) {
+      if (rhs.m_has_val) {
+        geterr().~unexpected<E>();
+        construct();
+      } else {
+        geterr() = std::forward<Rhs>(rhs).geterr();
+      }
+    } else {
+      if (!rhs.m_has_val) {
+        construct_error(std::forward<Rhs>(rhs).geterr());
+      }
+    }
+  }
+
+  bool has_value() const { return this->m_has_val; }
+
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+    return this->m_unexpect;
+  }
+  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+    return std::move(this->m_unexpect);
+  }
+#ifndef TL_EXPECTED_NO_CONSTRR
+  constexpr const unexpected<E> &&geterr() const && {
+    return std::move(this->m_unexpect);
+  }
+#endif
+
+  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
+    // no-op
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
+              value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
+struct expected_copy_base : expected_operations_base<T, E> {
+  using expected_operations_base<T, E>::expected_operations_base;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
+  using expected_operations_base<T, E>::expected_operations_base;
+
+  expected_copy_base() = default;
+  expected_copy_base(const expected_copy_base &rhs)
+      : expected_operations_base<T, E>(no_init) {
+    if (rhs.has_value()) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  expected_copy_base(expected_copy_base &&rhs) = default;
+  expected_copy_base &operator=(const expected_copy_base &rhs) = default;
+  expected_copy_base &operator=(expected_copy_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_constructible. We
+// have to make do with a non-trivial move constructor even if T is trivially
+// move constructible
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+          bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
+              &&std::is_trivially_move_constructible<E>::value>
+struct expected_move_base : expected_copy_base<T, E> {
+  using expected_copy_base<T, E>::expected_copy_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_base;
+#endif
+template <class T, class E>
+struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
+  using expected_copy_base<T, E>::expected_copy_base;
+
+  expected_move_base() = default;
+  expected_move_base(const expected_move_base &rhs) = default;
+
+  expected_move_base(expected_move_base &&rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value)
+      : expected_copy_base<T, E>(no_init) {
+    if (rhs.has_value()) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+  expected_move_base &operator=(const expected_move_base &rhs) = default;
+  expected_move_base &operator=(expected_move_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial copy assignment operator
+template <class T, class E,
+          bool = is_void_or<
+              T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
+                             TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
+                             TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
+              &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value
+                  &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value
+                      &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
+struct expected_copy_assign_base : expected_move_base<T, E> {
+  using expected_move_base<T, E>::expected_move_base;
+};
+
+template <class T, class E>
+struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
+  using expected_move_base<T, E>::expected_move_base;
+
+  expected_copy_assign_base() = default;
+  expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
+
+  expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
+  expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
+    this->assign(rhs);
+    return *this;
+  }
+  expected_copy_assign_base &
+  operator=(expected_copy_assign_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move assignment operator
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_assignable. We have
+// to make do with a non-trivial move assignment operator even if T is trivially
+// move assignable
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+          bool =
+              is_void_or<T, conjunction<std::is_trivially_destructible<T>,
+                                        std::is_trivially_move_constructible<T>,
+                                        std::is_trivially_move_assignable<T>>>::
+                  value &&std::is_trivially_destructible<E>::value
+                      &&std::is_trivially_move_constructible<E>::value
+                          &&std::is_trivially_move_assignable<E>::value>
+struct expected_move_assign_base : expected_copy_assign_base<T, E> {
+  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_assign_base;
+#endif
+
+template <class T, class E>
+struct expected_move_assign_base<T, E, false>
+    : expected_copy_assign_base<T, E> {
+  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+
+  expected_move_assign_base() = default;
+  expected_move_assign_base(const expected_move_assign_base &rhs) = default;
+
+  expected_move_assign_base(expected_move_assign_base &&rhs) = default;
+
+  expected_move_assign_base &
+  operator=(const expected_move_assign_base &rhs) = default;
+
+  expected_move_assign_base &
+  operator=(expected_move_assign_base &&rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value
+          &&std::is_nothrow_move_assignable<T>::value) {
+    this->assign(std::move(rhs));
+    return *this;
+  }
+};
+
+// expected_delete_ctor_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible
+template <class T, class E,
+          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+                             std::is_copy_constructible<E>::value),
+          bool EnableMove = (is_move_constructible_or_void<T>::value &&
+                             std::is_move_constructible<E>::value)>
+struct expected_delete_ctor_base {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, true, false> {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, true> {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, false> {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+// expected_delete_assign_base will conditionally delete copy and move
+// constructors depending on whether T and E are copy/move constructible +
+// assignable
+template <class T, class E,
+          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+                             std::is_copy_constructible<E>::value &&
+                             is_copy_assignable_or_void<T>::value &&
+                             std::is_copy_assignable<E>::value),
+          bool EnableMove = (is_move_constructible_or_void<T>::value &&
+                             std::is_move_constructible<E>::value &&
+                             is_move_assignable_or_void<T>::value &&
+                             std::is_move_assignable<E>::value)>
+struct expected_delete_assign_base {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, true, false> {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, true> {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = delete;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, false> {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = delete;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+// This is needed to be able to construct the expected_default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit constexpr default_constructor_tag() = default;
+};
+
+// expected_default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E,
+          bool Enable =
+              std::is_default_constructible<T>::value || std::is_void<T>::value>
+struct expected_default_ctor_base {
+  constexpr expected_default_ctor_base() noexcept = default;
+  constexpr expected_default_ctor_base(
+      expected_default_ctor_base const &) noexcept = default;
+  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+      default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base const &) noexcept = default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base &&) noexcept = default;
+
+  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E> struct expected_default_ctor_base<T, E, false> {
+  constexpr expected_default_ctor_base() noexcept = delete;
+  constexpr expected_default_ctor_base(
+      expected_default_ctor_base const &) noexcept = default;
+  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+      default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base const &) noexcept = default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base &&) noexcept = default;
+
+  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+} // namespace detail
+
+template <class E> class bad_expected_access : public std::exception {
+public:
+  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
+
+  virtual const char *what() const noexcept override {
+    return "Bad expected access";
+  }
+
+  const E &error() const & { return m_val; }
+  E &error() & { return m_val; }
+  const E &&error() const && { return std::move(m_val); }
+  E &&error() && { return std::move(m_val); }
+
+private:
+  E m_val;
+};
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+template <class T, class E>
+class expected : private detail::expected_move_assign_base<T, E>,
+                 private detail::expected_delete_ctor_base<T, E>,
+                 private detail::expected_delete_assign_base<T, E>,
+                 private detail::expected_default_ctor_base<T, E> {
+  static_assert(!std::is_reference<T>::value, "T must not be a reference");
+  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
+                "T must not be in_place_t");
+  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
+                "T must not be unexpect_t");
+  static_assert(
+      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
+      "T must not be unexpected<E>");
+  static_assert(!std::is_reference<E>::value, "E must not be a reference");
+
+  T *valptr() { return std::addressof(this->m_val); }
+  const T *valptr() const { return std::addressof(this->m_val); }
+  unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
+  const unexpected<E> *errptr() const {
+    return std::addressof(this->m_unexpect);
+  }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &val() {
+    return this->m_val;
+  }
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  constexpr const U &val() const {
+    return this->m_val;
+  }
+  constexpr const unexpected<E> &err() const { return this->m_unexpect; }
+
+  using impl_base = detail::expected_move_assign_base<T, E>;
+  using ctor_base = detail::expected_default_ctor_base<T, E>;
+
+public:
+  typedef T value_type;
+  typedef E error_type;
+  typedef unexpected<E> unexpected_type;
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto and_then(F &&f) const & {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F> constexpr auto and_then(F &&f) const && {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR auto
+  and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected &>(),
+                                              std::forward<F>(f))) {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR auto
+  and_then(F &&f) && -> decltype(and_then_impl(std::declval<expected &&>(),
+                                               std::forward<F>(f))) {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
+      std::declval<expected const &>(), std::forward<F>(f))) {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
+      std::declval<expected const &&>(), std::forward<F>(f))) {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto map(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto map(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+      std::declval<expected &>(), std::declval<F &&>()))
+  map(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+                                                      std::declval<F &&>()))
+  map(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+                                       std::declval<F &&>()))
+  map(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+                                       std::declval<F &&>()))
+  map(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+      std::declval<expected &>(), std::declval<F &&>()))
+  transform(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+                                                      std::declval<F &&>()))
+  transform(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+                                       std::declval<F &&>()))
+  transform(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+                                       std::declval<F &&>()))
+  transform(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto map_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto map_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+                                                   std::declval<F &&>()))
+  map_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+                                                   std::declval<F &&>()))
+  map_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &>(),
+                                    std::declval<F &&>()))
+  map_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+                                    std::declval<F &&>()))
+  map_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+                                                   std::declval<F &&>()))
+  transform_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+                                                   std::declval<F &&>()))
+  transform_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &>(),
+                                    std::declval<F &&>()))
+  transform_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+                                    std::declval<F &&>()))
+  transform_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+  template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
+    return or_else_impl(*this, std::forward<F>(f));
+  }
+
+  template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
+    return or_else_impl(std::move(*this), std::forward<F>(f));
+  }
+
+  template <class F> expected constexpr or_else(F &&f) const & {
+    return or_else_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F> expected constexpr or_else(F &&f) const && {
+    return or_else_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+  constexpr expected() = default;
+  constexpr expected(const expected &rhs) = default;
+  constexpr expected(expected &&rhs) = default;
+  expected &operator=(const expected &rhs) = default;
+  expected &operator=(expected &&rhs) = default;
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected(in_place_t, Args &&...args)
+      : impl_base(in_place, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
+      : impl_base(in_place, il, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class G = E,
+            detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+                nullptr,
+            detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
+                nullptr>
+  explicit constexpr expected(const unexpected<G> &e)
+      : impl_base(unexpect, e.value()),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <
+      class G = E,
+      detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+          nullptr,
+      detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
+  constexpr expected(unexpected<G> const &e)
+      : impl_base(unexpect, e.value()),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <
+      class G = E,
+      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+      detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
+  explicit constexpr expected(unexpected<G> &&e) noexcept(
+      std::is_nothrow_constructible<E, G &&>::value)
+      : impl_base(unexpect, std::move(e.value())),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <
+      class G = E,
+      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+      detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
+  constexpr expected(unexpected<G> &&e) noexcept(
+      std::is_nothrow_constructible<E, G &&>::value)
+      : impl_base(unexpect, std::move(e.value())),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected(unexpect_t, Args &&...args)
+      : impl_base(unexpect, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
+                              Args &&...args)
+      : impl_base(unexpect, il, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class U, class G,
+            detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
+                                  std::is_convertible<G const &, E>::value)> * =
+                nullptr,
+            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+                * = nullptr>
+  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(*rhs);
+    } else {
+      this->construct_error(rhs.error());
+    }
+  }
+
+  template <class U, class G,
+            detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
+                                 std::is_convertible<G const &, E>::value)> * =
+                nullptr,
+            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+                * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(*rhs);
+    } else {
+      this->construct_error(rhs.error());
+    }
+  }
+
+  template <
+      class U, class G,
+      detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
+                            std::is_convertible<G &&, E>::value)> * = nullptr,
+      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(std::move(*rhs));
+    } else {
+      this->construct_error(std::move(rhs.error()));
+    }
+  }
+
+  template <
+      class U, class G,
+      detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
+                           std::is_convertible<G &&, E>::value)> * = nullptr,
+      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(std::move(*rhs));
+    } else {
+      this->construct_error(std::move(rhs.error()));
+    }
+  }
+
+  template <
+      class U = T,
+      detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+      detail::expected_enable_forward_value<T, E, U> * = nullptr>
+  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+      : expected(in_place, std::forward<U>(v)) {}
+
+  template <
+      class U = T,
+      detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+      detail::expected_enable_forward_value<T, E, U> * = nullptr>
+  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+      : expected(in_place, std::forward<U>(v)) {}
+
+  template <
+      class U = T, class G = T,
+      detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
+          nullptr,
+      detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
+      detail::enable_if_t<
+          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+           !detail::conjunction<std::is_scalar<T>,
+                                std::is_same<T, detail::decay_t<U>>>::value &&
+           std::is_constructible<T, U>::value &&
+           std::is_assignable<G &, U>::value &&
+           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+  expected &operator=(U &&v) {
+    if (has_value()) {
+      val() = std::forward<U>(v);
+    } else {
+      err().~unexpected<E>();
+      ::new (valptr()) T(std::forward<U>(v));
+      this->m_has_val = true;
+    }
+
+    return *this;
+  }
+
+  template <
+      class U = T, class G = T,
+      detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
+          nullptr,
+      detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
+      detail::enable_if_t<
+          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+           !detail::conjunction<std::is_scalar<T>,
+                                std::is_same<T, detail::decay_t<U>>>::value &&
+           std::is_constructible<T, U>::value &&
+           std::is_assignable<G &, U>::value &&
+           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+  expected &operator=(U &&v) {
+    if (has_value()) {
+      val() = std::forward<U>(v);
+    } else {
+      auto tmp = std::move(err());
+      err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        ::new (valptr()) T(std::forward<U>(v));
+        this->m_has_val = true;
+      } catch (...) {
+        err() = std::move(tmp);
+        throw;
+      }
+#else
+      ::new (valptr()) T(std::forward<U>(v));
+      this->m_has_val = true;
+#endif
+    }
+
+    return *this;
+  }
+
+  template <class G = E,
+            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
+                                std::is_assignable<G &, G>::value> * = nullptr>
+  expected &operator=(const unexpected<G> &rhs) {
+    if (!has_value()) {
+      err() = rhs;
+    } else {
+      this->destroy_val();
+      ::new (errptr()) unexpected<E>(rhs);
+      this->m_has_val = false;
+    }
+
+    return *this;
+  }
+
+  template <class G = E,
+            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
+                                std::is_move_assignable<G>::value> * = nullptr>
+  expected &operator=(unexpected<G> &&rhs) noexcept {
+    if (!has_value()) {
+      err() = std::move(rhs);
+    } else {
+      this->destroy_val();
+      ::new (errptr()) unexpected<E>(std::move(rhs));
+      this->m_has_val = false;
+    }
+
+    return *this;
+  }
+
+  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
+                               T, Args &&...>::value> * = nullptr>
+  void emplace(Args &&...args) {
+    if (has_value()) {
+      val().~T();
+    } else {
+      err().~unexpected<E>();
+      this->m_has_val = true;
+    }
+    ::new (valptr()) T(std::forward<Args>(args)...);
+  }
+
+  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
+                               T, Args &&...>::value> * = nullptr>
+  void emplace(Args &&...args) {
+    if (has_value()) {
+      val().~T();
+      ::new (valptr()) T(std::forward<Args>(args)...);
+    } else {
+      auto tmp = std::move(err());
+      err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        ::new (valptr()) T(std::forward<Args>(args)...);
+        this->m_has_val = true;
+      } catch (...) {
+        err() = std::move(tmp);
+        throw;
+      }
+#else
+      ::new (valptr()) T(std::forward<Args>(args)...);
+      this->m_has_val = true;
+#endif
+    }
+  }
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_nothrow_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  void emplace(std::initializer_list<U> il, Args &&...args) {
+    if (has_value()) {
+      T t(il, std::forward<Args>(args)...);
+      val() = std::move(t);
+    } else {
+      err().~unexpected<E>();
+      ::new (valptr()) T(il, std::forward<Args>(args)...);
+      this->m_has_val = true;
+    }
+  }
+
+  template <class U, class... Args,
+            detail::enable_if_t<!std::is_nothrow_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  void emplace(std::initializer_list<U> il, Args &&...args) {
+    if (has_value()) {
+      T t(il, std::forward<Args>(args)...);
+      val() = std::move(t);
+    } else {
+      auto tmp = std::move(err());
+      err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        ::new (valptr()) T(il, std::forward<Args>(args)...);
+        this->m_has_val = true;
+      } catch (...) {
+        err() = std::move(tmp);
+        throw;
+      }
+#else
+      ::new (valptr()) T(il, std::forward<Args>(args)...);
+      this->m_has_val = true;
+#endif
+    }
+  }
+
+private:
+  using t_is_void = std::true_type;
+  using t_is_not_void = std::false_type;
+  using t_is_nothrow_move_constructible = std::true_type;
+  using move_constructing_t_can_throw = std::false_type;
+  using e_is_nothrow_move_constructible = std::true_type;
+  using move_constructing_e_can_throw = std::false_type;
+
+  void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
+    // swapping void is a no-op
+  }
+
+  void swap_where_both_have_value(expected &rhs, t_is_not_void) {
+    using std::swap;
+    swap(val(), rhs.val());
+  }
+
+  void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
+      std::is_nothrow_move_constructible<E>::value) {
+    ::new (errptr()) unexpected_type(std::move(rhs.err()));
+    rhs.err().~unexpected_type();
+    std::swap(this->m_has_val, rhs.m_has_val);
+  }
+
+  void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
+    swap_where_only_one_has_value_and_t_is_not_void(
+        rhs, typename std::is_nothrow_move_constructible<T>::type{},
+        typename std::is_nothrow_move_constructible<E>::type{});
+  }
+
+  void swap_where_only_one_has_value_and_t_is_not_void(
+      expected &rhs, t_is_nothrow_move_constructible,
+      e_is_nothrow_move_constructible) noexcept {
+    auto temp = std::move(val());
+    val().~T();
+    ::new (errptr()) unexpected_type(std::move(rhs.err()));
+    rhs.err().~unexpected_type();
+    ::new (rhs.valptr()) T(std::move(temp));
+    std::swap(this->m_has_val, rhs.m_has_val);
+  }
+
+  void swap_where_only_one_has_value_and_t_is_not_void(
+      expected &rhs, t_is_nothrow_move_constructible,
+      move_constructing_e_can_throw) {
+    auto temp = std::move(val());
+    val().~T();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+    try {
+      ::new (errptr()) unexpected_type(std::move(rhs.err()));
+      rhs.err().~unexpected_type();
+      ::new (rhs.valptr()) T(std::move(temp));
+      std::swap(this->m_has_val, rhs.m_has_val);
+    } catch (...) {
+      val() = std::move(temp);
+      throw;
+    }
+#else
+    ::new (errptr()) unexpected_type(std::move(rhs.err()));
+    rhs.err().~unexpected_type();
+    ::new (rhs.valptr()) T(std::move(temp));
+    std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+  }
+
+  void swap_where_only_one_has_value_and_t_is_not_void(
+      expected &rhs, move_constructing_t_can_throw,
+      e_is_nothrow_move_constructible) {
+    auto temp = std::move(rhs.err());
+    rhs.err().~unexpected_type();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+    try {
+      ::new (rhs.valptr()) T(std::move(val()));
+      val().~T();
+      ::new (errptr()) unexpected_type(std::move(temp));
+      std::swap(this->m_has_val, rhs.m_has_val);
+    } catch (...) {
+      rhs.err() = std::move(temp);
+      throw;
+    }
+#else
+    ::new (rhs.valptr()) T(std::move(val()));
+    val().~T();
+    ::new (errptr()) unexpected_type(std::move(temp));
+    std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+  }
+
+public:
+  template <class OT = T, class OE = E>
+  detail::enable_if_t<detail::is_swappable<OT>::value &&
+                      detail::is_swappable<OE>::value &&
+                      (std::is_nothrow_move_constructible<OT>::value ||
+                       std::is_nothrow_move_constructible<OE>::value)>
+  swap(expected &rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value
+          &&detail::is_nothrow_swappable<T>::value
+              &&std::is_nothrow_move_constructible<E>::value
+                  &&detail::is_nothrow_swappable<E>::value) {
+    if (has_value() && rhs.has_value()) {
+      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
+    } else if (!has_value() && rhs.has_value()) {
+      rhs.swap(*this);
+    } else if (has_value()) {
+      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
+    } else {
+      using std::swap;
+      swap(err(), rhs.err());
+    }
+  }
+
+  constexpr const T *operator->() const {
+    TL_ASSERT(has_value());
+    return valptr();
+  }
+  TL_EXPECTED_11_CONSTEXPR T *operator->() {
+    TL_ASSERT(has_value());
+    return valptr();
+  }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  constexpr const U &operator*() const & {
+    TL_ASSERT(has_value());
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
+    TL_ASSERT(has_value());
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  constexpr const U &&operator*() const && {
+    TL_ASSERT(has_value());
+    return std::move(val());
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
+    TL_ASSERT(has_value());
+    return std::move(val());
+  }
+
+  constexpr bool has_value() const noexcept { return this->m_has_val; }
+  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR const U &value() const & {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(err().value()));
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &value() & {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(err().value()));
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+    return std::move(val());
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &&value() && {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+    return std::move(val());
+  }
+
+  constexpr const E &error() const & {
+    TL_ASSERT(!has_value());
+    return err().value();
+  }
+  TL_EXPECTED_11_CONSTEXPR E &error() & {
+    TL_ASSERT(!has_value());
+    return err().value();
+  }
+  constexpr const E &&error() const && {
+    TL_ASSERT(!has_value());
+    return std::move(err().value());
+  }
+  TL_EXPECTED_11_CONSTEXPR E &&error() && {
+    TL_ASSERT(!has_value());
+    return std::move(err().value());
+  }
+
+  template <class U> constexpr T value_or(U &&v) const & {
+    static_assert(std::is_copy_constructible<T>::value &&
+                      std::is_convertible<U &&, T>::value,
+                  "T must be copy-constructible and convertible to from U&&");
+    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
+  }
+  template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
+    static_assert(std::is_move_constructible<T>::value &&
+                      std::is_convertible<U &&, T>::value,
+                  "T must be move-constructible and convertible to from U&&");
+    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
+  }
+};
+
+namespace detail {
+template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type;
+template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
+template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value()
+             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+             : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value() ? detail::invoke(std::forward<F>(f))
+                         : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class> struct TC;
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
+auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value()
+             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+             : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
+constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value() ? detail::invoke(std::forward<F>(f))
+                         : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+                                                 *std::forward<Exp>(exp)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = expected<void, err_t<Exp>>;
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+    return result();
+  }
+
+  return result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = expected<void, err_t<Exp>>;
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f));
+    return result();
+  }
+
+  return result(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+    -> ret_t<Exp, detail::decay_t<Ret>> {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+                                                 *std::forward<Exp>(exp)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+    return {};
+  }
+
+  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+    -> ret_t<Exp, detail::decay_t<Ret>> {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f));
+    return {};
+  }
+
+  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+  return exp.has_value()
+             ? result(*std::forward<Exp>(exp))
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result(*std::forward<Exp>(exp));
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+  return exp.has_value()
+             ? result()
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result();
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+#else
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+  return exp.has_value()
+             ? result(*std::forward<Exp>(exp))
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result(*std::forward<Exp>(exp));
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+  return exp.has_value()
+             ? result()
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result();
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto or_else_impl(Exp &&exp, F &&f) {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : detail::invoke(std::forward<F>(f),
+                                          std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : (detail::invoke(std::forward<F>(f),
+                                           std::forward<Exp>(exp).error()),
+                            std::forward<Exp>(exp));
+}
+#else
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+auto or_else_impl(Exp &&exp, F &&f) -> Ret {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : detail::invoke(std::forward<F>(f),
+                                          std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : (detail::invoke(std::forward<F>(f),
+                                           std::forward<Exp>(exp).error()),
+                            std::forward<Exp>(exp));
+}
+#endif
+} // namespace detail
+
+template <class T, class E, class U, class F>
+constexpr bool operator==(const expected<T, E> &lhs,
+                          const expected<U, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? false
+             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
+}
+template <class T, class E, class U, class F>
+constexpr bool operator!=(const expected<T, E> &lhs,
+                          const expected<U, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? true
+             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
+}
+template <class E, class F>
+constexpr bool operator==(const expected<void, E> &lhs,
+                          const expected<void, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? false
+             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
+}
+template <class E, class F>
+constexpr bool operator!=(const expected<void, E> &lhs,
+                          const expected<void, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? true
+             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
+}
+
+template <class T, class E, class U>
+constexpr bool operator==(const expected<T, E> &x, const U &v) {
+  return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator==(const U &v, const expected<T, E> &x) {
+  return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const expected<T, E> &x, const U &v) {
+  return x.has_value() ? *x != v : true;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const U &v, const expected<T, E> &x) {
+  return x.has_value() ? *x != v : true;
+}
+
+template <class T, class E>
+constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
+  return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
+  return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
+  return x.has_value() ? true : x.error() != e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
+  return x.has_value() ? true : x.error() != e.value();
+}
+
+template <class T, class E,
+          detail::enable_if_t<(std::is_void<T>::value ||
+                               std::is_move_constructible<T>::value) &&
+                              detail::is_swappable<T>::value &&
+                              std::is_move_constructible<E>::value &&
+                              detail::is_swappable<E>::value> * = nullptr>
+void swap(expected<T, E> &lhs,
+          expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
+  lhs.swap(rhs);
+}
+} // namespace tl
+
+#endif
\ No newline at end of file
diff --git a/clang/include/clang/IPC2978/rapidhash.h b/clang/include/clang/IPC2978/rapidhash.h
new file mode 100644
index 0000000000000..06ee8220f97d2
--- /dev/null
+++ b/clang/include/clang/IPC2978/rapidhash.h
@@ -0,0 +1,574 @@
+/*
+ * rapidhash V3 - Very fast, high quality, platform-independent hashing algorithm.
+ *
+ * Based on 'wyhash', by Wang Yi <godspeed_china at yeah.net>
+ *
+ * Copyright (C) 2025 Nicolas De Carli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * You can contact the author at:
+ *   - rapidhash source repository: https://github.com/Nicoshev/rapidhash
+ */
+
+/*
+ *  Includes.
+ */
+ #include <stdint.h>
+ #include <string.h>
+ #if defined(_MSC_VER)
+ # include <intrin.h>
+ # if defined(_M_X64) && !defined(_M_ARM64EC)
+ #   pragma intrinsic(_umul128)
+ # endif
+ #endif
+
+ /*
+  *  C/C++ macros.
+  */
+
+ #ifdef _MSC_VER
+ # define RAPIDHASH_ALWAYS_INLINE __forceinline
+ #elif defined(__GNUC__)
+ # define RAPIDHASH_ALWAYS_INLINE inline __attribute__((__always_inline__))
+ #else
+ # define RAPIDHASH_ALWAYS_INLINE inline
+ #endif
+
+ #ifdef __cplusplus
+ # define RAPIDHASH_NOEXCEPT noexcept
+ # define RAPIDHASH_CONSTEXPR constexpr
+ # ifndef RAPIDHASH_INLINE
+ #   define RAPIDHASH_INLINE RAPIDHASH_ALWAYS_INLINE
+ # endif
+ # if __cplusplus >= 201402L && !defined(_MSC_VER)
+ #   define RAPIDHASH_INLINE_CONSTEXPR RAPIDHASH_ALWAYS_INLINE constexpr
+ # else
+ #   define RAPIDHASH_INLINE_CONSTEXPR RAPIDHASH_ALWAYS_INLINE
+ # endif
+ #else
+ # define RAPIDHASH_NOEXCEPT
+ # define RAPIDHASH_CONSTEXPR static const
+ # ifndef RAPIDHASH_INLINE
+ #   define RAPIDHASH_INLINE static RAPIDHASH_ALWAYS_INLINE
+ # endif
+ # define RAPIDHASH_INLINE_CONSTEXPR RAPIDHASH_INLINE
+ #endif
+
+ /*
+  *  Unrolled macro.
+  *  Improves large input speed, but increases code size and worsens small input speed.
+  *
+  *  RAPIDHASH_COMPACT: Normal behavior.
+  *  RAPIDHASH_UNROLLED:
+  *
+  */
+  #ifndef RAPIDHASH_UNROLLED
+  # define RAPIDHASH_COMPACT
+  #elif defined(RAPIDHASH_COMPACT)
+  # error "cannot define RAPIDHASH_COMPACT and RAPIDHASH_UNROLLED simultaneously."
+  #endif
+
+ /*
+  *  Protection macro, alters behaviour of rapid_mum multiplication function.
+  *
+  *  RAPIDHASH_FAST: Normal behavior, max speed.
+  *  RAPIDHASH_PROTECTED: Extra protection against entropy loss.
+  */
+ #ifndef RAPIDHASH_PROTECTED
+ # define RAPIDHASH_FAST
+ #elif defined(RAPIDHASH_FAST)
+ # error "cannot define RAPIDHASH_PROTECTED and RAPIDHASH_FAST simultaneously."
+ #endif
+
+ /*
+  *  Likely and unlikely macros.
+  */
+ #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
+ # define _likely_(x)  __builtin_expect(x,1)
+ # define _unlikely_(x)  __builtin_expect(x,0)
+ #else
+ # define _likely_(x) (x)
+ # define _unlikely_(x) (x)
+ #endif
+
+ /*
+  *  Endianness macros.
+  */
+ #ifndef RAPIDHASH_LITTLE_ENDIAN
+ # if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ #   define RAPIDHASH_LITTLE_ENDIAN
+ # elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ #   define RAPIDHASH_BIG_ENDIAN
+ # else
+ #   warning "could not determine endianness! Falling back to little endian."
+ #   define RAPIDHASH_LITTLE_ENDIAN
+ # endif
+ #endif
+
+ /*
+  *  Default secret parameters.
+  */
+   RAPIDHASH_CONSTEXPR uint64_t rapid_secret[8] = {
+     0x2d358dccaa6c78a5ull,
+     0x8bb84b93962eacc9ull,
+     0x4b33a62ed433d4a3ull,
+     0x4d5a2da51de1aa47ull,
+     0xa0761d6478bd642full,
+     0xe7037ed1a0b428dbull,
+     0x90ed1765281c388cull,
+     0xaaaaaaaaaaaaaaaaull};
+
+ /*
+  *  64*64 -> 128bit multiply function.
+  *
+  *  @param A  Address of 64-bit number.
+  *  @param B  Address of 64-bit number.
+  *
+  *  Calculates 128-bit C = *A * *B.
+  *
+  *  When RAPIDHASH_FAST is defined:
+  *  Overwrites A contents with C's low 64 bits.
+  *  Overwrites B contents with C's high 64 bits.
+  *
+  *  When RAPIDHASH_PROTECTED is defined:
+  *  Xors and overwrites A contents with C's low 64 bits.
+  *  Xors and overwrites B contents with C's high 64 bits.
+  */
+ RAPIDHASH_INLINE_CONSTEXPR void rapid_mum(uint64_t *A, uint64_t *B) RAPIDHASH_NOEXCEPT {
+ #if defined(__SIZEOF_INT128__)
+   __uint128_t r=*A; r*=*B;
+   #ifdef RAPIDHASH_PROTECTED
+   *A^=(uint64_t)r; *B^=(uint64_t)(r>>64);
+   #else
+   *A=(uint64_t)r; *B=(uint64_t)(r>>64);
+   #endif
+ #elif defined(_MSC_VER) && (defined(_WIN64) || defined(_M_HYBRID_CHPE_ARM64))
+   #if defined(_M_X64)
+     #ifdef RAPIDHASH_PROTECTED
+     uint64_t a, b;
+     a=_umul128(*A,*B,&b);
+     *A^=a;  *B^=b;
+     #else
+     *A=_umul128(*A,*B,B);
+     #endif
+   #else
+     #ifdef RAPIDHASH_PROTECTED
+     uint64_t a, b;
+     b = __umulh(*A, *B);
+     a = *A * *B;
+     *A^=a;  *B^=b;
+     #else
+     uint64_t c = __umulh(*A, *B);
+     *A = *A * *B;
+     *B = c;
+     #endif
+   #endif
+ #else
+   uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B;
+   uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
+   uint64_t lo=t+(rm1<<32);
+   c+=lo<t;
+   uint64_t hi=rh+(rm0>>32)+(rm1>>32)+c;
+   #ifdef RAPIDHASH_PROTECTED
+   *A^=lo;  *B^=hi;
+   #else
+   *A=lo;  *B=hi;
+   #endif
+ #endif
+ }
+
+ /*
+  *  Multiply and xor mix function.
+  *
+  *  @param A  64-bit number.
+  *  @param B  64-bit number.
+  *
+  *  Calculates 128-bit C = A * B.
+  *  Returns 64-bit xor between high and low 64 bits of C.
+  */
+  RAPIDHASH_INLINE_CONSTEXPR uint64_t rapid_mix(uint64_t A, uint64_t B) RAPIDHASH_NOEXCEPT { rapid_mum(&A,&B); return A^B; }
+
+ /*
+  *  Read functions.
+  */
+ #ifdef RAPIDHASH_LITTLE_ENDIAN
+ RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return v;}
+ RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return v;}
+ #elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
+ RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return __builtin_bswap64(v);}
+ RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return __builtin_bswap32(v);}
+ #elif defined(_MSC_VER)
+ RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return _byteswap_uint64(v);}
+ RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return _byteswap_ulong(v);}
+ #else
+ RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT {
+   uint64_t v; memcpy(&v, p, 8);
+   return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >>  8) & 0xff000000)| ((v <<  8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000));
+ }
+ RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT {
+   uint32_t v; memcpy(&v, p, 4);
+   return (((v >> 24) & 0xff)| ((v >>  8) & 0xff00)| ((v <<  8) & 0xff0000)| ((v << 24) & 0xff000000));
+ }
+ #endif
+
+ /*
+  *  rapidhash main function.
+  *
+  *  @param key     Buffer to be hashed.
+  *  @param len     @key length, in bytes.
+  *  @param seed    64-bit seed used to alter the hash result predictably.
+  *  @param secret  Triplet of 64-bit secrets used to alter hash result predictably.
+  *
+  *  Returns a 64-bit hash.
+  */
+RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT {
+  const uint8_t *p=(const uint8_t *)key;
+  seed ^= rapid_mix(seed ^ secret[2], secret[1]);
+  uint64_t a=0, b=0;
+  size_t i = len;
+  if (_likely_(len <= 16)) {
+    if (len >= 4) {
+      seed ^= len;
+      if (len >= 8) {
+        const uint8_t* plast = p + len - 8;
+        a = rapid_read64(p);
+        b = rapid_read64(plast);
+      } else {
+        const uint8_t* plast = p + len - 4;
+        a = rapid_read32(p);
+        b = rapid_read32(plast);
+      }
+    } else if (len > 0) {
+      a = (((uint64_t)p[0])<<45)|p[len-1];
+      b = p[len>>1];
+    } else
+      a = b = 0;
+  } else {
+    uint64_t see1 = seed, see2 = seed;
+    uint64_t see3 = seed, see4 = seed;
+    uint64_t see5 = seed, see6 = seed;
+#ifdef RAPIDHASH_COMPACT
+    if (i > 112) {
+      do {
+        seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed);
+        see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1);
+        see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2);
+        see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3);
+        see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4);
+        see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5);
+        see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6);
+        p += 112;
+        i -= 112;
+      } while(i > 112);
+      seed ^= see1;
+      see2 ^= see3;
+      see4 ^= see5;
+      seed ^= see6;
+      see2 ^= see4;
+      seed ^= see2;
+    }
+#else
+    if (i > 224) {
+      do {
+          seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed);
+          see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1);
+          see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2);
+          see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3);
+          see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4);
+          see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5);
+          see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6);
+          seed = rapid_mix(rapid_read64(p + 112) ^ secret[0], rapid_read64(p + 120) ^ seed);
+          see1 = rapid_mix(rapid_read64(p + 128) ^ secret[1], rapid_read64(p + 136) ^ see1);
+          see2 = rapid_mix(rapid_read64(p + 144) ^ secret[2], rapid_read64(p + 152) ^ see2);
+          see3 = rapid_mix(rapid_read64(p + 160) ^ secret[3], rapid_read64(p + 168) ^ see3);
+          see4 = rapid_mix(rapid_read64(p + 176) ^ secret[4], rapid_read64(p + 184) ^ see4);
+          see5 = rapid_mix(rapid_read64(p + 192) ^ secret[5], rapid_read64(p + 200) ^ see5);
+          see6 = rapid_mix(rapid_read64(p + 208) ^ secret[6], rapid_read64(p + 216) ^ see6);
+          p += 224;
+          i -= 224;
+      } while (i > 224);
+    }
+    if (i > 112) {
+      seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed);
+      see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1);
+      see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2);
+      see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3);
+      see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4);
+      see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5);
+      see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6);
+      p += 112;
+      i -= 112;
+    }
+    seed ^= see1;
+    see2 ^= see3;
+    see4 ^= see5;
+    seed ^= see6;
+    see2 ^= see4;
+    seed ^= see2;
+#endif
+    if (i > 16) {
+      seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed);
+      if (i > 32) {
+          seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed);
+          if (i > 48) {
+              seed = rapid_mix(rapid_read64(p + 32) ^ secret[1], rapid_read64(p + 40) ^ seed);
+              if (i > 64) {
+                  seed = rapid_mix(rapid_read64(p + 48) ^ secret[1], rapid_read64(p + 56) ^ seed);
+                  if (i > 80) {
+                      seed = rapid_mix(rapid_read64(p + 64) ^ secret[2], rapid_read64(p + 72) ^ seed);
+                      if (i > 96) {
+                          seed = rapid_mix(rapid_read64(p + 80) ^ secret[1], rapid_read64(p + 88) ^ seed);
+                      }
+                  }
+              }
+          }
+      }
+    }
+    a=rapid_read64(p+i-16) ^ i;  b=rapid_read64(p+i-8);
+  }
+  a ^= secret[1];
+  b ^= seed;
+  rapid_mum(&a, &b);
+  return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i);
+}
+
+ /*
+  *  rapidhashMicro main function.
+  *
+  *  @param key     Buffer to be hashed.
+  *  @param len     @key length, in bytes.
+  *  @param seed    64-bit seed used to alter the hash result predictably.
+  *  @param secret  Triplet of 64-bit secrets used to alter hash result predictably.
+  *
+  *  Returns a 64-bit hash.
+  */
+  RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT {
+    const uint8_t *p=(const uint8_t *)key;
+    seed ^= rapid_mix(seed ^ secret[2], secret[1]);
+    uint64_t a=0, b=0;
+    size_t i = len;
+    if (_likely_(len <= 16)) {
+      if (len >= 4) {
+        seed ^= len;
+        if (len >= 8) {
+          const uint8_t* plast = p + len - 8;
+          a = rapid_read64(p);
+          b = rapid_read64(plast);
+        } else {
+          const uint8_t* plast = p + len - 4;
+          a = rapid_read32(p);
+          b = rapid_read32(plast);
+        }
+      } else if (len > 0) {
+        a = (((uint64_t)p[0])<<45)|p[len-1];
+        b = p[len>>1];
+      } else
+        a = b = 0;
+    } else {
+      if (i > 80) {
+        uint64_t see1 = seed, see2 = seed;
+        uint64_t see3 = seed, see4 = seed;
+        do {
+          seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed);
+          see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1);
+          see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2);
+          see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3);
+          see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4);
+          p += 80;
+          i -= 80;
+        } while(i > 80);
+        seed ^= see1;
+        see2 ^= see3;
+        seed ^= see4;
+        seed ^= see2;
+      }
+      if (i > 16) {
+        seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed);
+        if (i > 32) {
+            seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed);
+            if (i > 48) {
+                seed = rapid_mix(rapid_read64(p + 32) ^ secret[1], rapid_read64(p + 40) ^ seed);
+                if (i > 64) {
+                    seed = rapid_mix(rapid_read64(p + 48) ^ secret[1], rapid_read64(p + 56) ^ seed);
+                }
+            }
+        }
+      }
+      a=rapid_read64(p+i-16) ^ i;  b=rapid_read64(p+i-8);
+    }
+    a ^= secret[1];
+    b ^= seed;
+    rapid_mum(&a, &b);
+    return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i);
+  }
+
+  /*
+  *  rapidhashNano main function.
+  *
+  *  @param key     Buffer to be hashed.
+  *  @param len     @key length, in bytes.
+  *  @param seed    64-bit seed used to alter the hash result predictably.
+  *  @param secret  Triplet of 64-bit secrets used to alter hash result predictably.
+  *
+  *  Returns a 64-bit hash.
+  */
+  RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT {
+    const uint8_t *p=(const uint8_t *)key;
+    seed ^= rapid_mix(seed ^ secret[2], secret[1]);
+    uint64_t a=0, b=0;
+    size_t i = len;
+    if (_likely_(len <= 16)) {
+      if (len >= 4) {
+        seed ^= len;
+        if (len >= 8) {
+          const uint8_t* plast = p + len - 8;
+          a = rapid_read64(p);
+          b = rapid_read64(plast);
+        } else {
+          const uint8_t* plast = p + len - 4;
+          a = rapid_read32(p);
+          b = rapid_read32(plast);
+        }
+      } else if (len > 0) {
+        a = (((uint64_t)p[0])<<45)|p[len-1];
+        b = p[len>>1];
+      } else
+        a = b = 0;
+    } else {
+      if (i > 48) {
+        uint64_t see1 = seed, see2 = seed;
+        do {
+          seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed);
+          see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1);
+          see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2);
+          p += 48;
+          i -= 48;
+        } while(i > 48);
+        seed ^= see1;
+        seed ^= see2;
+      }
+      if (i > 16) {
+        seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed);
+        if (i > 32) {
+            seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed);
+        }
+      }
+      a=rapid_read64(p+i-16) ^ i;  b=rapid_read64(p+i-8);
+    }
+    a ^= secret[1];
+    b ^= seed;
+    rapid_mum(&a, &b);
+    return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i);
+  }
+
+/*
+ *  rapidhash seeded hash function.
+ *
+ *  @param key     Buffer to be hashed.
+ *  @param len     @key length, in bytes.
+ *  @param seed    64-bit seed used to alter the hash result predictably.
+ *
+ *  Calls rapidhash_internal using provided parameters and default secrets.
+ *
+ *  Returns a 64-bit hash.
+ */
+RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT {
+  return rapidhash_internal(key, len, seed, rapid_secret);
+}
+
+/*
+ *  rapidhash general purpose hash function.
+ *
+ *  @param key     Buffer to be hashed.
+ *  @param len     @key length, in bytes.
+ *
+ *  Calls rapidhash_withSeed using provided parameters and the default seed.
+ *
+ *  Returns a 64-bit hash.
+ */
+RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash(const void *key, size_t len) RAPIDHASH_NOEXCEPT {
+  return rapidhash_withSeed(key, len, 0);
+}
+
+/*
+ *  rapidhashMicro seeded hash function.
+ *
+ *  Designed for HPC and server applications, where cache misses make a noticeable performance detriment.
+ *  Clang-18+ compiles it to ~140 instructions without stack usage, both on x86-64 and aarch64.
+ *  Faster for sizes up to 512 bytes, just 15%-20% slower for inputs above 1kb.
+ *
+ *  @param key     Buffer to be hashed.
+ *  @param len     @key length, in bytes.
+ *  @param seed    64-bit seed used to alter the hash result predictably.
+ *
+ *  Calls rapidhash_internal using provided parameters and default secrets.
+ *
+ *  Returns a 64-bit hash.
+ */
+ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT {
+  return rapidhashMicro_internal(key, len, seed, rapid_secret);
+}
+
+/*
+ *  rapidhashMicro hash function.
+ *
+ *  @param key     Buffer to be hashed.
+ *  @param len     @key length, in bytes.
+ *
+ *  Calls rapidhash_withSeed using provided parameters and the default seed.
+ *
+ *  Returns a 64-bit hash.
+ */
+RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro(const void *key, size_t len) RAPIDHASH_NOEXCEPT {
+  return rapidhashMicro_withSeed(key, len, 0);
+}
+
+/*
+ *  rapidhashNano seeded hash function.
+ *
+ *  @param key     Buffer to be hashed.
+ *  @param len     @key length, in bytes.
+ *  @param seed    64-bit seed used to alter the hash result predictably.
+ *
+ *  Calls rapidhash_internal using provided parameters and default secrets.
+ *
+ *  Returns a 64-bit hash.
+ */
+ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT {
+  return rapidhashNano_internal(key, len, seed, rapid_secret);
+}
+
+/*
+ *  rapidhashNano hash function.
+ *
+ *  Designed for Mobile and embedded applications, where keeping a small code size is a top priority.
+ *  Clang-18+ compiles it to less than 100 instructions without stack usage, both on x86-64 and aarch64.
+ *  The fastest for sizes up to 48 bytes, but may be considerably slower for larger inputs.
+ *
+ *  @param key     Buffer to be hashed.
+ *  @param len     @key length, in bytes.
+ *
+ *  Calls rapidhash_withSeed using provided parameters and the default seed.
+ *
+ *  Returns a 64-bit hash.
+ */
+RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano(const void *key, size_t len) RAPIDHASH_NOEXCEPT {
+  return rapidhashNano_withSeed(key, len, 0);
+}
\ No newline at end of file
diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h
index 2f33c0749f02a..5370639b44ab5 100644
--- a/clang/include/clang/Lex/HeaderSearchOptions.h
+++ b/clang/include/clang/Lex/HeaderSearchOptions.h
@@ -277,6 +277,11 @@ class HeaderSearchOptions {
   LLVM_PREFERRED_TYPE(bool)
   unsigned ModulesIncludeVFSUsage : 1;
 
+  /// Whether to coordinate with build-system for building of C++20 Modules \
+  /// and header-units.
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned NoScanIPC : 1;
+
   /// Whether we should look for a module in module maps only in provided
   /// header search paths or if we are allowed to look for module maps in
   /// subdirectories of provided paths too.
diff --git a/clang/include/clang/Lex/ModuleLoader.h b/clang/include/clang/Lex/ModuleLoader.h
index a58407200c41c..429d6b547d884 100644
--- a/clang/include/clang/Lex/ModuleLoader.h
+++ b/clang/include/clang/Lex/ModuleLoader.h
@@ -160,6 +160,10 @@ class ModuleLoader {
   virtual bool lookupMissingImports(StringRef Name,
                                     SourceLocation TriggerLoc) = 0;
 
+  /// Load C++20 Header-Unit received from IPC it the
+  /// Header-Unit is not already loaded.
+  virtual Module *loadIPCReceivedHeaderUnit(StringRef FileName) = 0;
+
   bool HadFatalFailure = false;
 };
 
@@ -186,6 +190,10 @@ class TrivialModuleLoader : public ModuleLoader {
                             SourceLocation TriggerLoc) override {
     return false;
   }
+
+  Module *loadIPCReceivedHeaderUnit(StringRef FileName) override {
+    return nullptr;
+  }
 };
 
 } // namespace clang
diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt
index 4f2218b583e41..73da7e0973574 100644
--- a/clang/lib/CMakeLists.txt
+++ b/clang/lib/CMakeLists.txt
@@ -21,6 +21,7 @@ add_subdirectory(DirectoryWatcher)
 add_subdirectory(Index)
 add_subdirectory(IndexSerialization)
 add_subdirectory(InstallAPI)
+add_subdirectory(IPC2978)
 add_subdirectory(StaticAnalyzer)
 add_subdirectory(Format)
 if(CLANG_INCLUDE_TESTS)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index e8181dca59c17..323b5c2dd1831 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4100,6 +4100,11 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
   Args.ClaimAllArgs(options::OPT_fmodules_reduced_bmi);
   Args.ClaimAllArgs(options::OPT_fno_modules_reduced_bmi);
 
+  if (Args.hasArg(options::OPT_no_scan_ipc)) {
+    CmdArgs.push_back("-noScanIPC");
+    Args.claimAllArgs(options::OPT_no_scan_ipc);
+  }
+
   // We need to include the case the input file is a module file here.
   // Since the default compilation model for C++ module interface unit will
   // create temporary module file and compile the temporary module file
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 9f99edadbb70f..b85645523c4a2 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -31,6 +31,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Frontend/Utils.h"
 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/PreprocessorOptions.h"
@@ -1900,6 +1901,37 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
   if (M)
     checkConfigMacros(getPreprocessor(), M, ImportLoc);
 
+  if (N2978::managerCompiler) {
+    // in case of noScanIPC, PrebuiltModuleFiles is empty, so we initialize it
+    // from the build-system here, so the selectModuleSource() call
+    // later-on will return ModuleSource::MS_PrebuiltModulePath.
+    auto &PrebuiltModuleFiles =
+        const_cast<std::map<std::string, std::string, std::less<>> &>(
+            HS.getHeaderSearchOpts().PrebuiltModuleFiles);
+    if (auto it = PrebuiltModuleFiles.find(ModuleName);
+        it == PrebuiltModuleFiles.end()) {
+      N2978::CTBModule mod;
+      mod.moduleName = ModuleName;
+      if (const auto &r =
+              N2978::managerCompiler->receiveBTCModule(std::move(mod));
+          r) {
+        auto &[requested, deps] = r.value();
+        PrebuiltModuleFiles.emplace(std::move(ModuleName),
+                                    std::move(requested.filePath));
+        for (const auto &[file, logicalName, isHeaderUnit] : deps) {
+          if (isHeaderUnit) {
+            loadIPCReceivedHeaderUnit(file.filePath);
+          } else {
+            PrebuiltModuleFiles.emplace(std::move(logicalName),
+                                        std::move(file.filePath));
+          }
+        }
+      } else {
+        string errorMessage = r.error();
+      }
+    }
+  }
+
   // Select the source and filename for loading the named module.
   std::string ModuleFilename;
   ModuleSource Source =
@@ -2394,6 +2426,20 @@ CompilerInstance::lookupMissingImports(StringRef Name,
 
   return false;
 }
+
+Module *CompilerInstance::loadIPCReceivedHeaderUnit(const StringRef FileName) {
+  serialization::ModuleFile *f =
+      getASTReader()->getModuleManager().lookupByFileName(FileName);
+  if (!f)
+    loadModuleFile(FileName, f);
+
+  f->Kind = serialization::MK_PrebuiltModule;
+  const serialization::SubmoduleID Id = getASTReader()->getGlobalSubmoduleID(
+      *f, serialization::NUM_PREDEF_SUBMODULE_IDS);
+
+  return getASTReader()->getSubmodule(Id);
+}
+
 void CompilerInstance::resetAndLeakSema() { llvm::BuryPointer(takeSema()); }
 
 void CompilerInstance::setExternalSemaSource(
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index da96352e1d82c..3b6396522c5a1 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3419,6 +3419,9 @@ static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts,
 
   for (const std::string &F : Opts.VFSOverlayFiles)
     GenerateArg(Consumer, OPT_ivfsoverlay, F);
+
+  if (Opts.NoScanIPC)
+    GenerateArg(Consumer, OPT_no_scan_ipc);
 }
 
 static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
@@ -3551,6 +3554,8 @@ static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
   for (const auto *A : Args.filtered(OPT_ivfsoverlay, OPT_vfsoverlay))
     Opts.AddVFSOverlayFile(A->getValue());
 
+  Opts.NoScanIPC = Args.hasArg(OPT_no_scan_ipc);
+
   return Diags.getNumErrors() == NumErrorsBefore;
 }
 
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index a7d6a068fe2d0..a892ffbed70ca 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -29,6 +29,7 @@
 #include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Frontend/SARIFDiagnosticPrinter.h"
 #include "clang/Frontend/Utils.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/LiteralSupport.h"
 #include "clang/Lex/Preprocessor.h"
@@ -828,6 +829,16 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
   setCurrentInput(Input);
   setCompilerInstance(&CI);
 
+  if (CI.getHeaderSearchOpts().NoScanIPC) {
+    FrontendOptions &Options = CI.getFrontendOpts();
+    std::string OutputPath = Options.OutputFile.empty()
+                                 ? Options.ModuleOutputPath
+                                 : Options.OutputFile;
+    if (const auto &r = N2978::makeIPCManagerCompiler(OutputPath); r) {
+      N2978::managerCompiler = new N2978::IPCManagerCompiler(r.value());
+    }
+  }
+
   bool HasBegunSourceFile = false;
   bool ReplayASTFile = Input.getKind().getFormat() == InputKind::Precompiled &&
                        usesPreprocessorOnly();
diff --git a/clang/lib/IPC2978/.clang-format-ignore b/clang/lib/IPC2978/.clang-format-ignore
new file mode 100644
index 0000000000000..f59ec20aabf58
--- /dev/null
+++ b/clang/lib/IPC2978/.clang-format-ignore
@@ -0,0 +1 @@
+*
\ No newline at end of file
diff --git a/clang/lib/IPC2978/CMakeLists.txt b/clang/lib/IPC2978/CMakeLists.txt
new file mode 100644
index 0000000000000..1ea37f9c9983a
--- /dev/null
+++ b/clang/lib/IPC2978/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+add_clang_library(clangIPC2978 STATIC
+        Manager.cpp
+        IPCManagerBS.cpp
+        IPCManagerCompiler.cpp
+)
\ No newline at end of file
diff --git a/clang/lib/IPC2978/IPCManagerBS.cpp b/clang/lib/IPC2978/IPCManagerBS.cpp
new file mode 100644
index 0000000000000..6af8d5f5458e6
--- /dev/null
+++ b/clang/lib/IPC2978/IPCManagerBS.cpp
@@ -0,0 +1,341 @@
+#include "clang/IPC2978/IPCManagerBS.hpp"
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/Messages.hpp"
+#include "clang/IPC2978/expected.hpp"
+#include <string>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include "clang/IPC2978/rapidhash.h"
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#endif
+using std::string;
+
+namespace N2978
+{
+
+tl::expected<IPCManagerBS, string> makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath)
+{
+#ifdef _WIN32
+    BMIIfHeaderUnitObjOtherwisePath = R"(\\.\pipe\)" + BMIIfHeaderUnitObjOtherwisePath;
+    void *hPipe = CreateNamedPipeA(BMIIfHeaderUnitObjOtherwisePath.c_str(), // pipe name
+                                   PIPE_ACCESS_DUPLEX |                     // read/write access
+                                       FILE_FLAG_FIRST_PIPE_INSTANCE,       // overlapped mode
+                                   PIPE_TYPE_MESSAGE |                      // message-type pipe
+                                       PIPE_READMODE_MESSAGE |              // message read mode
+                                       PIPE_WAIT,                           // blocking mode
+                                   1,                                       // unlimited instances
+                                   BUFFERSIZE * sizeof(TCHAR),              // output buffer size
+                                   BUFFERSIZE * sizeof(TCHAR),              // input buffer size
+                                   PIPE_TIMEOUT,                            // client time-out
+                                   nullptr);                                // default security attributes
+    if (hPipe == INVALID_HANDLE_VALUE)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    return IPCManagerBS(hPipe);
+
+#else
+
+    // Named Pipes are used but Unix Domain sockets could have been used as well. The tradeoff is that a file is created
+    // and there needs to be bind, listen, accept calls which means that an extra fd is created is temporarily on the
+    // server side. it can be closed immediately after.
+
+    const int fdSocket = socket(AF_UNIX, SOCK_STREAM, 0);
+
+    // Create server socket
+    if (fdSocket == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    // Prepare address structure
+    sockaddr_un addr{};
+    addr.sun_family = AF_UNIX;
+    // We use file hash to make a file path smaller, since there is a limit of NAME_MAX that is generally 108 bytes.
+    // TODO
+    // Have an option to receive this path in constructor to make it compatible with Android and IOS.
+    string prependDir = "/tmp/";
+    const uint64_t hash = rapidhash(BMIIfHeaderUnitObjOtherwisePath.c_str(), BMIIfHeaderUnitObjOtherwisePath.size());
+    prependDir.append(to16charHexString(hash));
+    std::copy(prependDir.begin(), prependDir.end(), addr.sun_path);
+
+    // Remove any existing socket
+    unlink(prependDir.c_str());
+
+    // Bind socket to the file system path
+    if (bind(fdSocket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    if (chmod(prependDir.c_str(), 0666) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    // Listen for incoming connections
+    if (listen(fdSocket, 1) == -1)
+    {
+        close(fdSocket);
+        return tl::unexpected(getErrorString());
+    }
+
+    return IPCManagerBS(fdSocket);
+#endif
+}
+
+#ifdef _WIN32
+IPCManagerBS::IPCManagerBS(void *hPipe_)
+{
+    hPipe = hPipe_;
+}
+#else
+IPCManagerBS::IPCManagerBS(const int fdSocket_)
+{
+    fdSocket = fdSocket_;
+}
+#endif
+
+bool checked = false;
+tl::expected<void, string> IPCManagerBS::receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const
+{
+    if (!connectedToCompiler)
+    {
+#ifdef _WIN32
+        if (!ConnectNamedPipe(hPipe, nullptr))
+        {
+            // Is the client already connected?
+            if (GetLastError() != ERROR_PIPE_CONNECTED)
+            {
+                return tl::unexpected(getErrorString());
+            }
+        }
+#else
+        const int fd = accept(fdSocket, nullptr, nullptr);
+        close(fdSocket);
+        if (fd == -1)
+        {
+            return tl::unexpected(getErrorString());
+        }
+        if (checked)
+        {
+            bool breakpoint = true;
+        }
+        checked = true;
+        const_cast<int &>(fdSocket) = fd;
+#endif
+        const_cast<bool &>(connectedToCompiler) = true;
+    }
+    //    raise(SIGTRAP); // At the location of the BP.
+
+    // Read from the pipe.
+    char buffer[BUFFERSIZE];
+    uint32_t bytesRead;
+    if (const auto &r = readInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    else
+    {
+        bytesRead = *r;
+    }
+
+    uint32_t bytesProcessed = 1;
+
+    // read call fails if zero byte is read, so safe to process 1 byte
+    switch (static_cast<CTB>(buffer[0]))
+    {
+
+    case CTB::MODULE: {
+
+        const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+
+        messageType = CTB::MODULE;
+        getInitializedObjectFromBuffer<CTBModule>(ctbBuffer).moduleName = *r;
+    }
+
+    break;
+
+    case CTB::NON_MODULE: {
+
+        const auto &r = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+
+        const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r2)
+        {
+            return tl::unexpected(r.error());
+        }
+
+        messageType = CTB::NON_MODULE;
+        auto &[isHeaderUnit, str] = getInitializedObjectFromBuffer<CTBNonModule>(ctbBuffer);
+        isHeaderUnit = *r;
+        str = *r2;
+    }
+
+    break;
+
+    case CTB::LAST_MESSAGE: {
+
+        const auto &exitStatusExpected = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!exitStatusExpected)
+        {
+            return tl::unexpected(exitStatusExpected.error());
+        }
+
+        const auto &outputExpected = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!outputExpected)
+        {
+            return tl::unexpected(outputExpected.error());
+        }
+
+        const auto &errorOutputExpected = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!errorOutputExpected)
+        {
+            return tl::unexpected(errorOutputExpected.error());
+        }
+
+        const auto &logicalNameExpected = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!logicalNameExpected)
+        {
+            return tl::unexpected(logicalNameExpected.error());
+        }
+
+        const auto &fileSizeExpected = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+        if (!fileSizeExpected)
+        {
+            return tl::unexpected(fileSizeExpected.error());
+        }
+
+        messageType = CTB::LAST_MESSAGE;
+
+        auto &[exitStatus, output, errorOutput, logicalName, fileSize] =
+            getInitializedObjectFromBuffer<CTBLastMessage>(ctbBuffer);
+
+        exitStatus = *exitStatusExpected;
+        output = *outputExpected;
+        errorOutput = *errorOutputExpected;
+        logicalName = *logicalNameExpected;
+        fileSize = *fileSizeExpected;
+    }
+    break;
+    }
+
+    if (bytesRead != bytesProcessed)
+    {
+        return tl::unexpected(getErrorString(bytesRead, bytesProcessed));
+    }
+
+    return {};
+}
+
+tl::expected<void, string> IPCManagerBS::sendMessage(const BTCModule &moduleFile) const
+{
+    vector<char> buffer;
+    writeProcessMappingOfBMIFile(buffer, moduleFile.requested);
+    writeVectorOfModuleDep(buffer, moduleFile.deps);
+    if (const auto &r = writeInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return {};
+}
+
+tl::expected<void, string> IPCManagerBS::sendMessage(const BTCNonModule &nonModule) const
+{
+    vector<char> buffer;
+    buffer.emplace_back(nonModule.isHeaderUnit);
+    writeString(buffer, nonModule.filePath);
+    buffer.emplace_back(nonModule.user);
+    writeUInt32(buffer, nonModule.fileSize);
+    writeVectorOfHuDep(buffer, nonModule.deps);
+    if (const auto &r = writeInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return {};
+}
+
+tl::expected<void, string> IPCManagerBS::sendMessage(const BTCLastMessage &) const
+{
+    vector<char> buffer;
+    buffer.emplace_back(true);
+    if (const auto &r = writeInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return {};
+}
+
+tl::expected<ProcessMappingOfBMIFile, string> IPCManagerBS::createSharedMemoryBMIFile(const BMIFile &bmiFile)
+{
+    ProcessMappingOfBMIFile sharedFile{};
+#ifdef _WIN32
+    // 1) Open the existing file‐mapping object (must have been created by another process)
+    sharedFile.mapping = OpenFileMappingA(FILE_MAP_READ,           // read‐only access
+                                          FALSE,                   // do not inherit handle
+                                          bmiFile.filePath.c_str() // name of mapping
+    );
+
+    if (sharedFile.mapping == nullptr)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    return sharedFile;
+#else
+    const int fd = open(bmiFile.filePath.data(), O_RDONLY);
+    if (fd == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    sharedFile.mapping = mmap(nullptr, bmiFile.fileSize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
+    if (close(fd) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    if (sharedFile.mapping == MAP_FAILED)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    sharedFile.mappingSize = bmiFile.fileSize;
+    return sharedFile;
+#endif
+}
+
+tl::expected<void, string> IPCManagerBS::closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile)
+{
+#ifdef _WIN32
+    CloseHandle(processMappingOfBMIFile.mapping);
+#else
+    if (munmap(processMappingOfBMIFile.mapping, processMappingOfBMIFile.mappingSize) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+#endif
+    return {};
+}
+
+void IPCManagerBS::closeConnection() const
+{
+#ifdef _WIN32
+    CloseHandle(hPipe);
+#else
+    close(fdSocket);
+#endif
+}
+
+} // namespace N2978
\ No newline at end of file
diff --git a/clang/lib/IPC2978/IPCManagerCompiler.cpp b/clang/lib/IPC2978/IPCManagerCompiler.cpp
new file mode 100644
index 0000000000000..54790f75c348d
--- /dev/null
+++ b/clang/lib/IPC2978/IPCManagerCompiler.cpp
@@ -0,0 +1,359 @@
+
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/Messages.hpp"
+#include "clang/IPC2978/rapidhash.h"
+
+#include <string>
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <cstring>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+#endif
+
+using std::string;
+
+namespace N2978
+{
+
+tl::expected<IPCManagerCompiler, string> makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath)
+{
+#ifdef _WIN32
+    BMIIfHeaderUnitObjOtherwisePath = R"(\\.\pipe\)" + BMIIfHeaderUnitObjOtherwisePath;
+    HANDLE hPipe = CreateFileA(BMIIfHeaderUnitObjOtherwisePath.data(), // pipe name
+                               GENERIC_READ |                          // read and write access
+                                   GENERIC_WRITE,
+                               0,             // no sharing
+                               nullptr,       // default security attributes
+                               OPEN_EXISTING, // opens existing pipe
+                               0,             // default attributes
+                               nullptr);      // no template file
+
+    // Break if the pipe handle is valid.
+
+    if (hPipe == INVALID_HANDLE_VALUE)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    return IPCManagerCompiler(hPipe);
+#else
+
+    const int fdSocket = socket(AF_UNIX, SOCK_STREAM, 0);
+
+    if (fdSocket == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    // Prepare address structure
+    sockaddr_un addr{};
+    addr.sun_family = AF_UNIX;
+
+    // We use file hash to make a file path smaller, since there is a limit of NAME_MAX that is generally 108 bytes.
+    // TODO
+    // Have an option to receive this path in constructor to make it compatible with Android and IOS.
+    string prependDir = "/tmp/";
+    const uint64_t hash = rapidhash(BMIIfHeaderUnitObjOtherwisePath.c_str(), BMIIfHeaderUnitObjOtherwisePath.size());
+    prependDir.append(to16charHexString(hash));
+    std::copy(prependDir.begin(), prependDir.end(), addr.sun_path);
+
+    if (connect(fdSocket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    return IPCManagerCompiler(fdSocket);
+
+#endif
+}
+
+#ifdef _WIN32
+IPCManagerCompiler::IPCManagerCompiler(void *hPipe_)
+{
+    hPipe = hPipe_;
+}
+#else
+IPCManagerCompiler::IPCManagerCompiler(const int fdSocket_)
+{
+    fdSocket = fdSocket_;
+}
+#endif
+
+tl::expected<void, string> IPCManagerCompiler::receiveBTCLastMessage() const
+{
+    char buffer[BUFFERSIZE];
+    uint32_t bytesRead;
+    if (const auto &r = readInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    else
+    {
+        bytesRead = *r;
+    }
+
+    if (buffer[0] != static_cast<char>(true))
+    {
+        return tl::unexpected(getErrorString(ErrorCategory::INCORRECT_BTC_LAST_MESSAGE));
+    }
+
+    if (constexpr uint32_t bytesProcessed = 1; bytesRead != bytesProcessed)
+    {
+        return tl::unexpected(getErrorString(bytesRead, bytesProcessed));
+    }
+
+    return {};
+}
+
+tl::expected<BTCModule, string> IPCManagerCompiler::receiveBTCModule(const CTBModule &moduleName) const
+{
+
+    // raise(SIGTRAP); // At the location of the BP.
+    vector<char> buffer = getBufferWithType(CTB::MODULE);
+    writeString(buffer, moduleName.moduleName);
+    if (const auto &r = writeInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+
+    return receiveMessage<BTCModule>();
+}
+
+tl::expected<BTCNonModule, string> IPCManagerCompiler::receiveBTCNonModule(const CTBNonModule &nonModule) const
+{
+    vector<char> buffer = getBufferWithType(CTB::NON_MODULE);
+    buffer.emplace_back(nonModule.isHeaderUnit);
+    writeString(buffer, nonModule.logicalName);
+    if (const auto &r = writeInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return receiveMessage<BTCNonModule>();
+}
+
+tl::expected<void, string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage) const
+{
+    vector<char> buffer = getBufferWithType(CTB::LAST_MESSAGE);
+    buffer.emplace_back(lastMessage.errorOccurred);
+    writeString(buffer, lastMessage.output);
+    writeString(buffer, lastMessage.errorOutput);
+    writeString(buffer, lastMessage.logicalName);
+    writeUInt32(buffer, lastMessage.fileSize);
+    if (const auto &r = writeInternal(buffer); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return {};
+}
+
+tl::expected<void, string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage,
+                                                                  const string &bmiFile, const string &filePath) const
+{
+#ifdef _WIN32
+    const HANDLE hFile = CreateFileA(filePath.c_str(), GENERIC_READ | GENERIC_WRITE,
+                                     0, // no sharing during setup
+                                     nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+    if (hFile == INVALID_HANDLE_VALUE)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    LARGE_INTEGER fileSize;
+    fileSize.QuadPart = bmiFile.size();
+    // 3) Create a RW mapping of that file:
+    const HANDLE hMap =
+        CreateFileMappingA(hFile, nullptr, PAGE_READWRITE, fileSize.HighPart, fileSize.LowPart, filePath.c_str());
+    if (!hMap)
+    {
+        CloseHandle(hFile);
+        return tl::unexpected(getErrorString());
+    }
+
+    void *pView = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, bmiFile.size());
+    if (!pView)
+    {
+        CloseHandle(hFile);
+        CloseHandle(hMap);
+        return tl::unexpected(getErrorString());
+    }
+
+    memcpy(pView, bmiFile.c_str(), bmiFile.size());
+
+    if (!FlushViewOfFile(pView, bmiFile.size()))
+    {
+        UnmapViewOfFile(pView);
+        CloseHandle(hFile);
+        CloseHandle(hMap);
+        return tl::unexpected(getErrorString());
+    }
+
+    UnmapViewOfFile(pView);
+    CloseHandle(hFile);
+
+    if (const auto &r = sendCTBLastMessage(lastMessage); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+
+    if (lastMessage.errorOccurred == EXIT_SUCCESS)
+    {
+        if (const auto &r = receiveBTCLastMessage(); !r)
+        {
+            return tl::unexpected(r.error());
+        }
+    }
+
+    CloseHandle(hMap);
+#else
+
+    const uint64_t fileSize = bmiFile.size();
+    // 1. Open & size
+    const int fd = open(filePath.c_str(), O_RDWR | O_CREAT, 0666);
+    if (fd == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    if (ftruncate(fd, fileSize) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    // 2. Map for write
+    void *mapping = mmap(nullptr, fileSize, PROT_WRITE, MAP_SHARED, fd, 0);
+    if (mapping == MAP_FAILED)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    // 3. We no longer need the FD
+    close(fd);
+
+    memcpy(mapping, bmiFile.data(), bmiFile.size());
+
+    // 4. Flush to disk synchronously
+    if (msync(mapping, fileSize, MS_SYNC) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    if (const auto &r = sendCTBLastMessage(lastMessage); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+
+    if (lastMessage.errorOccurred == EXIT_SUCCESS)
+    {
+        if (const auto &r = receiveBTCLastMessage(); !r)
+        {
+            return tl::unexpected(r.error());
+        }
+    }
+
+    munmap(mapping, fileSize);
+#endif
+
+    return {};
+}
+
+tl::expected<ProcessMappingOfBMIFile, string> IPCManagerCompiler::readSharedMemoryBMIFile(const BMIFile &file)
+{
+    ProcessMappingOfBMIFile f{};
+#ifdef _WIN32
+    // 1) Open the existing file‐mapping object (must have been created by another process)
+    const HANDLE mapping = OpenFileMappingA(FILE_MAP_READ,       // read‐only access
+                                            FALSE,               // do not inherit a handle
+                                            file.filePath.data() // name of mapping
+    );
+
+    if (mapping == nullptr)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    // 2) Map a view of the file into our address space
+    const LPVOID view = MapViewOfFile(mapping,       // handle to mapping object
+                                      FILE_MAP_READ, // read‐only view
+                                      0,             // file offset high
+                                      0,             // file offset low
+                                      file.fileSize  // number of bytes to map (0 maps the whole file)
+    );
+
+    if (view == nullptr)
+    {
+        CloseHandle(mapping);
+        return tl::unexpected(getErrorString());
+    }
+
+    f.mapping = mapping;
+    f.view = view;
+    f.file = {static_cast<char *>(view), file.fileSize};
+#else
+    const int fd = open(file.filePath.data(), O_RDONLY);
+    if (fd == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+    void *mapping = mmap(nullptr, file.fileSize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
+
+    if (close(fd) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    if (mapping == MAP_FAILED)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+    f.mapping = mapping;
+    f.mappingSize = file.fileSize;
+    f.file = {static_cast<char *>(mapping), file.fileSize};
+#endif
+    return f;
+}
+
+tl::expected<void, string> IPCManagerCompiler::closeBMIFileMapping(
+    const ProcessMappingOfBMIFile &processMappingOfBMIFile)
+{
+#ifdef _WIN32
+    UnmapViewOfFile(processMappingOfBMIFile.view);
+    CloseHandle(processMappingOfBMIFile.mapping);
+#else
+    if (munmap(processMappingOfBMIFile.mapping, processMappingOfBMIFile.mappingSize) == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+#endif
+    return {};
+}
+
+void IPCManagerCompiler::closeConnection() const
+{
+#ifdef _WIN32
+    CloseHandle(hPipe);
+#else
+    close(fdSocket);
+#endif
+}
+
+bool operator==(const CTBNonModule &lhs, const CTBNonModule &rhs)
+{
+    return lhs.isHeaderUnit == rhs.isHeaderUnit && lhs.logicalName == rhs.logicalName;
+}
+
+uint64_t CTBNonModuleHash::operator()(const CTBNonModule &ctb) const
+{
+    const_cast<char &>(ctb.logicalName[ctb.logicalName.size()]) = ctb.isHeaderUnit;
+    const uint64_t hash = rapidhash(ctb.logicalName.data(), ctb.logicalName.size() + 1);
+    const_cast<char &>(ctb.logicalName[ctb.logicalName.size()]) = '\0';
+    return hash;
+}
+
+} // namespace N2978
\ No newline at end of file
diff --git a/clang/lib/IPC2978/Manager.cpp b/clang/lib/IPC2978/Manager.cpp
new file mode 100644
index 0000000000000..c6d1c334692ae
--- /dev/null
+++ b/clang/lib/IPC2978/Manager.cpp
@@ -0,0 +1,458 @@
+
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/Messages.hpp"
+#include "clang/IPC2978/expected.hpp"
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <cstring>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+namespace N2978
+{
+
+string getErrorString()
+{
+#ifdef _WIN32
+    const DWORD err = GetLastError();
+
+    char *msg_buf;
+    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
+                   err, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), reinterpret_cast<char *>(&msg_buf), 0, nullptr);
+
+    if (msg_buf == nullptr)
+    {
+        char fallback_msg[128] = {};
+        snprintf(fallback_msg, sizeof(fallback_msg), "GetLastError() = %ld", err);
+        return fallback_msg;
+    }
+
+    string msg = msg_buf;
+    LocalFree(msg_buf);
+    return msg;
+#else
+    return {std::strerror(errno)};
+#endif
+}
+
+string getErrorString(const uint32_t bytesRead_, const uint32_t bytesProcessed_)
+{
+    return "Error: Bytes Readd vs Bytes Processed Mismatch.\nBytes Read: " + std::to_string(bytesRead_) +
+           ", Bytes Processed: " + std::to_string(bytesProcessed_);
+}
+
+string getErrorString(const ErrorCategory errorCategory_)
+{
+    string errorString;
+
+    switch (errorCategory_)
+    {
+    case ErrorCategory::READ_FILE_ZERO_BYTES_READ:
+        errorString = "Error: ReadFile Zero Bytes Read.";
+        break;
+    case ErrorCategory::INCORRECT_BTC_LAST_MESSAGE:
+        errorString = "Error: Incorrect BTC Last Message.";
+        break;
+    case ErrorCategory::UNKNOWN_CTB_TYPE:
+        errorString = "Error: Unknown CTB message received.";
+        break;
+    case ErrorCategory::NONE:
+        string str = __FILE__;
+        str += ':';
+        str += __LINE__;
+        errorString = "N2978 IPC API internal error" + str;
+        break;
+    }
+
+    return errorString;
+}
+
+tl::expected<uint32_t, string> Manager::readInternal(char (&buffer)[BUFFERSIZE]) const
+{
+    uint32_t bytesRead;
+
+#ifdef _WIN32
+    const bool success = ReadFile(hPipe,               // pipe handle
+                                  buffer,              // buffer to receive reply
+                                  BUFFERSIZE,          // size of buffer
+                                  LPDWORD(&bytesRead), // number of bytes read
+                                  nullptr);            // not overlapped
+
+    if (const uint32_t lastError = GetLastError(); !success && lastError != ERROR_MORE_DATA)
+    {
+        return tl::unexpected(getErrorString());
+    }
+
+#else
+    bytesRead = read(fdSocket, buffer, BUFFERSIZE);
+    if (bytesRead == -1)
+    {
+        return tl::unexpected(getErrorString());
+    }
+#endif
+
+    if (!bytesRead)
+    {
+        return tl::unexpected(getErrorString(ErrorCategory::READ_FILE_ZERO_BYTES_READ));
+    }
+
+    return bytesRead;
+}
+
+#ifndef _WIN32
+tl::expected<void, string> writeAll(const int fd, const char *buffer, const uint32_t count)
+{
+    uint32_t bytesWritten = 0;
+
+    while (bytesWritten != count)
+    {
+        const uint32_t result = write(fd, buffer + bytesWritten, count - bytesWritten);
+        if (result == -1)
+        {
+            if (errno == EINTR)
+            {
+                // Interrupted by signal: retry
+                continue;
+            }
+            return tl::unexpected(getErrorString());
+        }
+        if (result == 0)
+        {
+            // According to POSIX, write() returning 0 is only valid for count == 0
+            return tl::unexpected(getErrorString());
+        }
+        bytesWritten += result;
+    }
+
+    return {};
+}
+#endif
+
+tl::expected<void, string> Manager::writeInternal(const vector<char> &buffer) const
+{
+#ifdef _WIN32
+    const bool success = WriteFile(hPipe,         // pipe handle
+                                   buffer.data(), // message
+                                   buffer.size(), // message length
+                                   nullptr,       // bytes written
+                                   nullptr);      // not overlapped
+    if (!success)
+    {
+        return tl::unexpected(getErrorString());
+    }
+#else
+    if (const auto &r = writeAll(fdSocket, buffer.data(), buffer.size()); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+#endif
+    return {};
+}
+
+vector<char> Manager::getBufferWithType(CTB type)
+{
+    vector<char> buffer;
+    buffer.emplace_back(static_cast<uint8_t>(type));
+    return buffer;
+}
+
+void Manager::writeUInt32(vector<char> &buffer, const uint32_t value)
+{
+    const auto ptr = reinterpret_cast<const char *>(&value);
+    buffer.insert(buffer.end(), ptr, ptr + 4);
+}
+
+void Manager::writeString(vector<char> &buffer, const string &str)
+{
+    writeUInt32(buffer, str.size());
+    buffer.insert(buffer.end(), str.begin(), str.end()); // Insert all characters
+}
+
+void Manager::writeProcessMappingOfBMIFile(vector<char> &buffer, const BMIFile &file)
+{
+    writeString(buffer, file.filePath);
+    writeUInt32(buffer, file.fileSize);
+}
+
+void Manager::writeModuleDep(vector<char> &buffer, const ModuleDep &dep)
+{
+    writeProcessMappingOfBMIFile(buffer, dep.file);
+    writeString(buffer, dep.logicalName);
+    buffer.emplace_back(dep.isHeaderUnit);
+}
+
+void Manager::writeHuDep(vector<char> &buffer, const HuDep &dep)
+{
+    writeProcessMappingOfBMIFile(buffer, dep.file);
+    writeString(buffer, dep.logicalName);
+    buffer.emplace_back(dep.user);
+}
+
+void Manager::writeVectorOfStrings(vector<char> &buffer, const vector<string> &strs)
+{
+    writeUInt32(buffer, strs.size());
+    for (const string &str : strs)
+    {
+        writeString(buffer, str);
+    }
+}
+
+void Manager::writeVectorOfProcessMappingOfBMIFiles(vector<char> &buffer, const vector<BMIFile> &files)
+{
+    writeUInt32(buffer, files.size());
+    for (const BMIFile &file : files)
+    {
+        writeProcessMappingOfBMIFile(buffer, file);
+    }
+}
+
+void Manager::writeVectorOfModuleDep(vector<char> &buffer, const vector<ModuleDep> &deps)
+{
+    writeUInt32(buffer, deps.size());
+    for (const ModuleDep &dep : deps)
+    {
+        writeModuleDep(buffer, dep);
+    }
+}
+
+void Manager::writeVectorOfHuDep(vector<char> &buffer, const vector<HuDep> &deps)
+{
+    writeUInt32(buffer, deps.size());
+    for (const HuDep &dep : deps)
+    {
+        writeHuDep(buffer, dep);
+    }
+}
+
+tl::expected<bool, string> Manager::readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                     uint32_t &bytesProcessed) const
+{
+    bool result;
+    const auto &r =
+        readNumberOfBytes(reinterpret_cast<char *>(&result), sizeof(result), buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return result;
+}
+
+tl::expected<uint32_t, string> Manager::readUInt32FromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                           uint32_t &bytesProcessed) const
+{
+    uint32_t size;
+    if (const auto &r = readNumberOfBytes(reinterpret_cast<char *>(&size), 4, buffer, bytesRead, bytesProcessed); !r)
+    {
+        return tl::unexpected(r.error());
+    }
+    return size;
+}
+
+tl::expected<string, string> Manager::readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                         uint32_t &bytesProcessed) const
+{
+    auto r = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+    const uint32_t stringSize = *r;
+    string str(stringSize, 'a');
+    if (const auto &r2 = readNumberOfBytes(str.data(), stringSize, buffer, bytesRead, bytesProcessed); !r2)
+    {
+        return tl::unexpected(r2.error());
+    }
+    return str;
+}
+
+tl::expected<BMIFile, string> Manager::readProcessMappingOfBMIFileFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                                           uint32_t &bytesProcessed) const
+{
+    const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+    const auto &r2 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+
+    BMIFile file;
+    file.filePath = *r;
+    file.fileSize = *r2;
+    return file;
+}
+
+tl::expected<vector<string>, string> Manager::readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                         uint32_t &bytesRead,
+                                                                         uint32_t &bytesProcessed) const
+{
+    const auto &r = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+    const uint32_t vectorSize = *r;
+    vector<string> vec;
+    vec.reserve(vectorSize);
+    for (uint32_t i = 0; i < vectorSize; ++i)
+    {
+        const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+        vec.emplace_back(*r2);
+    }
+    return vec;
+}
+
+tl::expected<ModuleDep, string> Manager::readModuleDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                               uint32_t &bytesProcessed) const
+{
+    const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+
+    const auto &r2 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r2)
+    {
+        return tl::unexpected(r2.error());
+    }
+
+    const auto &r3 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r3)
+    {
+        return tl::unexpected(r3.error());
+    }
+
+    const auto &r4 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r4)
+    {
+        return tl::unexpected(r4.error());
+    }
+
+    ModuleDep modDep;
+
+    modDep.file.filePath = *r;
+    modDep.file.fileSize = *r2;
+    modDep.logicalName = *r3;
+    modDep.isHeaderUnit = *r4;
+
+    return modDep;
+}
+
+tl::expected<vector<ModuleDep>, string> Manager::readVectorOfModuleDepFromPipe(char (&buffer)[4096],
+                                                                               uint32_t &bytesRead,
+                                                                               uint32_t &bytesProcessed) const
+{
+    const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    if (!vectorSize)
+    {
+        return tl::unexpected(vectorSize.error());
+    }
+
+    vector<ModuleDep> vec;
+    vec.reserve(*vectorSize);
+    for (uint32_t i = 0; i < *vectorSize; ++i)
+    {
+        const auto &r = readModuleDepFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+
+        vec.emplace_back(*r);
+    }
+    return vec;
+}
+
+tl::expected<HuDep, string> Manager::readHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                       uint32_t &bytesProcessed) const
+{
+    const auto &r = readProcessMappingOfBMIFileFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+
+    const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r2)
+    {
+        return tl::unexpected(r2.error());
+    }
+
+    const auto &r3 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r3)
+    {
+        return tl::unexpected(r3.error());
+    }
+
+    HuDep huDep;
+    huDep.file = *r;
+    huDep.logicalName = *r2;
+    huDep.user = *r3;
+    return huDep;
+}
+
+tl::expected<vector<HuDep>, string> Manager::readVectorOfHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                                       uint32_t &bytesProcessed) const
+{
+    const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    if (!vectorSize)
+    {
+        return tl::unexpected(vectorSize.error());
+    }
+
+    vector<HuDep> vec;
+    vec.reserve(*vectorSize);
+    for (uint32_t i = 0; i < *vectorSize; ++i)
+    {
+        const auto &r = readHuDepFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+        vec.emplace_back(*r);
+    }
+    return vec;
+}
+
+tl::expected<void, string> Manager::readNumberOfBytes(char *output, const uint32_t size, char (&buffer)[BUFFERSIZE],
+                                                      uint32_t &bytesRead, uint32_t &bytesProcessed) const
+{
+    uint32_t pendingSize = size;
+    uint32_t offset = 0;
+    while (true)
+    {
+        const uint32_t bytesAvailable = bytesRead - bytesProcessed;
+        if (bytesAvailable >= pendingSize)
+        {
+            memcpy(output + offset, buffer + bytesProcessed, pendingSize);
+            bytesProcessed += pendingSize;
+            break;
+        }
+
+        if (bytesAvailable)
+        {
+            memcpy(output + offset, buffer + bytesProcessed, bytesAvailable);
+            offset += bytesAvailable;
+            pendingSize -= bytesAvailable;
+        }
+
+        bytesProcessed = 0;
+        if (const auto &r = readInternal(buffer); r)
+        {
+            bytesRead = *r;
+        }
+        else
+        {
+            return tl::unexpected(r.error());
+        }
+    }
+    return {};
+}
+} // namespace N2978
\ No newline at end of file
diff --git a/clang/lib/IPC2978/setup.py b/clang/lib/IPC2978/setup.py
new file mode 100644
index 0000000000000..b1a99fd4f14e3
--- /dev/null
+++ b/clang/lib/IPC2978/setup.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+import os
+import shutil
+
+# The directory where this script resides (lib/clang/IPC2978)
+source_dir = os.path.abspath(os.path.dirname(__file__))
+# Assumed that IPC2978 repo is present in parallel with llvm repo.
+copy_from = os.path.abspath(source_dir + ('../' * 5) + 'ipc2978api/') + os.sep
+# The directory for the header-files
+include_dir = os.path.abspath(os.path.join(source_dir, '../../include/clang/IPC2978')) + os.sep
+# The directory for the unit-tests.
+ipc_test_source_file = os.path.abspath(os.path.join(source_dir, '../../unittests/IPC2978/IPC2978Test.cpp'))
+
+shutil.copytree(copy_from + 'include', include_dir, dirs_exist_ok=True)
+shutil.copytree(copy_from + 'src', source_dir, dirs_exist_ok=True)
+# We'll process files in both include and source directories
+roots = [source_dir, include_dir]
+
+# Gather all header filenames in the include directory (top-level only)
+include_files = [f for f in os.listdir(include_dir) if f.endswith(".hpp")]
+
+files = []
+
+# Iterate through the source and include directories
+for root in roots:
+    # Skipping the CMakeLists.txt and .py files
+    files.extend([os.path.join(root, x) for x in os.listdir(root) if
+                  not os.path.join(root, x).endswith(".txt") and not os.path.join(root, x).endswith(".py")])
+
+for file in files:
+    out_lines = []
+    with open(file, 'r', encoding='utf-8') as f:
+        lines = f.readlines()
+        # Examine each line for an include directive
+        for line in lines:
+            if line.startswith('#include "'):
+                out_lines.append(line.replace('#include "', '#include "clang/IPC2978/', 1))
+            else:
+                out_lines.append(line)
+    with open(file, 'w', encoding='utf-8') as f:
+        for line in out_lines:
+            f.writelines(line)
+
+shutil.copy(copy_from + "/tests/ClangTest.cpp", ipc_test_source_file)
+
+# Modifying the copied ClangTest.cpp file
+out_lines = []
+with open(ipc_test_source_file, 'r', encoding='utf-8') as f:
+    lines = f.readlines()
+    # Examine each line for an include directive
+    for line in lines:
+        if line.startswith('// #define IS_THIS_CLANG_REPO'):
+            out_lines.append('#define IS_THIS_CLANG_REPO')
+        else:
+            out_lines.append(line)
+with open(ipc_test_source_file, 'w', encoding='utf-8') as f:
+    for line in out_lines:
+        f.writelines(line)
diff --git a/clang/lib/Lex/CMakeLists.txt b/clang/lib/Lex/CMakeLists.txt
index f61737cd68021..879dc26f5a7a4 100644
--- a/clang/lib/Lex/CMakeLists.txt
+++ b/clang/lib/Lex/CMakeLists.txt
@@ -34,4 +34,5 @@ add_clang_library(clangLex
 
   LINK_LIBS
   clangBasic
+  clangIPC2978
   )
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 9d01b8d99e227..39fcf28978aed 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -23,6 +23,7 @@
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/TokenKinds.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
 #include "clang/Lex/CodeCompletionHandler.h"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/LexDiagnostic.h"
@@ -2310,7 +2311,6 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   SmallString<1024> RelativePath;
   // We get the raw path only if we have 'Callbacks' to which we later pass
   // the path.
-  ModuleMap::KnownHeader SuggestedModule;
   SourceLocation FilenameLoc = FilenameTok.getLocation();
   StringRef LookupFilename = Filename;
 
@@ -2325,10 +2325,43 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
     BackslashStyle = llvm::sys::path::Style::windows;
   }
 
-  OptionalFileEntryRef File = LookupHeaderIncludeOrImport(
-      &CurDir, Filename, FilenameLoc, FilenameRange, FilenameTok,
-      IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile,
-      LookupFilename, RelativePath, SearchPath, SuggestedModule, isAngled);
+  ModuleMap::KnownHeader SuggestedModule;
+  OptionalFileEntryRef File;
+
+  N2978::BTCNonModule BTCNonMod;
+  if (N2978::managerCompiler) {
+    N2978::CTBNonModule CTBNonMod;
+    CTBNonMod.isHeaderUnit = IsImportDecl;
+    CTBNonMod.logicalName = Filename.str();
+
+    if (auto it = N2978::respnses.find(CTBNonMod);
+        it != N2978::respnses.end()) {
+      BTCNonMod = it->second;
+    } else {
+      // todo
+      //  check if an already received header-unit possess this header-file.
+      //  do not send in that case.
+      if (const auto &Result =
+              N2978::managerCompiler->receiveBTCNonModule(std::move(CTBNonMod));
+          Result)
+        BTCNonMod = *Result;
+      N2978::respnses.emplace(CTBNonMod, BTCNonMod);
+    }
+
+    File = getFileManager().getOptionalFileRef(BTCNonMod.filePath);
+
+    if (BTCNonMod.isHeaderUnit) {
+      IsImportDecl = true;
+      SuggestedModule = {
+          getModuleLoader().loadIPCReceivedHeaderUnit(BTCNonMod.filePath),
+          ModuleMap::NormalHeader};
+    }
+  } else {
+    File = LookupHeaderIncludeOrImport(
+        &CurDir, Filename, FilenameLoc, FilenameRange, FilenameTok,
+        IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile,
+        LookupFilename, RelativePath, SearchPath, SuggestedModule, isAngled);
+  }
 
   if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
     if (File && isPCHThroughHeader(&File->getFileEntry()))
@@ -2368,8 +2401,9 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
 
   Module *ModuleToImport = SuggestedModule.getModule();
 
-  bool MaybeTranslateInclude = Action == Enter && File && ModuleToImport &&
-                               !ModuleToImport->isForBuilding(getLangOpts());
+  bool MaybeTranslateInclude =
+      Action == Enter && File && ModuleToImport &&
+      (!ModuleToImport->isForBuilding(getLangOpts()) || BTCNonMod.isHeaderUnit);
 
   // Maybe a usable Header Unit
   bool UsableHeaderUnit = false;
@@ -2529,9 +2563,11 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   }
 
   // Issue a diagnostic if the name of the file on disk has a different case
-  // than the one we're about to open.
+  // than the one we're about to open. Not checked if it is an IPC received
+  // module.
   const bool CheckIncludePathPortability =
-      !IsMapped && !File->getFileEntry().tryGetRealPathName().empty();
+      !BTCNonMod.isHeaderUnit && !IsMapped &&
+      !File->getFileEntry().tryGetRealPathName().empty();
 
   if (CheckIncludePathPortability) {
     StringRef Name = LookupFilename;
diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp
index c8ee625eb57ad..53a44ba05161d 100644
--- a/clang/lib/Parse/ParseAST.cpp
+++ b/clang/lib/Parse/ParseAST.cpp
@@ -15,6 +15,9 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ExternalASTSource.h"
 #include "clang/AST/Stmt.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/IPC2978/IPCManagerBS.hpp"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
 #include "clang/Parse/Parser.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -182,6 +185,19 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
 
   Consumer->HandleTranslationUnit(S.getASTContext());
 
+  if (N2978::managerCompiler) {
+    N2978::CTBLastMessage Last;
+    if (S.getCurrentModule() &&
+        S.getCurrentModule()->Kind != Module::ModuleHeaderUnit)
+      Last.logicalName = S.getCurrentModule()->Name;
+
+    Last.errorOccurred = S.getDiagnostics().hasErrorOccurred();
+    if (const auto &r =
+            N2978::managerCompiler->sendCTBLastMessage(std::move(Last));
+        r) {
+    }
+  }
+
   // Finalize the template instantiation observer chain.
   // FIXME: This (and init.) should be done in the Sema class, but because
   // Sema does not have a reliable "Finalize" function (it has a
diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index 54c781a35c20c..defdbd2abe86a 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -92,6 +92,7 @@ add_subdirectory(Frontend)
 add_subdirectory(Rewrite)
 add_subdirectory(Sema)
 add_subdirectory(CodeGen)
+add_subdirectory(IPC2978)
 if(HAVE_CLANG_REPL_SUPPORT)
   add_subdirectory(Interpreter)
 endif()
@@ -126,6 +127,10 @@ add_distinct_clang_unittest(AllClangUnitTests
   ${LLVM_COMPONENTS}
 )
 
+# Needed for the IPC2978Test
+target_compile_definitions(AllClangUnitTests PRIVATE LLVM_TOOLS_BINARY_DIR="${LLVM_TOOLS_BINARY_DIR}")
+add_dependencies(AllClangUnitTests clang)
+
 # The Tooling library has some internal headers. Make those work. If we like
 # the merged clang unit test binary, we can update the include paths and make
 # this the default.
diff --git a/clang/unittests/IPC2978/.clang-format-ignore b/clang/unittests/IPC2978/.clang-format-ignore
new file mode 100644
index 0000000000000..f59ec20aabf58
--- /dev/null
+++ b/clang/unittests/IPC2978/.clang-format-ignore
@@ -0,0 +1 @@
+*
\ No newline at end of file
diff --git a/clang/unittests/IPC2978/CMakeLists.txt b/clang/unittests/IPC2978/CMakeLists.txt
new file mode 100644
index 0000000000000..5f0a50a13e62d
--- /dev/null
+++ b/clang/unittests/IPC2978/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_clang_unittest(IPC2978Test
+        IPC2978Test.cpp
+        CLANG_LIBS
+        clangIPC2978
+)
\ No newline at end of file
diff --git a/clang/unittests/IPC2978/IPC2978Test.cpp b/clang/unittests/IPC2978/IPC2978Test.cpp
new file mode 100644
index 0000000000000..3f62c5f906d1c
--- /dev/null
+++ b/clang/unittests/IPC2978/IPC2978Test.cpp
@@ -0,0 +1,1068 @@
+//===- unittests/IPC2978/IPC2978Test.cpp - Tests IPC2978 Support -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// The following line is uncommented by clang/lib/IPC2978/setup.py for clang/unittests/IPC2978/IPC2978.cpp
+
+#define IS_THIS_CLANG_REPO
+#ifdef IS_THIS_CLANG_REPO
+#include "clang/IPC2978/IPCManagerBS.hpp"
+#include "gtest/gtest.h"
+template <typename T> void printMessage(const T &, bool)
+{
+}
+#else
+#include "IPCManagerBS.hpp"
+#include "Testing.hpp"
+#include "fmt/printf.h"
+#endif
+
+#include <filesystem>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
+using namespace std::filesystem;
+using namespace N2978;
+using namespace std;
+
+#ifdef _WIN32
+#define CLANG_CMD ".\\clang.exe"
+#else
+#define CLANG_CMD "./clang"
+#endif
+
+namespace
+{
+#ifdef _WIN32
+PROCESS_INFORMATION pi;
+tl::expected<void, string> Run(const string &command)
+{
+    STARTUPINFOA si{};
+    si.cb = sizeof(si);
+    if (!CreateProcessA(nullptr,                             // lpApplicationName
+                        const_cast<char *>(command.c_str()), // lpCommandLine
+                        nullptr,                             // lpProcessAttributes
+                        nullptr,                             // lpThreadAttributes
+                        FALSE,                               // bInheritHandles
+                        0,                                   // dwCreationFlags
+                        nullptr,                             // lpEnvironment
+                        nullptr,                             // lpCurrentDirectory
+                        &si,                                 // lpStartupInfo
+                        &pi                                  // lpProcessInformation
+                        ))
+    {
+        return tl::unexpected("CreateProcess" + getErrorString());
+    }
+    return {};
+}
+#else
+
+int procStatus;
+int procId;
+/// Start a process and gather its raw output.  Returns its exit code.
+/// Crashes (calls Fatal()) on error.
+tl::expected<void, string> Run(const string &command)
+{
+    if (procId = fork(); procId == -1)
+    {
+        return tl::unexpected("fork" + getErrorString());
+    }
+    if (procId == 0)
+    {
+        // Child process
+        exit(WEXITSTATUS(system(command.c_str())));
+    }
+    return {};
+}
+#endif
+
+tl::expected<void, std::string> CloseProcess()
+{
+#ifdef _WIN32
+    if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
+    {
+        return tl::unexpected("WaitForSingleObject" + getErrorString());
+    }
+#else
+    if (waitpid(procId, &procStatus, 0) == -1)
+    {
+        return tl::unexpected("waitpid" + getErrorString());
+    }
+#endif
+    return {};
+}
+
+// Creates all the input files (source files + pcm files) that are needed for the test.
+void setupTest()
+{
+    //  A.cpp
+    const string aDotCpp = R"(
+export module A;     // primary module interface unit
+
+export import :B;    // Hello() is visible when importing 'A'.
+import :C;           // WorldImpl() is now visible only for 'A.cpp'.
+// export import :C; // ERROR: Cannot export a module implementation unit.
+
+// World() is visible by any translation unit importing 'A'.
+export char const* World()
+{
+    return WorldImpl();
+}
+)";
+    // A-B.cpp
+    const string aBDotCPP = R"(
+export module A:B; // partition module interface unit
+
+// Hello() is visible by any translation unit importing 'A'.
+export char const* Hello() { return "Hello"; }
+)";
+
+    // A-C.cpp
+    const string aCDotCPP = R"(
+module A:C; // partition module implementation unit
+
+// WorldImpl() is visible by any module unit of 'A' importing ':C'.
+char const* WorldImpl() { return "World"; }
+)";
+
+    // M.hpp, N.hpp and O.hpp are to be used as header-units, header-files
+    // while X.hpp, Y.hpp and Z.hpp are to be used as big-hu by include Big.hpp
+
+    // M.hpp
+    const string mDotHpp = R"(
+// this file can not be included without first defining M_HEADER_FILE
+// this is to demonstrate difference between header-file and header-unit.
+// as macros don't seep into header-units
+
+#ifdef M_HEADER_FILE
+inline int m = 5;
+#else
+fail compilation
+#endif
+)";
+
+    // N.hpp
+    const string nDotHpp = R"(
+// should work just fine as macro should not seep in here while inclusion.
+
+#ifdef N_HEADER_FILE
+fail compilation
+#else
+#define M_HEADER_FILE
+#include "M.hpp"
+inline int n = 5 + m;
+#endif
+
+// COMMAND_MACRO should be defined while compiling this.
+// however, it should still be fine if it is not defined while compiling
+// a file consuming this
+
+#ifndef COMMAND_MACRO
+fail compilation
+#endif
+)";
+
+    // O.hpp
+    const string oDotHpp = R"(
+// TRANSLATING should be defined if /translateInclude is being used.
+// "O.hpp" should still be treated as header-file.
+
+#define M_HEADER_FILE
+#include "M.hpp"
+#ifdef TRANSLATING
+#include "N.hpp"
+#else
+import "N.hpp";
+#endif
+
+inline int o = n + m + 5;
+)";
+
+    // X.hpp
+    const string xDotHpp = R"(
+#ifndef X_HPP
+#define X_HPP
+inline int x = 5;
+#endif
+)";
+
+    // Y.hpp
+    const string yDotHpp = R"(
+#ifndef Y_HPP
+#define Y_HPP
+#include "X.hpp"
+inline int y = x + 5;
+#endif
+)";
+
+    // Z.hpp
+    const string zDotHpp = R"(
+#ifndef Z_HPP
+#define Z_HPP
+#include "Y.hpp"
+inline int z = x + y + 5;
+#endif
+)";
+
+    // Big.hpp
+    const string bigDotHpp = R"(
+#include "X.hpp"
+// todo
+// following two should not be requested as Big.hpp includes the following as well.
+#include "Y.hpp"
+#include "Z.hpp"
+)";
+
+    // Foo.cpp
+    const string fooDotCpp = R"(
+module;
+#include "X.hpp"
+#include "Z.hpp"
+export module Foo;
+import A;
+
+export void Foo()
+{
+    Hello();
+    World();
+    int s = x + y + z;
+}
+)";
+
+    // main.cpp
+    const string mainDotCpp = R"(
+import Foo;
+// only foo will be requested as A is already sent with it.
+import A;
+
+int main()
+{
+    Hello();
+    World();
+    Foo();
+// no } to test for error
+)";
+
+    ofstream("A.cpp") << aDotCpp;
+    ofstream("A-B.cpp") << aBDotCPP;
+    ofstream("A-C.cpp") << aCDotCPP;
+    ofstream("M.hpp") << mDotHpp;
+    ofstream("N.hpp") << nDotHpp;
+    ofstream("O.hpp") << oDotHpp;
+    ofstream("X.hpp") << xDotHpp;
+    ofstream("Y.hpp") << yDotHpp;
+    ofstream("Z.hpp") << zDotHpp;
+    ofstream("Big.hpp") << bigDotHpp;
+    ofstream("Foo.cpp") << fooDotCpp;
+    ofstream("main.cpp") << mainDotCpp;
+}
+
+tl::expected<int, string> runTest()
+{
+    setupTest();
+
+    string current = current_path().generic_string() + '/';
+    string mainFilePath = current + "main .o";
+    string modFilePath = current + "mod .pcm";
+    string mod1FilePath = current + "mod1 .pcm";
+    string mod2FilePath = current + "mod2 .pcm";
+
+    string aCObj = (current_path() / "A-C .o").generic_string();
+    string aCPcm = (current_path() / "A-C .pcm").generic_string();
+    string aBObj = (current_path() / "A-B .o").generic_string();
+    string aBPcm = (current_path() / "A-B .pcm").generic_string();
+    string aObj = (current_path() / "A .o").generic_string();
+    string aPcm = (current_path() / "A .pcm").generic_string();
+    string bObj = (current_path() / "B .o").generic_string();
+    string bPcm = (current_path() / "B .pcm").generic_string();
+    string mHpp = (current_path() / "M.hpp").generic_string();
+    string nHpp = (current_path() / "N.hpp").generic_string();
+    string oHpp = (current_path() / "O.hpp").generic_string();
+    string nPcm = (current_path() / "N .pcm").generic_string();
+    string oPcm = (current_path() / "O .pcm").generic_string();
+    string xHpp = (current_path() / "X.hpp").generic_string();
+    string yHpp = (current_path() / "Y.hpp").generic_string();
+    string zHpp = (current_path() / "Z.hpp").generic_string();
+    string bigHpp = (current_path() / "Big.hpp").generic_string();
+    string bigPcm = (current_path() / "Big .pcm").generic_string();
+    string fooPcm = (current_path() / "Foo .pcm").generic_string();
+    string fooObj = (current_path() / "Foo .o").generic_string();
+    string mainObj = (current_path() / "main .o").generic_string();
+
+    // compiling A-C.cpp
+    {
+        const auto &r = makeIPCManagerBS(aCObj);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + aCObj +
+                                "\" -noScanIPC -c -xc++-module A-C.cpp -fmodule-output=\"" + aCPcm + "\"";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+
+        if (ctbLastMessage.logicalName != "A:C")
+        {
+            return tl::unexpected("wrong logical name received while compiling A-C.cpp");
+        }
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling A-B.cpp
+    {
+        const auto &r = makeIPCManagerBS(aBObj);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + aBObj +
+                                "\" -noScanIPC -c -xc++-module A-B.cpp -fmodule-output=\"" + aBPcm + "\"";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+
+        if (ctbLastMessage.logicalName != "A:B")
+        {
+            return tl::unexpected("wrong logical name received while compiling A-B.cpp");
+        }
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling A.cpp
+    {
+        const auto &r = makeIPCManagerBS(aObj);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + aObj +
+                                "\" -noScanIPC -c -xc++-module A.cpp -fmodule-output=\"" + aPcm + "\"";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbModule = reinterpret_cast<CTBModule &>(buffer);
+
+        if (ctbModule.moduleName != "A:B")
+        {
+            return tl::unexpected("wrong logical name received while compiling A-B.cpp");
+        }
+        printMessage(ctbModule, false);
+
+        BTCModule btcMod;
+        btcMod.requested.filePath = aBPcm;
+        ModuleDep modDep;
+        modDep.file.filePath = aCPcm;
+        modDep.logicalName = "A:C";
+        modDep.isHeaderUnit = false;
+        btcMod.deps.emplace_back(std::move(modDep));
+
+        if (const auto &r2 = manager.sendMessage(std::move(btcMod)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling N.hpp
+    {
+        const auto &r = makeIPCManagerBS(nPcm);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + nPcm +
+                                "\" -noScanIPC -xc++-header N.hpp -DCOMMAND_MACRO";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+
+        if (ctbNonModMHpp.logicalName != "M.hpp" || ctbNonModMHpp.isHeaderUnit == true)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCNonModule nonModMPcm;
+        nonModMPcm.isHeaderUnit = false;
+        nonModMPcm.filePath = mHpp;
+        if (const auto &r2 = manager.sendMessage(std::move(nonModMPcm)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling O.hpp
+    {
+        const auto &r = makeIPCManagerBS(oPcm);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand =
+            CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + oPcm + "\" -noScanIPC -xc++-header O.hpp";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+        if (ctbNonModMHpp.logicalName != "M.hpp" || ctbNonModMHpp.isHeaderUnit == true)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCNonModule nonModMPcm;
+        nonModMPcm.isHeaderUnit = false;
+        nonModMPcm.filePath = mHpp;
+        if (const auto &r2 = manager.sendMessage(std::move(nonModMPcm)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbNonModNHpp = reinterpret_cast<CTBNonModule &>(buffer);
+        if (ctbNonModNHpp.logicalName != "N.hpp" || ctbNonModNHpp.isHeaderUnit == false)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCNonModule nonModNPcm;
+        nonModNPcm.isHeaderUnit = true;
+        nonModNPcm.filePath = nPcm;
+
+        if (const auto &r2 = manager.sendMessage(std::move(nonModNPcm)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling O.hpp with include-translation. BTCNonModule for N.hpp will be sent with
+    // isHeaderUnit = true and its filePath = nPcm.
+    {
+        const auto &r = makeIPCManagerBS(oPcm);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + oPcm +
+                                "\" -noScanIPC -xc++-header O.hpp -DTRANSLATING";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+        if (ctbNonModMHpp.logicalName != "M.hpp" || ctbNonModMHpp.isHeaderUnit == true)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCNonModule nonModMPcm;
+        nonModMPcm.isHeaderUnit = false;
+        nonModMPcm.filePath = mHpp;
+        if (const auto &r2 = manager.sendMessage(std::move(nonModMPcm)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbNonModNHpp = reinterpret_cast<CTBNonModule &>(buffer);
+        if (ctbNonModNHpp.logicalName != "N.hpp" || ctbNonModNHpp.isHeaderUnit == true)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCNonModule nonModNPcm;
+        nonModNPcm.isHeaderUnit = true;
+        nonModNPcm.filePath = nPcm;
+
+        if (const auto &r2 = manager.sendMessage(std::move(nonModNPcm)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling Big.hpp
+    {
+        const auto &r = makeIPCManagerBS(bigPcm);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand =
+            CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + bigPcm + "\" -noScanIPC -xc++-header Big.hpp";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        auto sendHeaderFile = [&manager, &type, &buffer](const string &headerFileName,
+                                                         const string &filePath) -> tl::expected<int, string> {
+            if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+            {
+                string str = r2.error();
+                return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+            }
+
+            if (type != CTB::NON_MODULE)
+            {
+                return tl::unexpected("received message of wrong type");
+            }
+            const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+
+            if (ctbNonModMHpp.logicalName != headerFileName || ctbNonModMHpp.isHeaderUnit == true)
+            {
+                return tl::unexpected("wrong message received");
+            }
+
+            BTCNonModule headerFile;
+            headerFile.isHeaderUnit = false;
+            headerFile.filePath = filePath;
+            if (const auto &r2 = manager.sendMessage(std::move(headerFile)); !r2)
+            {
+                string str = r2.error();
+                return tl::unexpected("manager send message failed" + r2.error() + "\n");
+            }
+            return {};
+        };
+
+        if (const auto &r2 = sendHeaderFile("X.hpp", xHpp); !r2)
+        {
+            return r2;
+        }
+
+        if (const auto &r2 = sendHeaderFile("Y.hpp", yHpp); !r2)
+        {
+            return r2;
+        }
+
+        if (const auto &r2 = sendHeaderFile("Z.hpp", zHpp); !r2)
+        {
+            return r2;
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling Foo.cpp
+    {
+        const auto &r = makeIPCManagerBS(fooObj);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + fooObj +
+                                "\" -noScanIPC -c -xc++-module Foo.cpp -fmodule-output=\"" + fooPcm + "\"";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &xHeader = reinterpret_cast<CTBNonModule &>(buffer);
+
+        if (xHeader.logicalName != "X.hpp" || xHeader.isHeaderUnit == true)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCNonModule bigHu;
+        bigHu.isHeaderUnit = true;
+        bigHu.filePath = bigPcm;
+
+        if (const auto &r2 = manager.sendMessage(bigHu); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::NON_MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &zHeader = reinterpret_cast<CTBNonModule &>(buffer);
+
+        if (zHeader.logicalName != "Z.hpp" || zHeader.isHeaderUnit == true)
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        if (const auto &r2 = manager.sendMessage(bigHu); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+        const auto &aModule = reinterpret_cast<CTBModule &>(buffer);
+
+        if (aModule.moduleName != "A")
+        {
+            return tl::unexpected("wrong message received");
+        }
+
+        BTCModule amod;
+        amod.requested.filePath = aPcm;
+        ModuleDep d;
+        d.isHeaderUnit = false;
+        d.file.filePath = aBPcm;
+        d.logicalName = "A:B";
+        amod.deps.emplace_back(d);
+        d.file.filePath = aCPcm;
+        d.logicalName = "A:C";
+        amod.deps.emplace_back(d);
+
+        if (const auto &r2 = manager.sendMessage(amod); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+    }
+
+    // compiling main.cpp
+    auto compileMain = [&](bool shouldFail) -> tl::expected<int, string> {
+        const auto &r = makeIPCManagerBS(mainObj);
+        if (!r)
+        {
+            return tl::unexpected("creating manager failed" + r.error() + "\n");
+        }
+
+        const IPCManagerBS &manager = *r;
+
+        string compileCommand = CLANG_CMD R"( -std=c++20 -o ")" + mainObj + "\" -noScanIPC -c main.cpp";
+        if (const auto &r2 = Run(compileCommand); !r2)
+        {
+            return tl::unexpected(r2.error());
+        }
+
+        CTB type;
+        char buffer[320];
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::MODULE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbModule = reinterpret_cast<CTBModule &>(buffer);
+
+        if (ctbModule.moduleName != "Foo")
+        {
+            return tl::unexpected("wrong logical name received while compiling A-B.cpp");
+        }
+        printMessage(ctbModule, false);
+
+        BTCModule btcMod;
+        btcMod.requested.filePath = fooPcm;
+        ModuleDep modDep;
+        modDep.file.filePath = bigPcm;
+        modDep.logicalName = "Big.hpp";
+        modDep.isHeaderUnit = true;
+        btcMod.deps.emplace_back(std::move(modDep));
+        modDep.isHeaderUnit = false;
+        modDep.file.filePath = aPcm;
+        modDep.logicalName = "A";
+        btcMod.deps.emplace_back(std::move(modDep));
+        modDep.file.filePath = aBPcm;
+        modDep.logicalName = "A:B";
+        btcMod.deps.emplace_back(std::move(modDep));
+        modDep.file.filePath = aCPcm;
+        modDep.logicalName = "A:C";
+        btcMod.deps.emplace_back(std::move(modDep));
+
+        if (const auto &r2 = manager.sendMessage(std::move(btcMod)); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
+        }
+
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
+
+        if (type != CTB::LAST_MESSAGE)
+        {
+            return tl::unexpected("received message of wrong type");
+        }
+
+        const auto &ctbLastMessage = reinterpret_cast<CTBLastMessage &>(buffer);
+        if (ctbLastMessage.errorOccurred != shouldFail)
+        {
+            return tl::unexpected("wrong last message received");
+        }
+
+        printMessage(ctbLastMessage, false);
+        manager.closeConnection();
+        if (const auto &r2 = CloseProcess(); !r2)
+        {
+            return tl::unexpected("closing process failed");
+        }
+        return {};
+    };
+
+    if (const auto &r = compileMain(true); !r)
+    {
+        string str = r.error();
+        return tl::unexpected("compiling main failed" + r.error() + "\n");
+    }
+    // main.cpp
+    const string mainDotCpp = R"(
+import Foo;
+// only foo will be requested as A is already sent with it.
+import A;
+
+int main()
+{
+    Hello();
+    World();
+    Foo();
+}
+)";
+
+    ofstream("main.cpp") << mainDotCpp;
+
+    if (const auto &r = compileMain(false); !r)
+    {
+        string str = r.error();
+        return tl::unexpected("compiling main failed" + r.error() + "\n");
+    }
+
+    fflush(stdout);
+    return {};
+}
+} // namespace
+#ifdef IS_THIS_CLANG_REPO
+TEST(IPC2978Test, IPC2978Test)
+{
+    const path p = current_path();
+    current_path(LLVM_TOOLS_BINARY_DIR);
+    const path mainFilePath = (LLVM_TOOLS_BINARY_DIR / path("main .o")).lexically_normal();
+    remove(mainFilePath);
+
+    const auto &r = runTest();
+    current_path(p);
+    if (!r)
+    {
+        FAIL() << r.error();
+    }
+    if (!exists(mainFilePath))
+    {
+        FAIL() << "main.o not found\n";
+    }
+}
+#else
+int main()
+{
+    remove(path("main .o"));
+    if (const auto &r = runTest(); !r)
+    {
+        fmt::print("{}\n", r.error());
+        return EXIT_FAILURE;
+    }
+    if (!exists(path("main .o")))
+    {
+        fmt::print("main.o not found\n");
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+#endif

>From 54d20f5acc2addbab23f01ac06da1d19f019ae4e Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 14:47:29 +0500
Subject: [PATCH 02/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/include/clang/Driver/Options.td         |   4 +-
 clang/include/clang/IPC2978/IPCManagerBS.hpp  |  16 +-
 .../clang/IPC2978/IPCManagerCompiler.hpp      |  92 ++++++---
 clang/include/clang/IPC2978/Manager.hpp       |  92 +++++----
 clang/include/clang/IPC2978/Messages.hpp      |  49 +++--
 clang/lib/Frontend/CompilerInstance.cpp       |  28 ++-
 clang/lib/Frontend/FrontendAction.cpp         |   3 +
 clang/lib/IPC2978/IPCManagerBS.cpp            |  45 +++--
 clang/lib/IPC2978/IPCManagerCompiler.cpp      | 107 +++++++---
 clang/lib/IPC2978/Manager.cpp                 | 186 ++++++++++++------
 clang/lib/Lex/PPDirectives.cpp                |  43 ++--
 clang/lib/Parse/ParseAST.cpp                  |   1 +
 clang/unittests/IPC2978/IPC2978Test.cpp       |  28 +--
 13 files changed, 445 insertions(+), 249 deletions(-)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 439926a8b0d23..b69b43c5a92ab 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9493,6 +9493,6 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
   HelpText<"Enable the wasm-opt optimizer (default)">,
   MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
 
-def no_scan_ipc : Flag<["-"], "noScanIPC">,
-                  Visibility<[ClangOption, CC1Option]>,
+def no_scan_ipc : Flag<["-", "/"], "noScanIPC">,
+                  Visibility<[ClangOption, CC1Option, CLOption]>,
                   HelpText<"Enable No scan IPC approach">;
\ No newline at end of file
diff --git a/clang/include/clang/IPC2978/IPCManagerBS.hpp b/clang/include/clang/IPC2978/IPCManagerBS.hpp
index f9222a55f3fc4..0722254bc9e27 100644
--- a/clang/include/clang/IPC2978/IPCManagerBS.hpp
+++ b/clang/include/clang/IPC2978/IPCManagerBS.hpp
@@ -11,7 +11,7 @@ namespace N2978
 // IPC Manager BuildSystem
 class IPCManagerBS : Manager
 {
-    friend tl::expected<IPCManagerBS, string> makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath);
+    friend tl::expected<IPCManagerBS, std::string> makeIPCManagerBS(std::string BMIIfHeaderUnitObjOtherwisePath);
     bool connectedToCompiler = false;
 
 #ifdef _WIN32
@@ -25,15 +25,15 @@ class IPCManagerBS : Manager
     IPCManagerBS &operator=(const IPCManagerBS &) = default;
     IPCManagerBS(IPCManagerBS &&) = default;
     IPCManagerBS &operator=(IPCManagerBS &&) = default;
-    tl::expected<void, string> receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const;
-    [[nodiscard]] tl::expected<void, string> sendMessage(const BTCModule &moduleFile) const;
-    [[nodiscard]] tl::expected<void, string> sendMessage(const BTCNonModule &nonModule) const;
-    [[nodiscard]] tl::expected<void, string> sendMessage(const BTCLastMessage &lastMessage) const;
-    static tl::expected<ProcessMappingOfBMIFile, string> createSharedMemoryBMIFile(const BMIFile &bmiFile);
-    static tl::expected<void, string> closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile);
+    tl::expected<void, std::string> receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const;
+    [[nodiscard]] tl::expected<void, std::string> sendMessage(const BTCModule &moduleFile) const;
+    [[nodiscard]] tl::expected<void, std::string> sendMessage(const BTCNonModule &nonModule) const;
+    [[nodiscard]] tl::expected<void, std::string> sendMessage(const BTCLastMessage &lastMessage) const;
+    static tl::expected<ProcessMappingOfBMIFile, std::string> createSharedMemoryBMIFile(const BMIFile &bmiFile);
+    static tl::expected<void, std::string> closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile);
     void closeConnection() const;
 };
 
-tl::expected<IPCManagerBS, string> makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath);
+tl::expected<IPCManagerBS, std::string> makeIPCManagerBS(std::string BMIIfHeaderUnitObjOtherwisePath);
 } // namespace N2978
 #endif // IPC_MANAGER_BS_HPP
diff --git a/clang/include/clang/IPC2978/IPCManagerCompiler.hpp b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
index d58a118849c2a..21a6ffc91724d 100644
--- a/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
+++ b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
@@ -5,35 +5,52 @@
 #include "clang/IPC2978/Manager.hpp"
 #include "clang/IPC2978/expected.hpp"
 
-using std::string_view;
 namespace N2978
 {
 
+enum class ResponseType
+{
+    MODULE,
+    HEADER_UNIT,
+    HEADER_FILE
+};
+
+struct Response
+{
+    // if type == HEADER_FILE, then fileSize has no meaning
+    BMIFile file;
+    ResponseType type;
+    bool user;
+    Response(BMIFile file_, ResponseType type_, bool user_);
+};
+
 // IPC Manager Compiler
 class IPCManagerCompiler : Manager
 {
-    template <typename T> tl::expected<T, string> receiveMessage() const;
+    template <typename T> tl::expected<T, std::string> receiveMessage() const;
     // This is not exposed. sendCTBLastMessage calls this.
-    [[nodiscard]] tl::expected<void, string> receiveBTCLastMessage() const;
+    [[nodiscard]] tl::expected<void, std::string> receiveBTCLastMessage() const;
 
   public:
     CTBLastMessage lastMessage{};
+    std::unordered_map<std::string, Response> responses;
 #ifdef _WIN32
     explicit IPCManagerCompiler(void *hPipe_);
 #else
     explicit IPCManagerCompiler(int fdSocket_);
 #endif
-    [[nodiscard]] tl::expected<BTCModule, string> receiveBTCModule(const CTBModule &moduleName) const;
-    [[nodiscard]] tl::expected<BTCNonModule, string> receiveBTCNonModule(const CTBNonModule &nonModule) const;
-    [[nodiscard]] tl::expected<void, string> sendCTBLastMessage(const CTBLastMessage &lastMessage) const;
-    [[nodiscard]] tl::expected<void, string> sendCTBLastMessage(const CTBLastMessage &lastMessage,
-                                                                const string &bmiFile, const string &filePath) const;
-    static tl::expected<ProcessMappingOfBMIFile, string> readSharedMemoryBMIFile(const BMIFile &file);
-    static tl::expected<void, string> closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile);
+    [[nodiscard]] tl::expected<BTCModule, std::string> receiveBTCModule(const CTBModule &moduleName);
+    [[nodiscard]] tl::expected<BTCNonModule, std::string> receiveBTCNonModule(const CTBNonModule &nonModule);
+    [[nodiscard]] tl::expected<void, std::string> sendCTBLastMessage(const CTBLastMessage &lastMessage) const;
+    [[nodiscard]] tl::expected<void, std::string> sendCTBLastMessage(const CTBLastMessage &lastMessage,
+                                                                     const std::string &bmiFile,
+                                                                     const std::string &filePath) const;
+    static tl::expected<ProcessMappingOfBMIFile, std::string> readSharedMemoryBMIFile(const BMIFile &file);
+    static tl::expected<void, std::string> closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile);
     void closeConnection() const;
 };
 
-template <typename T> tl::expected<T, string> IPCManagerCompiler::receiveMessage() const
+template <typename T> tl::expected<T, std::string> IPCManagerCompiler::receiveMessage() const
 {
     // Read from the pipe.
     char buffer[BUFFERSIZE];
@@ -57,15 +74,23 @@ template <typename T> tl::expected<T, string> IPCManagerCompiler::receiveMessage
             return tl::unexpected(r.error());
         }
 
-        const auto &r2 = readVectorOfModuleDepFromPipe(buffer, bytesRead, bytesProcessed);
+        const auto &r2 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
         if (!r2)
         {
             return tl::unexpected(r2.error());
         }
 
+        const auto &r3 = readVectorOfModuleDepFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r3)
+        {
+            return tl::unexpected(r3.error());
+        }
+
         BTCModule moduleFile;
         moduleFile.requested = *r;
-        moduleFile.deps = *r2;
+        moduleFile.user = *r2;
+        moduleFile.modDeps = *r3;
+
         if (bytesRead == bytesProcessed)
         {
             return moduleFile;
@@ -79,13 +104,13 @@ template <typename T> tl::expected<T, string> IPCManagerCompiler::receiveMessage
             return tl::unexpected(r.error());
         }
 
-        const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+        const auto &r2 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
         if (!r2)
         {
             return tl::unexpected(r2.error());
         }
 
-        const auto &r3 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+        const auto &r3 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
         if (!r3)
         {
             return tl::unexpected(r3.error());
@@ -97,18 +122,32 @@ template <typename T> tl::expected<T, string> IPCManagerCompiler::receiveMessage
             return tl::unexpected(r4.error());
         }
 
-        const auto &r5 = readVectorOfHuDepFromPipe(buffer, bytesRead, bytesProcessed);
+        const auto &r5 = readVectorOfStringFromPipe(buffer, bytesRead, bytesProcessed);
         if (!r5)
         {
             return tl::unexpected(r5.error());
         }
 
+        const auto &r6 = readVectorOfHeaderFileFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r6)
+        {
+            return tl::unexpected(r6.error());
+        }
+
+        const auto &r7 = readVectorOfHuDepFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r7)
+        {
+            return tl::unexpected(r7.error());
+        }
+
         BTCNonModule nonModule;
         nonModule.isHeaderUnit = *r;
-        nonModule.filePath = *r2;
-        nonModule.user = *r3;
+        nonModule.user = *r2;
+        nonModule.filePath = *r3;
         nonModule.fileSize = *r4;
-        nonModule.deps = *r5;
+        nonModule.logicalNames = *r5;
+        nonModule.headerFiles = *r6;
+        nonModule.huDeps = *r7;
 
         if (bytesRead == bytesProcessed)
         {
@@ -124,23 +163,12 @@ template <typename T> tl::expected<T, string> IPCManagerCompiler::receiveMessage
     {
         return tl::unexpected(getErrorString(bytesRead, bytesProcessed));
     }
-    string str = __FILE__;
+    std::string str = __FILE__;
     str += ':';
     str += std::to_string(__LINE__);
     return tl::unexpected(getErrorString("N2978 IPC API internal error" + str));
 }
-[[nodiscard]] tl::expected<IPCManagerCompiler, string> makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath);
+[[nodiscard]] tl::expected<IPCManagerCompiler, std::string> makeIPCManagerCompiler(std::string BMIIfHeaderUnitObjOtherwisePath);
 inline IPCManagerCompiler *managerCompiler;
-inline CTBLastMessage lastMessage;
-
-// Equality operator for use in unordered_map
-bool operator==(const CTBNonModule &lhs, const CTBNonModule &rhs);
-// Hash function for CTBNonModule
-struct CTBNonModuleHash
-{
-    uint64_t operator()(const CTBNonModule &ctb) const;
-};
-
-inline std::unordered_map<CTBNonModule, BTCNonModule, CTBNonModuleHash> respnses;
 } // namespace N2978
 #endif // IPC_MANAGER_COMPILER_HPP
diff --git a/clang/include/clang/IPC2978/Manager.hpp b/clang/include/clang/IPC2978/Manager.hpp
index 580e1878b3ef3..71ffdca111d03 100644
--- a/clang/include/clang/IPC2978/Manager.hpp
+++ b/clang/include/clang/IPC2978/Manager.hpp
@@ -8,8 +8,6 @@
 #include <string>
 #include <vector>
 
-using std::string, std::vector, std::string_view;
-
 #define BUFFERSIZE 4096
 
 #ifdef _WIN32
@@ -35,18 +33,18 @@ enum class ErrorCategory : uint8_t
     UNKNOWN_CTB_TYPE,
 };
 
-string getErrorString();
-string getErrorString(uint32_t bytesRead_, uint32_t bytesProcessed_);
-string getErrorString(ErrorCategory errorCategory_);
+std::string getErrorString();
+std::string getErrorString(uint32_t bytesRead_, uint32_t bytesProcessed_);
+std::string getErrorString(ErrorCategory errorCategory_);
 // to facilitate error propagation.
-inline string getErrorString(string err)
+inline std::string getErrorString(std::string err)
 {
     return err;
 }
 
 struct ProcessMappingOfBMIFile
 {
-    string_view file;
+    std::string_view file;
 #ifdef _WIN32
     void *mapping;
     void *view;
@@ -65,41 +63,51 @@ class Manager
     int fdSocket = 0;
 #endif
 
-    tl::expected<uint32_t, string> readInternal(char (&buffer)[BUFFERSIZE]) const;
-    tl::expected<void, string> writeInternal(const vector<char> &buffer) const;
-
-    static vector<char> getBufferWithType(CTB type);
-    static void writeUInt32(vector<char> &buffer, uint32_t value);
-    static void writeString(vector<char> &buffer, const string &str);
-    static void writeProcessMappingOfBMIFile(vector<char> &buffer, const BMIFile &file);
-    static void writeModuleDep(vector<char> &buffer, const ModuleDep &dep);
-    static void writeHuDep(vector<char> &buffer, const HuDep &dep);
-    static void writeVectorOfStrings(vector<char> &buffer, const vector<string> &strs);
-    static void writeVectorOfProcessMappingOfBMIFiles(vector<char> &buffer, const vector<BMIFile> &files);
-    static void writeVectorOfModuleDep(vector<char> &buffer, const vector<ModuleDep> &deps);
-    static void writeVectorOfHuDep(vector<char> &buffer, const vector<HuDep> &deps);
-
-    tl::expected<bool, string> readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                uint32_t &bytesProcessed) const;
-    tl::expected<uint32_t, string> readUInt32FromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                      uint32_t &bytesProcessed) const;
-    tl::expected<string, string> readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                    uint32_t &bytesProcessed) const;
-    tl::expected<BMIFile, string> readProcessMappingOfBMIFileFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                                      uint32_t &bytesProcessed) const;
-    tl::expected<vector<string>, string> readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                                    uint32_t &bytesProcessed) const;
-    tl::expected<ModuleDep, string> readModuleDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                          uint32_t &bytesProcessed) const;
-    tl::expected<vector<ModuleDep>, string> readVectorOfModuleDepFromPipe(char (&buffer)[BUFFERSIZE],
-                                                                          uint32_t &bytesRead,
-                                                                          uint32_t &bytesProcessed) const;
-    tl::expected<HuDep, string> readHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                  uint32_t &bytesProcessed) const;
-    tl::expected<vector<HuDep>, string> readVectorOfHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                                  uint32_t &bytesProcessed) const;
-    tl::expected<void, string> readNumberOfBytes(char *output, uint32_t size, char (&buffer)[BUFFERSIZE],
-                                                 uint32_t &bytesRead, uint32_t &bytesProcessed) const;
+    tl::expected<uint32_t, std::string> readInternal(char (&buffer)[BUFFERSIZE]) const;
+    tl::expected<void, std::string> writeInternal(const std::vector<char> &buffer) const;
+
+    static std::vector<char> getBufferWithType(CTB type);
+    static void writeUInt32(std::vector<char> &buffer, uint32_t value);
+    static void writeString(std::vector<char> &buffer, const std::string &str);
+    static void writeProcessMappingOfBMIFile(std::vector<char> &buffer, const BMIFile &file);
+    static void writeModuleDep(std::vector<char> &buffer, const ModuleDep &dep);
+    static void writeHuDep(std::vector<char> &buffer, const HuDep &dep);
+    static void writeHeaderFile(std::vector<char> &buffer, const HeaderFile &dep);
+    static void writeVectorOfStrings(std::vector<char> &buffer, const std::vector<std::string> &strs);
+    static void writeVectorOfProcessMappingOfBMIFiles(std::vector<char> &buffer, const std::vector<BMIFile> &files);
+    static void writeVectorOfModuleDep(std::vector<char> &buffer, const std::vector<ModuleDep> &deps);
+    static void writeVectorOfHuDeps(std::vector<char> &buffer, const std::vector<HuDep> &deps);
+    static void writeVectorOfHeaderFiles(std::vector<char> &buffer, const std::vector<HeaderFile> &headerFiles);
+
+    tl::expected<bool, std::string> readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                     uint32_t &bytesProcessed) const;
+    tl::expected<uint32_t, std::string> readUInt32FromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                           uint32_t &bytesProcessed) const;
+    tl::expected<std::string, std::string> readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                              uint32_t &bytesProcessed) const;
+    tl::expected<BMIFile, std::string> readProcessMappingOfBMIFileFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                           uint32_t &bytesRead,
+                                                                           uint32_t &bytesProcessed) const;
+    tl::expected<std::vector<std::string>, std::string> readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                                   uint32_t &bytesRead,
+                                                                                   uint32_t &bytesProcessed) const;
+    tl::expected<ModuleDep, std::string> readModuleDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                               uint32_t &bytesProcessed) const;
+    tl::expected<std::vector<ModuleDep>, std::string> readVectorOfModuleDepFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                                    uint32_t &bytesRead,
+                                                                                    uint32_t &bytesProcessed) const;
+    tl::expected<HuDep, std::string> readHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                       uint32_t &bytesProcessed) const;
+    tl::expected<std::vector<HuDep>, std::string> readVectorOfHuDepFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                            uint32_t &bytesRead,
+                                                                            uint32_t &bytesProcessed) const;
+    tl::expected<HeaderFile, std::string> readHeaderFileFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                                 uint32_t &bytesProcessed) const;
+    tl::expected<std::vector<HeaderFile>, std::string> readVectorOfHeaderFileFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                                      uint32_t &bytesRead,
+                                                                                      uint32_t &bytesProcessed) const;
+    tl::expected<void, std::string> readNumberOfBytes(char *output, uint32_t size, char (&buffer)[BUFFERSIZE],
+                                                      uint32_t &bytesRead, uint32_t &bytesProcessed) const;
 };
 
 template <typename T, typename... Args> constexpr T *construct_at(T *p, Args &&...args)
diff --git a/clang/include/clang/IPC2978/Messages.hpp b/clang/include/clang/IPC2978/Messages.hpp
index ca27679120095..65adc37857b72 100644
--- a/clang/include/clang/IPC2978/Messages.hpp
+++ b/clang/include/clang/IPC2978/Messages.hpp
@@ -6,11 +6,8 @@
 #include <string>
 #include <vector>
 
-using std::string, std::vector;
-
 namespace N2978
 {
-
 // CTB --> Compiler to Build-System
 // BTC --> Build-System to Compiler
 
@@ -30,7 +27,7 @@ enum class CTB : uint8_t
 // This is sent when the compiler needs a module.
 struct CTBModule
 {
-    string moduleName;
+    std::string moduleName;
 };
 
 // This is sent when the compiler needs something else than a module.
@@ -38,7 +35,7 @@ struct CTBModule
 struct CTBNonModule
 {
     bool isHeaderUnit = false;
-    string logicalName;
+    std::string logicalName;
 };
 
 // This is the last message sent by the compiler.
@@ -48,12 +45,12 @@ struct CTBLastMessage
     bool errorOccurred = false;
     // Following fields are meaningless if the compilation failed.
     // compiler output
-    string output;
+    std::string output;
     // compiler error output.
     // Any IPC related error output should be reported on stderr.
-    string errorOutput;
+    std::string errorOutput;
     // exported module name if any.
-    string logicalName;
+    std::string logicalName;
     // This is communicated because the receiving process has no
     // way to learn the shared memory file size on both Windows
     // and Linux without a filesystem call.
@@ -73,43 +70,61 @@ enum class BTC : uint8_t
 
 struct BMIFile
 {
-    string filePath;
+    std::string filePath;
     uint32_t fileSize = UINT32_MAX;
 };
 
 struct ModuleDep
 {
-    BMIFile file;
-    string logicalName;
     bool isHeaderUnit;
+    BMIFile file;
+    // if isHeaderUnit == true, then the following might
+    // contain more than one values, as header-unit can be
+    // composed of multiple header-files. And if later,
+    // any of the following logicalNames is included or
+    // imported, the following can be used instead.
+    std::vector<std::string> logicalNames;
+    bool user = true;
 };
 
 // Reply for CTBModule
 struct BTCModule
 {
     BMIFile requested;
-    vector<ModuleDep> deps;
+    bool user = true;
+    std::vector<ModuleDep> modDeps;
 };
 
 struct HuDep
 {
     BMIFile file;
-    string logicalName;
+    std::vector<std::string> logicalNames;
     // whether header-unit / header-file belongs to user or system directory.
     bool user = true;
 };
 
+struct HeaderFile
+{
+    std::string logicalName;
+    std::string filePath;
+    bool user = true;
+};
+
 // Reply for CTBNonModule
 struct BTCNonModule
 {
     bool isHeaderUnit = false;
-    string filePath;
-    // if isHeaderUnit == false, the following three are meaning-less.
-    // whether header-unit / header-file belongs to user or system directory.
     bool user = true;
+    std::string filePath;
+    // if isHeaderUnit == false, the following are meaning-less.
     // if isHeaderUnit == true, fileSize of the requested file.
     uint32_t fileSize;
-    vector<HuDep> deps;
+    std::vector<std::string> logicalNames;
+    // build-system might send the following on first request, if it knows that a
+    // header-unit is being compiled that compose multiple header-files to reduce
+    // the number of subsequent requests.
+    std::vector<HeaderFile> headerFiles;
+    std::vector<HuDep> huDeps;
 };
 
 // Reply for CTBLastMessage if the compilation succeeded.
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index b85645523c4a2..322bb18a1d3b2 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1905,31 +1905,27 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
     // in case of noScanIPC, PrebuiltModuleFiles is empty, so we initialize it
     // from the build-system here, so the selectModuleSource() call
     // later-on will return ModuleSource::MS_PrebuiltModulePath.
-    auto &PrebuiltModuleFiles =
-        const_cast<std::map<std::string, std::string, std::less<>> &>(
-            HS.getHeaderSearchOpts().PrebuiltModuleFiles);
-    if (auto it = PrebuiltModuleFiles.find(ModuleName);
-        it == PrebuiltModuleFiles.end()) {
+    auto &Responses = N2978::managerCompiler->responses;
+    if (const auto it = Responses.find(std::string(ModuleName));
+        it == Responses.end() || it->second.type != N2978::ResponseType::MODULE) {
       N2978::CTBModule mod;
       mod.moduleName = ModuleName;
       if (const auto &r =
               N2978::managerCompiler->receiveBTCModule(std::move(mod));
           r) {
-        auto &[requested, deps] = r.value();
-        PrebuiltModuleFiles.emplace(std::move(ModuleName),
+        auto &[requested, user, deps] = r.value();
+        const_cast<std::map<std::string, std::string, std::less<>> &>(
+            HS.getHeaderSearchOpts().PrebuiltModuleFiles).emplace(ModuleName,
                                     std::move(requested.filePath));
-        for (const auto &[file, logicalName, isHeaderUnit] : deps) {
-          if (isHeaderUnit) {
-            loadIPCReceivedHeaderUnit(file.filePath);
-          } else {
-            PrebuiltModuleFiles.emplace(std::move(logicalName),
-                                        std::move(file.filePath));
-          }
-        }
       } else {
-        string errorMessage = r.error();
+        // receive failed
       }
     }
+    else {
+      const_cast<std::map<std::string, std::string, std::less<>> &>(
+          HS.getHeaderSearchOpts().PrebuiltModuleFiles).emplace(ModuleName,
+                                  std::move(it->second.file.filePath));
+    }
   }
 
   // Select the source and filename for loading the named module.
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index a892ffbed70ca..d24f603fa18ca 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -837,6 +837,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     if (const auto &r = N2978::makeIPCManagerCompiler(OutputPath); r) {
       N2978::managerCompiler = new N2978::IPCManagerCompiler(r.value());
     }
+    else {
+      // making manager failed
+    }
   }
 
   bool HasBegunSourceFile = false;
diff --git a/clang/lib/IPC2978/IPCManagerBS.cpp b/clang/lib/IPC2978/IPCManagerBS.cpp
index 6af8d5f5458e6..cf03c4bcd540d 100644
--- a/clang/lib/IPC2978/IPCManagerBS.cpp
+++ b/clang/lib/IPC2978/IPCManagerBS.cpp
@@ -15,12 +15,11 @@
 #include <sys/un.h>
 #include <unistd.h>
 #endif
-using std::string;
 
 namespace N2978
 {
 
-tl::expected<IPCManagerBS, string> makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath)
+tl::expected<IPCManagerBS, std::string> makeIPCManagerBS(std::string BMIIfHeaderUnitObjOtherwisePath)
 {
 #ifdef _WIN32
     BMIIfHeaderUnitObjOtherwisePath = R"(\\.\pipe\)" + BMIIfHeaderUnitObjOtherwisePath;
@@ -103,7 +102,7 @@ IPCManagerBS::IPCManagerBS(const int fdSocket_)
 #endif
 
 bool checked = false;
-tl::expected<void, string> IPCManagerBS::receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const
+tl::expected<void, std::string> IPCManagerBS::receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const
 {
     if (!connectedToCompiler)
     {
@@ -113,7 +112,19 @@ tl::expected<void, string> IPCManagerBS::receiveMessage(char (&ctbBuffer)[320],
             // Is the client already connected?
             if (GetLastError() != ERROR_PIPE_CONNECTED)
             {
-                return tl::unexpected(getErrorString());
+
+                DWORD bytesAvail = 0;
+                DWORD bytesLeftThisMessage = 0;
+
+                // PeekNamedPipe returns FALSE if pipe is disconnected
+                if (PeekNamedPipe(hPipe, nullptr, 0, nullptr, &bytesAvail, &bytesLeftThisMessage))
+                {
+                    // compiler process ended and has left a message for us.
+                }
+                else
+                {
+                    return tl::unexpected(getErrorString());
+                }
             }
         }
 #else
@@ -242,11 +253,12 @@ tl::expected<void, string> IPCManagerBS::receiveMessage(char (&ctbBuffer)[320],
     return {};
 }
 
-tl::expected<void, string> IPCManagerBS::sendMessage(const BTCModule &moduleFile) const
+tl::expected<void, std::string> IPCManagerBS::sendMessage(const BTCModule &moduleFile) const
 {
-    vector<char> buffer;
+    std::vector<char> buffer;
     writeProcessMappingOfBMIFile(buffer, moduleFile.requested);
-    writeVectorOfModuleDep(buffer, moduleFile.deps);
+    buffer.emplace_back(moduleFile.user);
+    writeVectorOfModuleDep(buffer, moduleFile.modDeps);
     if (const auto &r = writeInternal(buffer); !r)
     {
         return tl::unexpected(r.error());
@@ -254,14 +266,16 @@ tl::expected<void, string> IPCManagerBS::sendMessage(const BTCModule &moduleFile
     return {};
 }
 
-tl::expected<void, string> IPCManagerBS::sendMessage(const BTCNonModule &nonModule) const
+tl::expected<void, std::string> IPCManagerBS::sendMessage(const BTCNonModule &nonModule) const
 {
-    vector<char> buffer;
+    std::vector<char> buffer;
     buffer.emplace_back(nonModule.isHeaderUnit);
-    writeString(buffer, nonModule.filePath);
     buffer.emplace_back(nonModule.user);
+    writeString(buffer, nonModule.filePath);
     writeUInt32(buffer, nonModule.fileSize);
-    writeVectorOfHuDep(buffer, nonModule.deps);
+    writeVectorOfStrings(buffer, nonModule.logicalNames);
+    writeVectorOfHeaderFiles(buffer, nonModule.headerFiles);
+    writeVectorOfHuDeps(buffer, nonModule.huDeps);
     if (const auto &r = writeInternal(buffer); !r)
     {
         return tl::unexpected(r.error());
@@ -269,9 +283,9 @@ tl::expected<void, string> IPCManagerBS::sendMessage(const BTCNonModule &nonModu
     return {};
 }
 
-tl::expected<void, string> IPCManagerBS::sendMessage(const BTCLastMessage &) const
+tl::expected<void, std::string> IPCManagerBS::sendMessage(const BTCLastMessage &) const
 {
-    vector<char> buffer;
+    std::vector<char> buffer;
     buffer.emplace_back(true);
     if (const auto &r = writeInternal(buffer); !r)
     {
@@ -280,7 +294,7 @@ tl::expected<void, string> IPCManagerBS::sendMessage(const BTCLastMessage &) con
     return {};
 }
 
-tl::expected<ProcessMappingOfBMIFile, string> IPCManagerBS::createSharedMemoryBMIFile(const BMIFile &bmiFile)
+tl::expected<ProcessMappingOfBMIFile, std::string> IPCManagerBS::createSharedMemoryBMIFile(const BMIFile &bmiFile)
 {
     ProcessMappingOfBMIFile sharedFile{};
 #ifdef _WIN32
@@ -316,7 +330,8 @@ tl::expected<ProcessMappingOfBMIFile, string> IPCManagerBS::createSharedMemoryBM
 #endif
 }
 
-tl::expected<void, string> IPCManagerBS::closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile)
+tl::expected<void, std::string> IPCManagerBS::closeBMIFileMapping(
+    const ProcessMappingOfBMIFile &processMappingOfBMIFile)
 {
 #ifdef _WIN32
     CloseHandle(processMappingOfBMIFile.mapping);
diff --git a/clang/lib/IPC2978/IPCManagerCompiler.cpp b/clang/lib/IPC2978/IPCManagerCompiler.cpp
index 54790f75c348d..1074fbdaf80ba 100644
--- a/clang/lib/IPC2978/IPCManagerCompiler.cpp
+++ b/clang/lib/IPC2978/IPCManagerCompiler.cpp
@@ -5,6 +5,7 @@
 #include "clang/IPC2978/rapidhash.h"
 
 #include <string>
+#include <utility>
 
 #ifdef _WIN32
 #include <Windows.h>
@@ -18,12 +19,10 @@
 #include <unistd.h>
 #endif
 
-using std::string;
-
 namespace N2978
 {
 
-tl::expected<IPCManagerCompiler, string> makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath)
+tl::expected<IPCManagerCompiler, std::string> makeIPCManagerCompiler(std::string BMIIfHeaderUnitObjOtherwisePath)
 {
 #ifdef _WIN32
     BMIIfHeaderUnitObjOtherwisePath = R"(\\.\pipe\)" + BMIIfHeaderUnitObjOtherwisePath;
@@ -86,7 +85,12 @@ IPCManagerCompiler::IPCManagerCompiler(const int fdSocket_)
 }
 #endif
 
-tl::expected<void, string> IPCManagerCompiler::receiveBTCLastMessage() const
+Response::Response(BMIFile file_, const ResponseType type_, const bool user_)
+    : file(std::move(file_)), type(type_), user(user_)
+{
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::receiveBTCLastMessage() const
 {
     char buffer[BUFFERSIZE];
     uint32_t bytesRead;
@@ -112,35 +116,94 @@ tl::expected<void, string> IPCManagerCompiler::receiveBTCLastMessage() const
     return {};
 }
 
-tl::expected<BTCModule, string> IPCManagerCompiler::receiveBTCModule(const CTBModule &moduleName) const
+tl::expected<BTCModule, std::string> IPCManagerCompiler::receiveBTCModule(const CTBModule &moduleName)
 {
 
-    // raise(SIGTRAP); // At the location of the BP.
-    vector<char> buffer = getBufferWithType(CTB::MODULE);
+    std::vector<char> buffer = getBufferWithType(CTB::MODULE);
     writeString(buffer, moduleName.moduleName);
     if (const auto &r = writeInternal(buffer); !r)
     {
         return tl::unexpected(r.error());
     }
 
-    return receiveMessage<BTCModule>();
+    const auto &received = receiveMessage<BTCModule>();
+
+    if (received)
+    {
+        auto &[f, user, deps] = received.value();
+        responses.emplace(moduleName.moduleName, Response(f, ResponseType::MODULE, user));
+        for (const auto &[isHeaderUnit, file, logicalNames, user] : deps)
+        {
+            if (isHeaderUnit)
+            {
+                for (const std::string &s : logicalNames)
+                {
+                    responses.emplace(s, Response(file, ResponseType::HEADER_UNIT, user));
+                }
+            }
+            else
+            {
+                responses.emplace(logicalNames[0], Response(file, ResponseType::MODULE, user));
+            }
+        }
+    }
+    return received;
 }
 
-tl::expected<BTCNonModule, string> IPCManagerCompiler::receiveBTCNonModule(const CTBNonModule &nonModule) const
+tl::expected<BTCNonModule, std::string> IPCManagerCompiler::receiveBTCNonModule(const CTBNonModule &nonModule)
 {
-    vector<char> buffer = getBufferWithType(CTB::NON_MODULE);
+    std::vector<char> buffer = getBufferWithType(CTB::NON_MODULE);
     buffer.emplace_back(nonModule.isHeaderUnit);
     writeString(buffer, nonModule.logicalName);
     if (const auto &r = writeInternal(buffer); !r)
     {
         return tl::unexpected(r.error());
     }
-    return receiveMessage<BTCNonModule>();
+
+    const auto &received = receiveMessage<BTCNonModule>();
+
+    if (received)
+    {
+        const auto &[isHeaderUnit, user, filePath, fileSize, logicalNames, headerFiles, huDeps] = received.value();
+
+        BMIFile f;
+        f.filePath = filePath;
+        f.fileSize = fileSize;
+
+        if (!isHeaderUnit)
+        {
+            responses.emplace(nonModule.logicalName, Response(f, ResponseType::HEADER_FILE, user));
+            return received;
+        }
+
+        responses.emplace(nonModule.logicalName, Response(f, ResponseType::HEADER_UNIT, user));
+
+        for (const std::string &h : logicalNames)
+        {
+            responses.emplace(h, Response(f, ResponseType::HEADER_UNIT, user));
+        }
+
+        for (const auto &[logicalName, filePath, user] : headerFiles)
+        {
+            BMIFile headerBMI;
+            headerBMI.filePath = filePath;
+            responses.emplace(logicalName, Response(headerBMI, ResponseType::HEADER_FILE, user));
+        }
+
+        for (const auto &[file, logicalNames, user] : huDeps)
+        {
+            for (const std::string &l : logicalNames)
+            {
+                responses.emplace(l, Response(file, ResponseType::HEADER_UNIT, user));
+            }
+        }
+    }
+    return received;
 }
 
-tl::expected<void, string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage) const
+tl::expected<void, std::string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage) const
 {
-    vector<char> buffer = getBufferWithType(CTB::LAST_MESSAGE);
+    std::vector<char> buffer = getBufferWithType(CTB::LAST_MESSAGE);
     buffer.emplace_back(lastMessage.errorOccurred);
     writeString(buffer, lastMessage.output);
     writeString(buffer, lastMessage.errorOutput);
@@ -153,8 +216,9 @@ tl::expected<void, string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastM
     return {};
 }
 
-tl::expected<void, string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage,
-                                                                  const string &bmiFile, const string &filePath) const
+tl::expected<void, std::string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage,
+                                                                       const std::string &bmiFile,
+                                                                       const std::string &filePath) const
 {
 #ifdef _WIN32
     const HANDLE hFile = CreateFileA(filePath.c_str(), GENERIC_READ | GENERIC_WRITE,
@@ -262,7 +326,7 @@ tl::expected<void, string> IPCManagerCompiler::sendCTBLastMessage(const CTBLastM
     return {};
 }
 
-tl::expected<ProcessMappingOfBMIFile, string> IPCManagerCompiler::readSharedMemoryBMIFile(const BMIFile &file)
+tl::expected<ProcessMappingOfBMIFile, std::string> IPCManagerCompiler::readSharedMemoryBMIFile(const BMIFile &file)
 {
     ProcessMappingOfBMIFile f{};
 #ifdef _WIN32
@@ -319,7 +383,7 @@ tl::expected<ProcessMappingOfBMIFile, string> IPCManagerCompiler::readSharedMemo
     return f;
 }
 
-tl::expected<void, string> IPCManagerCompiler::closeBMIFileMapping(
+tl::expected<void, std::string> IPCManagerCompiler::closeBMIFileMapping(
     const ProcessMappingOfBMIFile &processMappingOfBMIFile)
 {
 #ifdef _WIN32
@@ -347,13 +411,4 @@ bool operator==(const CTBNonModule &lhs, const CTBNonModule &rhs)
 {
     return lhs.isHeaderUnit == rhs.isHeaderUnit && lhs.logicalName == rhs.logicalName;
 }
-
-uint64_t CTBNonModuleHash::operator()(const CTBNonModule &ctb) const
-{
-    const_cast<char &>(ctb.logicalName[ctb.logicalName.size()]) = ctb.isHeaderUnit;
-    const uint64_t hash = rapidhash(ctb.logicalName.data(), ctb.logicalName.size() + 1);
-    const_cast<char &>(ctb.logicalName[ctb.logicalName.size()]) = '\0';
-    return hash;
-}
-
 } // namespace N2978
\ No newline at end of file
diff --git a/clang/lib/IPC2978/Manager.cpp b/clang/lib/IPC2978/Manager.cpp
index c6d1c334692ae..305415bc71497 100644
--- a/clang/lib/IPC2978/Manager.cpp
+++ b/clang/lib/IPC2978/Manager.cpp
@@ -14,7 +14,7 @@
 namespace N2978
 {
 
-string getErrorString()
+std::string getErrorString()
 {
 #ifdef _WIN32
     const DWORD err = GetLastError();
@@ -30,7 +30,7 @@ string getErrorString()
         return fallback_msg;
     }
 
-    string msg = msg_buf;
+    std::string msg = msg_buf;
     LocalFree(msg_buf);
     return msg;
 #else
@@ -38,15 +38,15 @@ string getErrorString()
 #endif
 }
 
-string getErrorString(const uint32_t bytesRead_, const uint32_t bytesProcessed_)
+std::string getErrorString(const uint32_t bytesRead_, const uint32_t bytesProcessed_)
 {
     return "Error: Bytes Readd vs Bytes Processed Mismatch.\nBytes Read: " + std::to_string(bytesRead_) +
            ", Bytes Processed: " + std::to_string(bytesProcessed_);
 }
 
-string getErrorString(const ErrorCategory errorCategory_)
+std::string getErrorString(const ErrorCategory errorCategory_)
 {
-    string errorString;
+    std::string errorString;
 
     switch (errorCategory_)
     {
@@ -60,7 +60,7 @@ string getErrorString(const ErrorCategory errorCategory_)
         errorString = "Error: Unknown CTB message received.";
         break;
     case ErrorCategory::NONE:
-        string str = __FILE__;
+        std::string str = __FILE__;
         str += ':';
         str += __LINE__;
         errorString = "N2978 IPC API internal error" + str;
@@ -70,7 +70,7 @@ string getErrorString(const ErrorCategory errorCategory_)
     return errorString;
 }
 
-tl::expected<uint32_t, string> Manager::readInternal(char (&buffer)[BUFFERSIZE]) const
+tl::expected<uint32_t, std::string> Manager::readInternal(char (&buffer)[BUFFERSIZE]) const
 {
     uint32_t bytesRead;
 
@@ -131,7 +131,7 @@ tl::expected<void, string> writeAll(const int fd, const char *buffer, const uint
 }
 #endif
 
-tl::expected<void, string> Manager::writeInternal(const vector<char> &buffer) const
+tl::expected<void, std::string> Manager::writeInternal(const std::vector<char> &buffer) const
 {
 #ifdef _WIN32
     const bool success = WriteFile(hPipe,         // pipe handle
@@ -152,55 +152,63 @@ tl::expected<void, string> Manager::writeInternal(const vector<char> &buffer) co
     return {};
 }
 
-vector<char> Manager::getBufferWithType(CTB type)
+std::vector<char> Manager::getBufferWithType(CTB type)
 {
-    vector<char> buffer;
+    std::vector<char> buffer;
     buffer.emplace_back(static_cast<uint8_t>(type));
     return buffer;
 }
 
-void Manager::writeUInt32(vector<char> &buffer, const uint32_t value)
+void Manager::writeUInt32(std::vector<char> &buffer, const uint32_t value)
 {
     const auto ptr = reinterpret_cast<const char *>(&value);
     buffer.insert(buffer.end(), ptr, ptr + 4);
 }
 
-void Manager::writeString(vector<char> &buffer, const string &str)
+void Manager::writeString(std::vector<char> &buffer, const std::string &str)
 {
     writeUInt32(buffer, str.size());
     buffer.insert(buffer.end(), str.begin(), str.end()); // Insert all characters
 }
 
-void Manager::writeProcessMappingOfBMIFile(vector<char> &buffer, const BMIFile &file)
+void Manager::writeProcessMappingOfBMIFile(std::vector<char> &buffer, const BMIFile &file)
 {
     writeString(buffer, file.filePath);
     writeUInt32(buffer, file.fileSize);
 }
 
-void Manager::writeModuleDep(vector<char> &buffer, const ModuleDep &dep)
+void Manager::writeModuleDep(std::vector<char> &buffer, const ModuleDep &dep)
 {
-    writeProcessMappingOfBMIFile(buffer, dep.file);
-    writeString(buffer, dep.logicalName);
     buffer.emplace_back(dep.isHeaderUnit);
+    writeProcessMappingOfBMIFile(buffer, dep.file);
+    writeVectorOfStrings(buffer, dep.logicalNames);
+    buffer.emplace_back(dep.user);
 }
 
-void Manager::writeHuDep(vector<char> &buffer, const HuDep &dep)
+void Manager::writeHuDep(std::vector<char> &buffer, const HuDep &dep)
 {
     writeProcessMappingOfBMIFile(buffer, dep.file);
+    writeVectorOfStrings(buffer, dep.logicalNames);
+    buffer.emplace_back(dep.user);
+}
+
+void Manager::writeHeaderFile(std::vector<char> &buffer, const HeaderFile &dep)
+{
     writeString(buffer, dep.logicalName);
+    writeString(buffer, dep.filePath);
     buffer.emplace_back(dep.user);
 }
 
-void Manager::writeVectorOfStrings(vector<char> &buffer, const vector<string> &strs)
+void Manager::writeVectorOfStrings(std::vector<char> &buffer, const std::vector<std::string> &strs)
 {
     writeUInt32(buffer, strs.size());
-    for (const string &str : strs)
+    for (const std::string &str : strs)
     {
         writeString(buffer, str);
     }
 }
 
-void Manager::writeVectorOfProcessMappingOfBMIFiles(vector<char> &buffer, const vector<BMIFile> &files)
+void Manager::writeVectorOfProcessMappingOfBMIFiles(std::vector<char> &buffer, const std::vector<BMIFile> &files)
 {
     writeUInt32(buffer, files.size());
     for (const BMIFile &file : files)
@@ -209,7 +217,7 @@ void Manager::writeVectorOfProcessMappingOfBMIFiles(vector<char> &buffer, const
     }
 }
 
-void Manager::writeVectorOfModuleDep(vector<char> &buffer, const vector<ModuleDep> &deps)
+void Manager::writeVectorOfModuleDep(std::vector<char> &buffer, const std::vector<ModuleDep> &deps)
 {
     writeUInt32(buffer, deps.size());
     for (const ModuleDep &dep : deps)
@@ -218,7 +226,7 @@ void Manager::writeVectorOfModuleDep(vector<char> &buffer, const vector<ModuleDe
     }
 }
 
-void Manager::writeVectorOfHuDep(vector<char> &buffer, const vector<HuDep> &deps)
+void Manager::writeVectorOfHuDeps(std::vector<char> &buffer, const std::vector<HuDep> &deps)
 {
     writeUInt32(buffer, deps.size());
     for (const HuDep &dep : deps)
@@ -227,8 +235,17 @@ void Manager::writeVectorOfHuDep(vector<char> &buffer, const vector<HuDep> &deps
     }
 }
 
-tl::expected<bool, string> Manager::readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                     uint32_t &bytesProcessed) const
+void Manager::writeVectorOfHeaderFiles(std::vector<char> &buffer, const std::vector<HeaderFile> &headerFiles)
+{
+    writeUInt32(buffer, headerFiles.size());
+    for (const HeaderFile &headerFile : headerFiles)
+    {
+        writeHeaderFile(buffer, headerFile);
+    }
+}
+
+tl::expected<bool, std::string> Manager::readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                          uint32_t &bytesProcessed) const
 {
     bool result;
     const auto &r =
@@ -240,8 +257,8 @@ tl::expected<bool, string> Manager::readBoolFromPipe(char (&buffer)[BUFFERSIZE],
     return result;
 }
 
-tl::expected<uint32_t, string> Manager::readUInt32FromPipe(char (&buffer)[4096], uint32_t &bytesRead,
-                                                           uint32_t &bytesProcessed) const
+tl::expected<uint32_t, std::string> Manager::readUInt32FromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                                uint32_t &bytesProcessed) const
 {
     uint32_t size;
     if (const auto &r = readNumberOfBytes(reinterpret_cast<char *>(&size), 4, buffer, bytesRead, bytesProcessed); !r)
@@ -251,8 +268,8 @@ tl::expected<uint32_t, string> Manager::readUInt32FromPipe(char (&buffer)[4096],
     return size;
 }
 
-tl::expected<string, string> Manager::readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
-                                                         uint32_t &bytesProcessed) const
+tl::expected<std::string, std::string> Manager::readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                                   uint32_t &bytesProcessed) const
 {
     auto r = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
     if (!r)
@@ -260,7 +277,7 @@ tl::expected<string, string> Manager::readStringFromPipe(char (&buffer)[BUFFERSI
         return tl::unexpected(r.error());
     }
     const uint32_t stringSize = *r;
-    string str(stringSize, 'a');
+    std::string str(stringSize, 'a');
     if (const auto &r2 = readNumberOfBytes(str.data(), stringSize, buffer, bytesRead, bytesProcessed); !r2)
     {
         return tl::unexpected(r2.error());
@@ -268,8 +285,9 @@ tl::expected<string, string> Manager::readStringFromPipe(char (&buffer)[BUFFERSI
     return str;
 }
 
-tl::expected<BMIFile, string> Manager::readProcessMappingOfBMIFileFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
-                                                                           uint32_t &bytesProcessed) const
+tl::expected<BMIFile, std::string> Manager::readProcessMappingOfBMIFileFromPipe(char (&buffer)[4096],
+                                                                                uint32_t &bytesRead,
+                                                                                uint32_t &bytesProcessed) const
 {
     const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed);
     if (!r)
@@ -284,9 +302,9 @@ tl::expected<BMIFile, string> Manager::readProcessMappingOfBMIFileFromPipe(char
     return file;
 }
 
-tl::expected<vector<string>, string> Manager::readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE],
-                                                                         uint32_t &bytesRead,
-                                                                         uint32_t &bytesProcessed) const
+tl::expected<std::vector<std::string>, std::string> Manager::readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE],
+                                                                                        uint32_t &bytesRead,
+                                                                                        uint32_t &bytesProcessed) const
 {
     const auto &r = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
     if (!r)
@@ -294,7 +312,7 @@ tl::expected<vector<string>, string> Manager::readVectorOfStringFromPipe(char (&
         return tl::unexpected(r.error());
     }
     const uint32_t vectorSize = *r;
-    vector<string> vec;
+    std::vector<std::string> vec;
     vec.reserve(vectorSize);
     for (uint32_t i = 0; i < vectorSize; ++i)
     {
@@ -308,22 +326,22 @@ tl::expected<vector<string>, string> Manager::readVectorOfStringFromPipe(char (&
     return vec;
 }
 
-tl::expected<ModuleDep, string> Manager::readModuleDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
-                                                               uint32_t &bytesProcessed) const
+tl::expected<ModuleDep, std::string> Manager::readModuleDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                                    uint32_t &bytesProcessed) const
 {
-    const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    const auto &r = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
     if (!r)
     {
         return tl::unexpected(r.error());
     }
 
-    const auto &r2 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    const auto &r2 = readProcessMappingOfBMIFileFromPipe(buffer, bytesRead, bytesProcessed);
     if (!r2)
     {
         return tl::unexpected(r2.error());
     }
 
-    const auto &r3 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    const auto &r3 = readVectorOfStringFromPipe(buffer, bytesRead, bytesProcessed);
     if (!r3)
     {
         return tl::unexpected(r3.error());
@@ -337,17 +355,17 @@ tl::expected<ModuleDep, string> Manager::readModuleDepFromPipe(char (&buffer)[40
 
     ModuleDep modDep;
 
-    modDep.file.filePath = *r;
-    modDep.file.fileSize = *r2;
-    modDep.logicalName = *r3;
-    modDep.isHeaderUnit = *r4;
+    modDep.isHeaderUnit = *r;
+    modDep.file = *r2;
+    modDep.logicalNames = *r3;
+    modDep.user = *r4;
 
     return modDep;
 }
 
-tl::expected<vector<ModuleDep>, string> Manager::readVectorOfModuleDepFromPipe(char (&buffer)[4096],
-                                                                               uint32_t &bytesRead,
-                                                                               uint32_t &bytesProcessed) const
+tl::expected<std::vector<ModuleDep>, std::string> Manager::readVectorOfModuleDepFromPipe(char (&buffer)[4096],
+                                                                                         uint32_t &bytesRead,
+                                                                                         uint32_t &bytesProcessed) const
 {
     const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
     if (!vectorSize)
@@ -355,7 +373,7 @@ tl::expected<vector<ModuleDep>, string> Manager::readVectorOfModuleDepFromPipe(c
         return tl::unexpected(vectorSize.error());
     }
 
-    vector<ModuleDep> vec;
+    std::vector<ModuleDep> vec;
     vec.reserve(*vectorSize);
     for (uint32_t i = 0; i < *vectorSize; ++i)
     {
@@ -370,8 +388,8 @@ tl::expected<vector<ModuleDep>, string> Manager::readVectorOfModuleDepFromPipe(c
     return vec;
 }
 
-tl::expected<HuDep, string> Manager::readHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
-                                                       uint32_t &bytesProcessed) const
+tl::expected<HuDep, std::string> Manager::readHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                            uint32_t &bytesProcessed) const
 {
     const auto &r = readProcessMappingOfBMIFileFromPipe(buffer, bytesRead, bytesProcessed);
     if (!r)
@@ -379,7 +397,7 @@ tl::expected<HuDep, string> Manager::readHuDepFromPipe(char (&buffer)[4096], uin
         return tl::unexpected(r.error());
     }
 
-    const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    const auto &r2 = readVectorOfStringFromPipe(buffer, bytesRead, bytesProcessed);
     if (!r2)
     {
         return tl::unexpected(r2.error());
@@ -393,13 +411,14 @@ tl::expected<HuDep, string> Manager::readHuDepFromPipe(char (&buffer)[4096], uin
 
     HuDep huDep;
     huDep.file = *r;
-    huDep.logicalName = *r2;
+    huDep.logicalNames = *r2;
     huDep.user = *r3;
     return huDep;
 }
 
-tl::expected<vector<HuDep>, string> Manager::readVectorOfHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
-                                                                       uint32_t &bytesProcessed) const
+tl::expected<std::vector<HuDep>, std::string> Manager::readVectorOfHuDepFromPipe(char (&buffer)[4096],
+                                                                                 uint32_t &bytesRead,
+                                                                                 uint32_t &bytesProcessed) const
 {
     const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
     if (!vectorSize)
@@ -407,7 +426,7 @@ tl::expected<vector<HuDep>, string> Manager::readVectorOfHuDepFromPipe(char (&bu
         return tl::unexpected(vectorSize.error());
     }
 
-    vector<HuDep> vec;
+    std::vector<HuDep> vec;
     vec.reserve(*vectorSize);
     for (uint32_t i = 0; i < *vectorSize; ++i)
     {
@@ -421,8 +440,61 @@ tl::expected<vector<HuDep>, string> Manager::readVectorOfHuDepFromPipe(char (&bu
     return vec;
 }
 
-tl::expected<void, string> Manager::readNumberOfBytes(char *output, const uint32_t size, char (&buffer)[BUFFERSIZE],
-                                                      uint32_t &bytesRead, uint32_t &bytesProcessed) const
+tl::expected<HeaderFile, std::string> Manager::readHeaderFileFromPipe(char (&buffer)[4096], uint32_t &bytesRead,
+                                                                      uint32_t &bytesProcessed) const
+{
+
+    const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r)
+    {
+        return tl::unexpected(r.error());
+    }
+
+    const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r2)
+    {
+        return tl::unexpected(r2.error());
+    }
+
+    const auto &r3 = readBoolFromPipe(buffer, bytesRead, bytesProcessed);
+    if (!r3)
+    {
+        return tl::unexpected(r3.error());
+    }
+
+    HeaderFile hf;
+    hf.logicalName = *r;
+    hf.filePath = *r2;
+    hf.user = *r3;
+    return hf;
+}
+
+tl::expected<std::vector<HeaderFile>, std::string> Manager::readVectorOfHeaderFileFromPipe(
+    char (&buffer)[4096], uint32_t &bytesRead, uint32_t &bytesProcessed) const
+{
+    const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed);
+    if (!vectorSize)
+    {
+        return tl::unexpected(vectorSize.error());
+    }
+
+    std::vector<HeaderFile> vec;
+    vec.reserve(*vectorSize);
+    for (uint32_t i = 0; i < *vectorSize; ++i)
+    {
+        const auto &r = readHeaderFileFromPipe(buffer, bytesRead, bytesProcessed);
+        if (!r)
+        {
+            return tl::unexpected(r.error());
+        }
+        vec.emplace_back(*r);
+    }
+    return vec;
+}
+
+tl::expected<void, std::string> Manager::readNumberOfBytes(char *output, const uint32_t size,
+                                                           char (&buffer)[BUFFERSIZE], uint32_t &bytesRead,
+                                                           uint32_t &bytesProcessed) const
 {
     uint32_t pendingSize = size;
     uint32_t offset = 0;
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 39fcf28978aed..330a6af0e733c 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2328,32 +2328,35 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   ModuleMap::KnownHeader SuggestedModule;
   OptionalFileEntryRef File;
 
-  N2978::BTCNonModule BTCNonMod;
+  std::string filePath;
+  bool isHeaderUnit = false;
   if (N2978::managerCompiler) {
-    N2978::CTBNonModule CTBNonMod;
-    CTBNonMod.isHeaderUnit = IsImportDecl;
-    CTBNonMod.logicalName = Filename.str();
-
-    if (auto it = N2978::respnses.find(CTBNonMod);
-        it != N2978::respnses.end()) {
-      BTCNonMod = it->second;
-    } else {
-      // todo
-      //  check if an already received header-unit possess this header-file.
-      //  do not send in that case.
+    auto &Responses = N2978::managerCompiler->responses;
+    if (auto it = Responses.find(std::string(Filename));
+        it == Responses.end() || it->second.type == N2978::ResponseType::MODULE) {
+      N2978::CTBNonModule CTBNonMod;
+      CTBNonMod.isHeaderUnit = IsImportDecl;
+      CTBNonMod.logicalName = Filename.str();
       if (const auto &Result =
               N2978::managerCompiler->receiveBTCNonModule(std::move(CTBNonMod));
-          Result)
-        BTCNonMod = *Result;
-      N2978::respnses.emplace(CTBNonMod, BTCNonMod);
+          Result) {
+           filePath = Result->filePath;
+        isHeaderUnit = Result->isHeaderUnit;
+      }
+      else {
+        // receive failed
+      }
+    } else {
+      filePath = it->second.file.filePath;
+      isHeaderUnit = it->second.type == N2978::ResponseType::HEADER_UNIT;
     }
 
-    File = getFileManager().getOptionalFileRef(BTCNonMod.filePath);
+    File = getFileManager().getOptionalFileRef(filePath);
 
-    if (BTCNonMod.isHeaderUnit) {
+    if (isHeaderUnit) {
       IsImportDecl = true;
       SuggestedModule = {
-          getModuleLoader().loadIPCReceivedHeaderUnit(BTCNonMod.filePath),
+          getModuleLoader().loadIPCReceivedHeaderUnit(filePath),
           ModuleMap::NormalHeader};
     }
   } else {
@@ -2403,7 +2406,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
 
   bool MaybeTranslateInclude =
       Action == Enter && File && ModuleToImport &&
-      (!ModuleToImport->isForBuilding(getLangOpts()) || BTCNonMod.isHeaderUnit);
+      (!ModuleToImport->isForBuilding(getLangOpts()) || isHeaderUnit);
 
   // Maybe a usable Header Unit
   bool UsableHeaderUnit = false;
@@ -2566,7 +2569,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   // than the one we're about to open. Not checked if it is an IPC received
   // module.
   const bool CheckIncludePathPortability =
-      !BTCNonMod.isHeaderUnit && !IsMapped &&
+      !isHeaderUnit && !IsMapped &&
       !File->getFileEntry().tryGetRealPathName().empty();
 
   if (CheckIncludePathPortability) {
diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp
index 53a44ba05161d..06f6f59e55bea 100644
--- a/clang/lib/Parse/ParseAST.cpp
+++ b/clang/lib/Parse/ParseAST.cpp
@@ -195,6 +195,7 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
     if (const auto &r =
             N2978::managerCompiler->sendCTBLastMessage(std::move(Last));
         r) {
+      N2978::managerCompiler->closeConnection();
     }
   }
 
diff --git a/clang/unittests/IPC2978/IPC2978Test.cpp b/clang/unittests/IPC2978/IPC2978Test.cpp
index 3f62c5f906d1c..f868242062027 100644
--- a/clang/unittests/IPC2978/IPC2978Test.cpp
+++ b/clang/unittests/IPC2978/IPC2978Test.cpp
@@ -429,9 +429,9 @@ tl::expected<int, string> runTest()
         btcMod.requested.filePath = aBPcm;
         ModuleDep modDep;
         modDep.file.filePath = aCPcm;
-        modDep.logicalName = "A:C";
+        modDep.logicalNames.emplace_back("A:C");
         modDep.isHeaderUnit = false;
-        btcMod.deps.emplace_back(std::move(modDep));
+        btcMod.modDeps.emplace_back(std::move(modDep));
 
         if (const auto &r2 = manager.sendMessage(std::move(btcMod)); !r2)
         {
@@ -880,11 +880,11 @@ tl::expected<int, string> runTest()
         ModuleDep d;
         d.isHeaderUnit = false;
         d.file.filePath = aBPcm;
-        d.logicalName = "A:B";
-        amod.deps.emplace_back(d);
+        d.logicalNames.emplace_back("A:B");
+        amod.modDeps.emplace_back(d);
         d.file.filePath = aCPcm;
-        d.logicalName = "A:C";
-        amod.deps.emplace_back(d);
+        d.logicalNames.emplace_back("A:C");
+        amod.modDeps.emplace_back(d);
 
         if (const auto &r2 = manager.sendMessage(amod); !r2)
         {
@@ -953,19 +953,19 @@ tl::expected<int, string> runTest()
         btcMod.requested.filePath = fooPcm;
         ModuleDep modDep;
         modDep.file.filePath = bigPcm;
-        modDep.logicalName = "Big.hpp";
+        modDep.logicalNames = "Big.hpp";
         modDep.isHeaderUnit = true;
-        btcMod.deps.emplace_back(std::move(modDep));
+        btcMod.modDeps.emplace_back(std::move(modDep));
         modDep.isHeaderUnit = false;
         modDep.file.filePath = aPcm;
-        modDep.logicalName = "A";
-        btcMod.deps.emplace_back(std::move(modDep));
+        modDep.logicalNames = "A";
+        btcMod.modDeps.emplace_back(std::move(modDep));
         modDep.file.filePath = aBPcm;
-        modDep.logicalName = "A:B";
-        btcMod.deps.emplace_back(std::move(modDep));
+        modDep.logicalNames = "A:B";
+        btcMod.modDeps.emplace_back(std::move(modDep));
         modDep.file.filePath = aCPcm;
-        modDep.logicalName = "A:C";
-        btcMod.deps.emplace_back(std::move(modDep));
+        modDep.logicalNames = "A:C";
+        btcMod.modDeps.emplace_back(std::move(modDep));
 
         if (const auto &r2 = manager.sendMessage(std::move(btcMod)); !r2)
         {

>From 04b79008cb56e67a1bc0f7f6ddc237e463b0db21 Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 14:57:48 +0500
Subject: [PATCH 03/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Frontend/CompilerInstance.cpp | 14 +++++++-------
 clang/lib/Frontend/FrontendAction.cpp   |  3 +--
 clang/lib/Lex/PPDirectives.cpp          | 13 ++++++-------
 3 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 322bb18a1d3b2..94caadfdbbe8c 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1907,7 +1907,8 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
     // later-on will return ModuleSource::MS_PrebuiltModulePath.
     auto &Responses = N2978::managerCompiler->responses;
     if (const auto it = Responses.find(std::string(ModuleName));
-        it == Responses.end() || it->second.type != N2978::ResponseType::MODULE) {
+        it == Responses.end() ||
+        it->second.type != N2978::ResponseType::MODULE) {
       N2978::CTBModule mod;
       mod.moduleName = ModuleName;
       if (const auto &r =
@@ -1915,16 +1916,15 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
           r) {
         auto &[requested, user, deps] = r.value();
         const_cast<std::map<std::string, std::string, std::less<>> &>(
-            HS.getHeaderSearchOpts().PrebuiltModuleFiles).emplace(ModuleName,
-                                    std::move(requested.filePath));
+            HS.getHeaderSearchOpts().PrebuiltModuleFiles)
+            .emplace(ModuleName, std::move(requested.filePath));
       } else {
         // receive failed
       }
-    }
-    else {
+    } else {
       const_cast<std::map<std::string, std::string, std::less<>> &>(
-          HS.getHeaderSearchOpts().PrebuiltModuleFiles).emplace(ModuleName,
-                                  std::move(it->second.file.filePath));
+          HS.getHeaderSearchOpts().PrebuiltModuleFiles)
+          .emplace(ModuleName, std::move(it->second.file.filePath));
     }
   }
 
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index d24f603fa18ca..eff6917287313 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -836,8 +836,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
                                  : Options.OutputFile;
     if (const auto &r = N2978::makeIPCManagerCompiler(OutputPath); r) {
       N2978::managerCompiler = new N2978::IPCManagerCompiler(r.value());
-    }
-    else {
+    } else {
       // making manager failed
     }
   }
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 330a6af0e733c..3a83861f75281 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2333,17 +2333,17 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   if (N2978::managerCompiler) {
     auto &Responses = N2978::managerCompiler->responses;
     if (auto it = Responses.find(std::string(Filename));
-        it == Responses.end() || it->second.type == N2978::ResponseType::MODULE) {
+        it == Responses.end() ||
+        it->second.type == N2978::ResponseType::MODULE) {
       N2978::CTBNonModule CTBNonMod;
       CTBNonMod.isHeaderUnit = IsImportDecl;
       CTBNonMod.logicalName = Filename.str();
       if (const auto &Result =
               N2978::managerCompiler->receiveBTCNonModule(std::move(CTBNonMod));
           Result) {
-           filePath = Result->filePath;
+        filePath = Result->filePath;
         isHeaderUnit = Result->isHeaderUnit;
-      }
-      else {
+      } else {
         // receive failed
       }
     } else {
@@ -2355,9 +2355,8 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
 
     if (isHeaderUnit) {
       IsImportDecl = true;
-      SuggestedModule = {
-          getModuleLoader().loadIPCReceivedHeaderUnit(filePath),
-          ModuleMap::NormalHeader};
+      SuggestedModule = {getModuleLoader().loadIPCReceivedHeaderUnit(filePath),
+                         ModuleMap::NormalHeader};
     }
   } else {
     File = LookupHeaderIncludeOrImport(

>From a211da66989ca9026fc6e05cf413036d5fc0200d Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 14:59:52 +0500
Subject: [PATCH 04/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Frontend/CompilerInstance.cpp |  6 +++---
 clang/lib/Lex/PPDirectives.cpp          | 24 +++++++++++++-----------
 2 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 94caadfdbbe8c..77e0de08dac24 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1909,10 +1909,10 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
     if (const auto it = Responses.find(std::string(ModuleName));
         it == Responses.end() ||
         it->second.type != N2978::ResponseType::MODULE) {
-      N2978::CTBModule mod;
-      mod.moduleName = ModuleName;
+      N2978::CTBModule Mod;
+      Mod.moduleName = ModuleName;
       if (const auto &r =
-              N2978::managerCompiler->receiveBTCModule(std::move(mod));
+              N2978::managerCompiler->receiveBTCModule(std::move(Mod));
           r) {
         auto &[requested, user, deps] = r.value();
         const_cast<std::map<std::string, std::string, std::less<>> &>(
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 3a83861f75281..14c284baee331 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2328,9 +2328,11 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   ModuleMap::KnownHeader SuggestedModule;
   OptionalFileEntryRef File;
 
-  std::string filePath;
-  bool isHeaderUnit = false;
+  bool IsHeaderUnit = false;
   if (N2978::managerCompiler) {
+
+    std::string FilePath;
+
     auto &Responses = N2978::managerCompiler->responses;
     if (auto it = Responses.find(std::string(Filename));
         it == Responses.end() ||
@@ -2341,21 +2343,21 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
       if (const auto &Result =
               N2978::managerCompiler->receiveBTCNonModule(std::move(CTBNonMod));
           Result) {
-        filePath = Result->filePath;
-        isHeaderUnit = Result->isHeaderUnit;
+        FilePath = Result->filePath;
+        IsHeaderUnit = Result->isHeaderUnit;
       } else {
         // receive failed
       }
     } else {
-      filePath = it->second.file.filePath;
-      isHeaderUnit = it->second.type == N2978::ResponseType::HEADER_UNIT;
+      FilePath = it->second.file.filePath;
+      IsHeaderUnit = it->second.type == N2978::ResponseType::HEADER_UNIT;
     }
 
-    File = getFileManager().getOptionalFileRef(filePath);
+    File = getFileManager().getOptionalFileRef(FilePath);
 
-    if (isHeaderUnit) {
+    if (IsHeaderUnit) {
       IsImportDecl = true;
-      SuggestedModule = {getModuleLoader().loadIPCReceivedHeaderUnit(filePath),
+      SuggestedModule = {getModuleLoader().loadIPCReceivedHeaderUnit(FilePath),
                          ModuleMap::NormalHeader};
     }
   } else {
@@ -2405,7 +2407,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
 
   bool MaybeTranslateInclude =
       Action == Enter && File && ModuleToImport &&
-      (!ModuleToImport->isForBuilding(getLangOpts()) || isHeaderUnit);
+      (!ModuleToImport->isForBuilding(getLangOpts()) || IsHeaderUnit);
 
   // Maybe a usable Header Unit
   bool UsableHeaderUnit = false;
@@ -2568,7 +2570,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   // than the one we're about to open. Not checked if it is an IPC received
   // module.
   const bool CheckIncludePathPortability =
-      !isHeaderUnit && !IsMapped &&
+      !IsHeaderUnit && !IsMapped &&
       !File->getFileEntry().tryGetRealPathName().empty();
 
   if (CheckIncludePathPortability) {

>From 7a641bd1ec0582f9198025a070a3bce839128b16 Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 17:24:56 +0500
Subject: [PATCH 05/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/IPC2978/IPCManagerCompiler.cpp | 44 ++++++-----
 clang/unittests/IPC2978/IPC2978Test.cpp  | 95 +++++++++---------------
 2 files changed, 56 insertions(+), 83 deletions(-)

diff --git a/clang/lib/IPC2978/IPCManagerCompiler.cpp b/clang/lib/IPC2978/IPCManagerCompiler.cpp
index 1074fbdaf80ba..5796a8573b0f7 100644
--- a/clang/lib/IPC2978/IPCManagerCompiler.cpp
+++ b/clang/lib/IPC2978/IPCManagerCompiler.cpp
@@ -138,12 +138,12 @@ tl::expected<BTCModule, std::string> IPCManagerCompiler::receiveBTCModule(const
             {
                 for (const std::string &s : logicalNames)
                 {
-                    responses.emplace(s, Response(file, ResponseType::HEADER_UNIT, user));
+                    responses.emplace(s, Response(std::move(file), ResponseType::HEADER_UNIT, user));
                 }
             }
             else
             {
-                responses.emplace(logicalNames[0], Response(file, ResponseType::MODULE, user));
+                responses.emplace(logicalNames[0], Response(std::move(file), ResponseType::MODULE, user));
             }
         }
     }
@@ -170,32 +170,30 @@ tl::expected<BTCNonModule, std::string> IPCManagerCompiler::receiveBTCNonModule(
         f.filePath = filePath;
         f.fileSize = fileSize;
 
-        if (!isHeaderUnit)
+        if (isHeaderUnit)
         {
-            responses.emplace(nonModule.logicalName, Response(f, ResponseType::HEADER_FILE, user));
-            return received;
-        }
-
-        responses.emplace(nonModule.logicalName, Response(f, ResponseType::HEADER_UNIT, user));
-
-        for (const std::string &h : logicalNames)
-        {
-            responses.emplace(h, Response(f, ResponseType::HEADER_UNIT, user));
-        }
-
-        for (const auto &[logicalName, filePath, user] : headerFiles)
-        {
-            BMIFile headerBMI;
-            headerBMI.filePath = filePath;
-            responses.emplace(logicalName, Response(headerBMI, ResponseType::HEADER_FILE, user));
+            for (const std::string &h : logicalNames)
+            {
+                responses.emplace(h, Response(f, ResponseType::HEADER_UNIT, user));
+            }
+            for (const auto &[file, logicalNames, user] : huDeps)
+            {
+                for (const std::string &l : logicalNames)
+                {
+                    responses.emplace(l, Response(std::move(file), ResponseType::HEADER_UNIT, user));
+                }
+            }
+            responses.emplace(nonModule.logicalName, Response(std::move(f), ResponseType::HEADER_UNIT, user));
         }
-
-        for (const auto &[file, logicalNames, user] : huDeps)
+        else
         {
-            for (const std::string &l : logicalNames)
+            for (const auto &[logicalName, filePath, user] : headerFiles)
             {
-                responses.emplace(l, Response(file, ResponseType::HEADER_UNIT, user));
+                BMIFile headerBMI;
+                headerBMI.filePath = filePath;
+                responses.emplace(logicalName, Response(std::move(headerBMI), ResponseType::HEADER_FILE, user));
             }
+            responses.emplace(nonModule.logicalName, Response(std::move(f), ResponseType::HEADER_FILE, user));
         }
     }
     return received;
diff --git a/clang/unittests/IPC2978/IPC2978Test.cpp b/clang/unittests/IPC2978/IPC2978Test.cpp
index f868242062027..c60b57bd72b6c 100644
--- a/clang/unittests/IPC2978/IPC2978Test.cpp
+++ b/clang/unittests/IPC2978/IPC2978Test.cpp
@@ -228,6 +228,8 @@ inline int z = x + y + 5;
 module;
 #include "X.hpp"
 #include "Z.hpp"
+#include "Y.hpp"
+#include "Big.hpp"
 export module Foo;
 import A;
 
@@ -614,8 +616,8 @@ tl::expected<int, string> runTest()
         }
     }
 
-    // compiling O.hpp with include-translation. BTCNonModule for N.hpp will be sent with
-    // isHeaderUnit = true and its filePath = nPcm.
+    // compiling O.hpp with include-translation. BTCNonModule for N.hpp will be received with
+    // isHeaderUnit = true.
     {
         const auto &r = makeIPCManagerBS(oPcm);
         if (!r)
@@ -723,49 +725,40 @@ tl::expected<int, string> runTest()
 
         CTB type;
         char buffer[320];
-        auto sendHeaderFile = [&manager, &type, &buffer](const string &headerFileName,
-                                                         const string &filePath) -> tl::expected<int, string> {
-            if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
-            {
-                string str = r2.error();
-                return tl::unexpected("manager receive message failed" + r2.error() + "\n");
-            }
-
-            if (type != CTB::NON_MODULE)
-            {
-                return tl::unexpected("received message of wrong type");
-            }
-            const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
-
-            if (ctbNonModMHpp.logicalName != headerFileName || ctbNonModMHpp.isHeaderUnit == true)
-            {
-                return tl::unexpected("wrong message received");
-            }
-
-            BTCNonModule headerFile;
-            headerFile.isHeaderUnit = false;
-            headerFile.filePath = filePath;
-            if (const auto &r2 = manager.sendMessage(std::move(headerFile)); !r2)
-            {
-                string str = r2.error();
-                return tl::unexpected("manager send message failed" + r2.error() + "\n");
-            }
-            return {};
-        };
+        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
+        {
+            string str = r2.error();
+            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
+        }
 
-        if (const auto &r2 = sendHeaderFile("X.hpp", xHpp); !r2)
+        if (type != CTB::NON_MODULE)
         {
-            return r2;
+            return tl::unexpected("received message of wrong type");
         }
+        const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
 
-        if (const auto &r2 = sendHeaderFile("Y.hpp", yHpp); !r2)
+        if (ctbNonModMHpp.logicalName != "X.hpp" || ctbNonModMHpp.isHeaderUnit == true)
         {
-            return r2;
+            return tl::unexpected("wrong message received");
         }
 
-        if (const auto &r2 = sendHeaderFile("Z.hpp", zHpp); !r2)
+        BTCNonModule headerFile;
+        headerFile.isHeaderUnit = false;
+        headerFile.filePath = xHpp;
+        HeaderFile h;
+        h.logicalName = "Y.hpp";
+        h.filePath = yHpp;
+        h.user = true;
+        headerFile.headerFiles.emplace_back(std::move(h));
+        h.logicalName = "Z.hpp";
+        h.filePath = zHpp;
+        h.user = true;
+        headerFile.headerFiles.emplace_back(std::move(h));
+
+        if (const auto &r2 = manager.sendMessage(std::move(headerFile)); !r2)
         {
-            return r2;
+            string str = r2.error();
+            return tl::unexpected("manager send message failed" + r2.error() + "\n");
         }
 
         if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
@@ -774,6 +767,7 @@ tl::expected<int, string> runTest()
             return tl::unexpected("manager receive message failed" + r2.error() + "\n");
         }
 
+        CTBNonModule &non = reinterpret_cast<CTBNonModule &>(buffer);
         if (type != CTB::LAST_MESSAGE)
         {
             return tl::unexpected("received message of wrong type");
@@ -828,29 +822,8 @@ tl::expected<int, string> runTest()
         BTCNonModule bigHu;
         bigHu.isHeaderUnit = true;
         bigHu.filePath = bigPcm;
-
-        if (const auto &r2 = manager.sendMessage(bigHu); !r2)
-        {
-            string str = r2.error();
-            return tl::unexpected("manager send message failed" + r2.error() + "\n");
-        }
-
-        if (const auto &r2 = manager.receiveMessage(buffer, type); !r2)
-        {
-            string str = r2.error();
-            return tl::unexpected("manager receive message failed" + r2.error() + "\n");
-        }
-
-        if (type != CTB::NON_MODULE)
-        {
-            return tl::unexpected("received message of wrong type");
-        }
-        const auto &zHeader = reinterpret_cast<CTBNonModule &>(buffer);
-
-        if (zHeader.logicalName != "Z.hpp" || zHeader.isHeaderUnit == true)
-        {
-            return tl::unexpected("wrong message received");
-        }
+        bigHu.logicalNames.emplace_back("Y.hpp");
+        bigHu.logicalNames.emplace_back("Z.hpp");
 
         if (const auto &r2 = manager.sendMessage(bigHu); !r2)
         {
@@ -950,6 +923,7 @@ tl::expected<int, string> runTest()
         printMessage(ctbModule, false);
 
         BTCModule btcMod;
+        /*
         btcMod.requested.filePath = fooPcm;
         ModuleDep modDep;
         modDep.file.filePath = bigPcm;
@@ -967,6 +941,7 @@ tl::expected<int, string> runTest()
         modDep.logicalNames = "A:C";
         btcMod.modDeps.emplace_back(std::move(modDep));
 
+        */
         if (const auto &r2 = manager.sendMessage(std::move(btcMod)); !r2)
         {
             string str = r2.error();

>From 25856dff378e1288042f7d57648b41fcf6944ec5 Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 19:11:00 +0500
Subject: [PATCH 06/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Lex/PPDirectives.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 14c284baee331..52f23c42d63e7 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2336,7 +2336,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
     auto &Responses = N2978::managerCompiler->responses;
     if (auto it = Responses.find(std::string(Filename));
         it == Responses.end() ||
-        it->second.type == N2978::ResponseType::MODULE) {
+        it->second.type == N2978::ResponseType::MODULE || (it->second.type == N2978::ResponseType::HEADER_FILE &&  IsImportDecl)) {
       N2978::CTBNonModule CTBNonMod;
       CTBNonMod.isHeaderUnit = IsImportDecl;
       CTBNonMod.logicalName = Filename.str();

>From 3b96442dbee92fff94db089cb9120a4bcf34dd6a Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 19:11:29 +0500
Subject: [PATCH 07/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Lex/PPDirectives.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 52f23c42d63e7..095303f459f3e 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2336,7 +2336,8 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
     auto &Responses = N2978::managerCompiler->responses;
     if (auto it = Responses.find(std::string(Filename));
         it == Responses.end() ||
-        it->second.type == N2978::ResponseType::MODULE || (it->second.type == N2978::ResponseType::HEADER_FILE &&  IsImportDecl)) {
+        it->second.type == N2978::ResponseType::MODULE ||
+        (it->second.type == N2978::ResponseType::HEADER_FILE && IsImportDecl)) {
       N2978::CTBNonModule CTBNonMod;
       CTBNonMod.isHeaderUnit = IsImportDecl;
       CTBNonMod.logicalName = Filename.str();

>From 26707d05933a410bac6357955e8121f321f30555 Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 20:30:24 +0500
Subject: [PATCH 08/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Serialization/ASTReader.cpp | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 32f7779278286..4a931c86044be 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -67,6 +67,7 @@
 #include "clang/Basic/TargetOptions.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Basic/Version.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/HeaderSearchOptions.h"
 #include "clang/Lex/MacroInfo.h"
@@ -3378,9 +3379,17 @@ ASTReader::ReadControlBlock(ModuleFile &F,
       // explicit name to file mappings. Also, we will still verify the
       // size/signature making sure it is essentially the same file but
       // perhaps in a different location.
-      if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule)
-        ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName(
-            ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule);
+      if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) {
+        if (N2978::managerCompiler) {
+          if (auto it = N2978::managerCompiler->responses.find(std::string(ImportedName)); it != N2978::managerCompiler->responses.end() && it->second.type == N2978::ResponseType::MODULE) {
+            ImportedFile = it->second.file.filePath;
+          }
+        }
+        else {
+          ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName(
+              ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule);
+        }
+      }
 
       if (IsImportingStdCXXModule && ImportedFile.empty()) {
         Diag(diag::err_failed_to_find_module_file) << ImportedName;

>From 641403e0e39326e457c31801e738e9b9e1d21f7e Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 20:30:52 +0500
Subject: [PATCH 09/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Serialization/ASTReader.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 4a931c86044be..5c3b86fab97f5 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -3379,13 +3379,16 @@ ASTReader::ReadControlBlock(ModuleFile &F,
       // explicit name to file mappings. Also, we will still verify the
       // size/signature making sure it is essentially the same file but
       // perhaps in a different location.
-      if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule) {
+      if (ImportedKind == MK_PrebuiltModule ||
+          ImportedKind == MK_ExplicitModule) {
         if (N2978::managerCompiler) {
-          if (auto it = N2978::managerCompiler->responses.find(std::string(ImportedName)); it != N2978::managerCompiler->responses.end() && it->second.type == N2978::ResponseType::MODULE) {
+          if (auto it = N2978::managerCompiler->responses.find(
+                  std::string(ImportedName));
+              it != N2978::managerCompiler->responses.end() &&
+              it->second.type == N2978::ResponseType::MODULE) {
             ImportedFile = it->second.file.filePath;
           }
-        }
-        else {
+        } else {
           ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName(
               ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule);
         }

>From 9051ebb38cd3d61dd21f5f38303ee3beb2883dc2 Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 13 Sep 2025 22:05:54 +0500
Subject: [PATCH 10/10] N2978 90% implemented. Only shared memory PCM files
 left.

---
 clang/lib/Frontend/CompilerInstance.cpp | 9 ++++++---
 clang/lib/Serialization/ASTReader.cpp   | 9 ++++++---
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 77e0de08dac24..dbf145b6e8e5b 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1905,10 +1905,13 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
     // in case of noScanIPC, PrebuiltModuleFiles is empty, so we initialize it
     // from the build-system here, so the selectModuleSource() call
     // later-on will return ModuleSource::MS_PrebuiltModulePath.
+    std::string f{ModuleName};
+    if (IsInclusionDirective)
+      f = f.substr(2, f.size() - 2);
+
     auto &Responses = N2978::managerCompiler->responses;
-    if (const auto it = Responses.find(std::string(ModuleName));
-        it == Responses.end() ||
-        it->second.type != N2978::ResponseType::MODULE) {
+    if (const auto it = Responses.find(f);
+        it == Responses.end() || it->second.type != (IsInclusionDirective ? N2978::ResponseType::HEADER_UNIT : N2978::ResponseType::MODULE)) {
       N2978::CTBModule Mod;
       Mod.moduleName = ModuleName;
       if (const auto &r =
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 5c3b86fab97f5..d03686775fae8 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -3382,10 +3382,13 @@ ASTReader::ReadControlBlock(ModuleFile &F,
       if (ImportedKind == MK_PrebuiltModule ||
           ImportedKind == MK_ExplicitModule) {
         if (N2978::managerCompiler) {
-          if (auto it = N2978::managerCompiler->responses.find(
-                  std::string(ImportedName));
+          std::string f{ImportedName};
+          if (f[0] == '.' && f[1] == '\\')
+            f = f.substr(2, f.size() - 2);
+
+          if (auto it = N2978::managerCompiler->responses.find(f);
               it != N2978::managerCompiler->responses.end() &&
-              it->second.type == N2978::ResponseType::MODULE) {
+              it->second.type != N2978::ResponseType::HEADER_FILE) {
             ImportedFile = it->second.file.filePath;
           }
         } else {



More information about the cfe-commits mailing list