[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
Wed Jul 9 02:59:18 PDT 2025
https://github.com/HassanSajjad-302 created https://github.com/llvm/llvm-project/pull/147682
This WIP pull request adds partial support for my draft [N2978](https://htmlpreview.github.io/?https://github.com/HassanSajjad-302/iso-papers/blob/main/generated/my-paper.html).
This paper allows for building C++20 modules and header-units
without the need to scan them first.
This allows for improved compilation support.
In my paper, I have referred to a sample implementation of the paper,
a library [ipc2978api](https://github.com/HassanSajjad-302/ipc2978api),
that compiler and build-systems can use to achieve
non-scanning builds.
ipc2978api has a complete cross-platform implementation of my paper
with error-handling.
The code coverage is 100%.
In it, I define a type `IPCManagerCompiler` that the compiler
can use to manage the interaction with the build-system.
It has seven public methods that are to be used
by the compiler.
```cpp
// IPCManagerCompiler class functions
[[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;
// A global function
[[nodiscard]] tl::expected<IPCManagerCompiler, string> makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath);
```
`tl::expected` is of this [library](https://github.com/TartanLlama/expected).
It is basically `std::expected` for C++17 which is used for LLVM.
While `CTB` and `BTC` mean compiler to build-system and
build-system to compiler respectively.
These are used to designate the message-type in the communication.
This is mentioned in my paper.
1) The First function is to get module from build-system.
2) The Second is to get non-module(header-unit/header-file).
3) The Third and Fourth send the last-message.
These include fields like exit-status, header-files, output string, logical-name
(If the module-compilation had any) and file output.
4) This is to be used if a PCM file is to be shared.
This will create a shared memory file.
So other processes don't have to read from the disk.
5) This is to be used to read the received PCM files.
6) The above function returns a `ProcessMappingOfBMIFile`
that can be passed to this function to close the mapping.
7) This is for closing the connection.
Calling Six and Seven isn't necessary since OS closes descriptors on process exit.
Calling `closeBMIFileMapping` will not close the mapping even if it is the only
compilation currently referencing it.
Since build-system will have it open anyway.
Build-system will close its mapping once all compilations that
might require the mapping have concluded.
`makeIPCManagerCompiler` function can be used to create `IPCManagerCompiler`.
PCM file-path is to be passed if header-unit is being compiled.
And object file-path is to be passed otherwise.
The build-system is required to pass the requisite options to the compiler.
Of these seven functions,
I have added support for the first one in this pull-request.
This does few modifications to the source-code.
Instead, it compiles the ipc2978api as part of
the Clang source code.
It has a `ClangTest.cpp` which is copied to the
LLVM repo as the unit test.
This WIP test will test the Clang support for my paper.
Currently, it only tests the first function.
It invokes the compilation of a `main.cpp` with `noScanIPC` flag.
This `main.cpp` file depends on `mod.cppm`, `mod1.cppm`, and
`mod2.cppm`.
These modules are already compiled and are passed to the compiler
by the test using `IPCManagerBS`.
After compilation completion, it tests for the existence of the `main.o` which marks the
successful completion of the test.
Test automatically executes in `build-dir/bin` directory
where the clang binary exists.
`clang/IPC2978/lib/setup.py` copies the test, source-files
and header-files from ipc2978api to the LLVM repo.
ipc2978api and LLVM repo should be in the same directory.
`setup.py` adjusts the includes in ipc2978api source-files to point to
ipc2978api headers in the LLVM repo.
This also uncomments a definition in `ClangTest.cpp` that allows it
to integrate with gtest.
I opened this half-baked pull-request to request assistance and feedback
to expedite the completion.
Once this is completed, I expect to very quickly add support for this
in my software
[HMake](https://github.com/HassanSajjad-302/HMake).
And then compile Boost with Big header-units without scanning.
Big header-units will be my software feature built on top of this.
It will allow amalgamating all header-files in a directory in a
single heder-unit.
I already compiled 25 Boost libraries with header-units with scanning with MSVC.
HMake will also support Clang's 2-phase compilation with this approach.
Compilation will share the Fat PCM which will be passed to other
compilations if free threads are available.
And this compilation will be signaled to produce the object-file.
So only one process is needed instead of three (scanning, Fat PCM, object-file).
HMake will also support
[this](https://isocpp.org/files/papers/P3057R0.html#hash-of-declarations-solution)
proposed by @ChuanqiXu9 and
[this](https://lists.isocpp.org/sg15/2023/11/2106.php) as-well.
While ipc2978api currently does not support 2-phase compilation
and declaration hashing,
I can add this first.
It is just fill in the blanks since the challenging work of
a connection establishment, memory-mapped files, error-handling, etc
is all complete.
And can prioritize this with C++20 modules support before C++20 header-units
support in HMake.
No other build-system can support C++20 modules the way HMake can.
HMake has a next-gen build algorithm that allows for dynamic nodes and edges.
Even with all this stuff, due to its next-gen architecture,
it would still be extremely fast and memory efficient.
It can exploit missed parallelization opportunities in the LLVM build(if any).
Due to these reasons, in the future, I would like to propose it for LLVM as well.
@vgvassilev @Bigcheese
>From 607326ba3e2dfbeea641d56277285bfac12caf0e Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Wed, 9 Jul 2025 14:50:33 +0500
Subject: [PATCH] Added partial support for N2978.
---
clang/include/clang/Driver/Options.td | 10 +
.../include/clang/Frontend/CompilerInstance.h | 5 +
.../clang/IPC2978/.clang-format-ignore | 1 +
clang/include/clang/IPC2978/IPCManagerBS.hpp | 39 +
.../clang/IPC2978/IPCManagerCompiler.hpp | 129 +
clang/include/clang/IPC2978/Manager.hpp | 135 +
clang/include/clang/IPC2978/Messages.hpp | 122 +
clang/include/clang/IPC2978/expected.hpp | 2444 +++++++++++++++++
clang/include/clang/IPC2978/rapidhash.h | 574 ++++
clang/include/clang/Lex/HeaderSearchOptions.h | 5 +
clang/lib/CMakeLists.txt | 1 +
clang/lib/Driver/ToolChains/Clang.cpp | 5 +
clang/lib/Frontend/CMakeLists.txt | 1 +
clang/lib/Frontend/CompilerInstance.cpp | 26 +
clang/lib/Frontend/CompilerInvocation.cpp | 5 +
clang/lib/IPC2978/.clang-format-ignore | 1 +
clang/lib/IPC2978/CMakeLists.txt | 6 +
clang/lib/IPC2978/IPCManagerBS.cpp | 352 +++
clang/lib/IPC2978/IPCManagerCompiler.cpp | 347 +++
clang/lib/IPC2978/Manager.cpp | 444 +++
clang/lib/IPC2978/setup.py | 58 +
clang/unittests/CMakeLists.txt | 7 +-
clang/unittests/IPC2978/.clang-format-ignore | 1 +
clang/unittests/IPC2978/CMakeLists.txt | 5 +
clang/unittests/IPC2978/IPC2978Test.cpp | 320 +++
25 files changed, 5042 insertions(+), 1 deletion(-)
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 77379f1130149..6a6010b4baa44 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9416,3 +9416,13 @@ 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">;
+def translate_include : Flag<["-"], "translateInclude">,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Consider header includes as header-units">;
+def find_include : Flag<["-"], "findIncludes">,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"In No Scan IPC approach, compiler will relay the finding includes responsibility to the build-system">;
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 0ae490f0e8073..84a524a549e02 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -16,6 +16,7 @@
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Frontend/Utils.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Lex/DependencyDirectivesScanner.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/ModuleLoader.h"
@@ -180,6 +181,10 @@ class CompilerInstance : public ModuleLoader {
/// The stream for verbose output.
raw_ostream *VerboseOutputStream = &llvm::errs();
+ /// Pointer for managing communication with build-system if noScan flag is
+ /// set.
+ N2978::IPCManagerCompiler *ipcManager = nullptr;
+
/// Holds information about the output file.
///
/// If TempFilename is not empty we must rename it to Filename at the end.
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..6aba39822d5ac
--- /dev/null
+++ b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
@@ -0,0 +1,129 @@
+
+#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:
+#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.angled = *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));
+ }
+}
+[[nodiscard]] tl::expected<IPCManagerCompiler, string> makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath);
+} // 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..c575e2b3fe09d
--- /dev/null
+++ b/clang/include/clang/IPC2978/Messages.hpp
@@ -0,0 +1,122 @@
+#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.
+// If findInclude flag is provided, then the compiler sends logicalName,
+// Otherwise the compiler sends the full path.
+struct CTBNonModule
+{
+ bool isHeaderUnit = false;
+ string str;
+};
+
+// This is the last message sent by the compiler.
+struct CTBLastMessage
+{
+ // Whether the compilation succeeded or failed.
+ bool exitStatus = false;
+ // Following fields are meaningless if the compilation failed.
+ // header-includes discovered during compilation.
+ vector<string> headerFiles;
+ // 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 file compiled is not a module interface unit
+ // or a header-unit.
+ 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;
+};
+
+// Reply for CTBModule
+struct BTCModule
+{
+ BMIFile requested;
+ vector<ModuleDep> deps;
+};
+
+struct HuDep
+{
+ BMIFile file;
+ string logicalName;
+ bool angled = false;
+};
+
+// Reply for CTBNonModule
+struct BTCNonModule
+{
+ bool isHeaderUnit = false;
+ string filePath;
+ // if isHeaderUnit == false, the following three are meaning-less.
+ bool angled = false;
+ // 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/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 0dbe46319247b..3ce724b14b085 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4118,6 +4118,11 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
}
}
+ if (Args.hasArg(options::OPT_no_scan_ipc)) {
+ CmdArgs.push_back("-noScanIPC");
+ Args.claimAllArgs(options::OPT_no_scan_ipc);
+ }
+
// Noop if we see '-fmodules-reduced-bmi' with other translation
// units than module units. This is more user friendly to allow end uers to
// enable this feature without asking for help from build systems.
diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt
index a916667208845..626bf06bd6efe 100644
--- a/clang/lib/Frontend/CMakeLists.txt
+++ b/clang/lib/Frontend/CMakeLists.txt
@@ -53,6 +53,7 @@ add_clang_library(clangFrontend
clangBasic
clangDriver
clangEdit
+ clangIPC2978
clangLex
clangParse
clangSema
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 09a66b652518f..1ffe9a5c75511 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1879,6 +1879,32 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
if (M)
checkConfigMacros(getPreprocessor(), M, ImportLoc);
+ if (getHeaderSearchOpts().NoScanIPC) {
+ if (!ipcManager) {
+ if (const auto &r = N2978::makeIPCManagerCompiler(OutputFiles.begin()->Filename); r) {
+ ipcManager = new N2978::IPCManagerCompiler(r.value());
+ }
+ }
+ N2978::CTBModule mod;
+ mod.moduleName = ModuleName;
+ if (const auto &r = ipcManager->receiveBTCModule(std::move(mod)); r) {
+ auto &[requested, deps] = r.value();
+
+ // in case of noScanIPC, PrebuiltModuleFiles is empty, so we receive 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);
+ PrebuiltModuleFiles.emplace(std::move(ModuleName) ,std::move(requested.filePath));
+ for (const auto &[file, logicalName] : deps) {
+ PrebuiltModuleFiles.emplace(std::move(logicalName) ,std::move(file.filePath));
+ }
+ }
+ else {
+ string errorMessage = r.error();
+ ModuleName = errorMessage;
+ }
+ }
+
// Select the source and filename for loading the named module.
std::string ModuleFilename;
ModuleSource Source =
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b9f75796ecc16..b14a792840a6a 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3389,6 +3389,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,
@@ -3521,6 +3524,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/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..2281cd7adca2f
--- /dev/null
+++ b/clang/lib/IPC2978/IPCManagerBS.cpp
@@ -0,0 +1,352 @@
+#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 &headerFilesExpected = readVectorOfStringFromPipe(buffer, bytesRead, bytesProcessed);
+ if (!headerFilesExpected)
+ {
+ return tl::unexpected(headerFilesExpected.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, headerFiles, output, errorOutput, logicalName, fileSize] =
+ getInitializedObjectFromBuffer<CTBLastMessage>(ctbBuffer);
+
+ exitStatus = *exitStatusExpected;
+ headerFiles = *headerFilesExpected;
+ output = *outputExpected;
+ errorOutput = *errorOutputExpected;
+ logicalName = *logicalNameExpected;
+ fileSize = *fileSizeExpected;
+ }
+ break;
+
+ default:
+
+ return tl::unexpected(getErrorString(ErrorCategory::UNKNOWN_CTB_TYPE));
+ }
+
+ 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.angled);
+ 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..44bdf66e944bd
--- /dev/null
+++ b/clang/lib/IPC2978/IPCManagerCompiler.cpp
@@ -0,0 +1,347 @@
+
+#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.str);
+ 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.exitStatus);
+ writeVectorOfStrings(buffer, lastMessage.headerFiles);
+ 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.exitStatus == 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.exitStatus == 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
+}
+
+} // 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..412909f323ecf
--- /dev/null
+++ b/clang/lib/IPC2978/Manager.cpp
@@ -0,0 +1,444 @@
+
+#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() = %d", 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;
+ }
+
+ 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);
+}
+
+void Manager::writeHuDep(vector<char> &buffer, const HuDep &dep)
+{
+ writeProcessMappingOfBMIFile(buffer, dep.file);
+ writeString(buffer, dep.logicalName);
+ buffer.emplace_back(dep.angled);
+}
+
+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());
+ }
+
+ ModuleDep modDep;
+
+ modDep.file.filePath = *r;
+ modDep.file.fileSize = *r2;
+ modDep.logicalName = *r3;
+
+ 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.angled = *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/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index 54c781a35c20c..631f881d745e1 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -92,12 +92,13 @@ add_subdirectory(Frontend)
add_subdirectory(Rewrite)
add_subdirectory(Sema)
add_subdirectory(CodeGen)
+add_subdirectory(IPC2978)
if(HAVE_CLANG_REPL_SUPPORT)
add_subdirectory(Interpreter)
endif()
# FIXME: libclang unit tests are disabled on Windows due
# to failures, mostly in libclang.VirtualFileOverlay_*.
-if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD)
+if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD)
add_subdirectory(libclang)
endif()
add_subdirectory(DirectoryWatcher)
@@ -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..0c0399121bf3f
--- /dev/null
+++ b/clang/unittests/IPC2978/IPC2978Test.cpp
@@ -0,0 +1,320 @@
+//===- 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 = {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
+
+[[nodiscard]] tl::expected<void, string> setupTest()
+{
+
+ const string mod = R"(export module mod;
+import mod2;
+
+export void func()
+{
+ EmptyClass2 a;
+ func2();
+ // does nothing
+}
+
+export struct EmptyClass
+{
+};)";
+
+ const string mod1 = R"(module;
+
+export module mod1;
+import mod2;)";
+
+ const string mod2 = R"(export module mod2;
+
+export void func2()
+{
+ // does nothing
+}
+
+export struct EmptyClass2
+{
+};)";
+
+ const string main = R"(import mod;
+import mod1;
+
+int main()
+{
+ func();
+ EmptyClass a;
+})";
+
+ ofstream("mod.cppm") << mod;
+ ofstream("mod1.cppm") << mod1;
+ ofstream("mod2.cppm") << mod2;
+ ofstream("main.cpp") << main;
+
+ // compile main.cpp which imports B.cpp which imports A.cpp.
+
+ if (system(CLANG_CMD
+ R"( -std=c++20 mod2.cppm -c -fmodule-output="mod2 .pcm" -fmodules-reduced-bmi -o "mod2 .o")") !=
+ EXIT_SUCCESS)
+ {
+ return tl::unexpected("could not run the first command\n");
+ }
+
+ if (system(
+ CLANG_CMD
+ R"( -std=c++20 mod.cppm -c -fmodule-output="mod .pcm" -fmodules-reduced-bmi -o "mod .o" -fmodule-file=mod2="./mod2 .pcm")") !=
+ EXIT_SUCCESS)
+ {
+ return tl::unexpected("could not run the second command\n");
+ }
+
+ if (system(
+ CLANG_CMD
+ R"( -std=c++20 mod1.cppm -c -fmodule-output="mod1 .pcm" -fmodules-reduced-bmi -o "mod1 .o" -fmodule-file=mod2="./mod2 .pcm")") !=
+ EXIT_SUCCESS)
+ {
+ tl::unexpected("could not run the second command\n");
+ }
+ return {};
+}
+
+tl::expected<int, string> runTest()
+{
+
+ if (const auto &r = setupTest(); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+
+ 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";
+
+ if (const auto &r = makeIPCManagerBS(std::move(mainFilePath)); r)
+ {
+ uint8_t messageCount = 0;
+ const IPCManagerBS &manager = *r;
+
+ string objFile = (current_path() / "main .o").generic_string() + "\"";
+ string compileCommand = CLANG_CMD R"( -std=c++20 -c main.cpp -noScanIPC -o ")" + objFile;
+ if (const auto &r2 = Run(compileCommand); !r2)
+ {
+ return tl::unexpected(r2.error());
+ }
+
+ while (true)
+ {
+ 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");
+ }
+
+ switch (type)
+ {
+
+ case CTB::MODULE: {
+ const auto &ctbModule = reinterpret_cast<CTBModule &>(buffer);
+ printMessage(ctbModule, false);
+
+ if (!messageCount)
+ {
+ ++messageCount;
+
+ BMIFile mod;
+ mod.filePath = modFilePath;
+ mod.fileSize = 0;
+
+ BMIFile mod2;
+ mod2.filePath = mod2FilePath;
+ mod2.fileSize = 0;
+ ModuleDep mod2Dep;
+ mod2Dep.file = std::move(mod2);
+ mod2Dep.logicalName = "mod2";
+
+ BTCModule b;
+ b.requested = std::move(mod);
+ b.deps.emplace_back(std::move(mod2Dep));
+
+ if (const auto &r2 = manager.sendMessage(b); !r2)
+ {
+ return tl::unexpected(r2.error());
+ }
+ printMessage(b, true);
+ }
+ else if (messageCount == 1)
+ {
+ ++messageCount;
+ BMIFile mod1;
+ mod1.filePath = mod1FilePath;
+ mod1.fileSize = 0;
+
+ BTCModule b;
+ b.requested = std::move(mod1);
+
+ if (const auto &r2 = manager.sendMessage(b); !r2)
+ {
+ return tl::unexpected(r2.error());
+ }
+ printMessage(b, true);
+ }
+ }
+
+ break;
+
+ case CTB::NON_MODULE: {
+ }
+
+ case CTB::LAST_MESSAGE: {
+ }
+
+ return tl::unexpected("Unexpected message received");
+ }
+
+ if (messageCount == 2)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ return tl::unexpected("creating manager failed" + r.error() + "\n");
+ }
+
+#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 {};
+}
+} // 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
More information about the cfe-commits
mailing list