[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