[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
Fri Mar 27 17:04:13 PDT 2026
https://github.com/HassanSajjad-302 updated https://github.com/llvm/llvm-project/pull/147682
>From 0a7b5a26ee02c3e15a6220d6251fb6ed138df70b Mon Sep 17 00:00:00 2001
From: Hassan Sajjad <hassan.sajjad069 at gmail.com>
Date: Sat, 28 Mar 2026 04:55:01 +0500
Subject: [PATCH] P2978 implemented.
---
.../include/clang/Frontend/CompilerInstance.h | 3 +
.../clang/IPC2978/.clang-format-ignore | 1 +
clang/include/clang/IPC2978/IPCManagerBS.hpp | 28 +
.../clang/IPC2978/IPCManagerCompiler.hpp | 90 +
clang/include/clang/IPC2978/Manager.hpp | 115 +
clang/include/clang/IPC2978/Messages.hpp | 136 +
clang/include/clang/IPC2978/expected.hpp | 2444 +++++++++++++++++
clang/include/clang/IPC2978/rapidhash.h | 574 ++++
clang/include/clang/Lex/HeaderSearchOptions.h | 5 +
clang/include/clang/Lex/ModuleLoader.h | 10 +
clang/include/clang/Options/Options.td | 4 +
clang/lib/CMakeLists.txt | 1 +
clang/lib/Driver/ToolChains/Clang.cpp | 5 +
clang/lib/Frontend/CompilerInstance.cpp | 46 +-
clang/lib/Frontend/CompilerInvocation.cpp | 5 +
clang/lib/IPC2978/.clang-format-ignore | 1 +
clang/lib/IPC2978/CMakeLists.txt | 6 +
clang/lib/IPC2978/IPCManagerBS.cpp | 267 ++
clang/lib/IPC2978/IPCManagerCompiler.cpp | 589 ++++
clang/lib/IPC2978/Manager.cpp | 262 ++
clang/lib/IPC2978/setup.py | 70 +
clang/lib/Lex/CMakeLists.txt | 1 +
clang/lib/Lex/PPDirectives.cpp | 50 +-
clang/lib/Parse/ParseAST.cpp | 3 +
clang/lib/Serialization/ASTReader.cpp | 24 +-
clang/lib/Serialization/ModuleManager.cpp | 31 +-
clang/tools/driver/cc1_main.cpp | 25 +-
clang/unittests/CMakeLists.txt | 5 +
clang/unittests/IPC2978/.clang-format-ignore | 1 +
clang/unittests/IPC2978/CMakeLists.txt | 5 +
clang/unittests/IPC2978/IPC2978Test.cpp | 1086 ++++++++
31 files changed, 5856 insertions(+), 37 deletions(-)
create mode 100644 clang/include/clang/IPC2978/.clang-format-ignore
create mode 100644 clang/include/clang/IPC2978/IPCManagerBS.hpp
create mode 100644 clang/include/clang/IPC2978/IPCManagerCompiler.hpp
create mode 100644 clang/include/clang/IPC2978/Manager.hpp
create mode 100644 clang/include/clang/IPC2978/Messages.hpp
create mode 100644 clang/include/clang/IPC2978/expected.hpp
create mode 100644 clang/include/clang/IPC2978/rapidhash.h
create mode 100644 clang/lib/IPC2978/.clang-format-ignore
create mode 100644 clang/lib/IPC2978/CMakeLists.txt
create mode 100644 clang/lib/IPC2978/IPCManagerBS.cpp
create mode 100644 clang/lib/IPC2978/IPCManagerCompiler.cpp
create mode 100644 clang/lib/IPC2978/Manager.cpp
create mode 100644 clang/lib/IPC2978/setup.py
create mode 100644 clang/unittests/IPC2978/.clang-format-ignore
create mode 100644 clang/unittests/IPC2978/CMakeLists.txt
create mode 100644 clang/unittests/IPC2978/IPC2978Test.cpp
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 0d684d5c7f9fe..e0ba35ed92e88 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -969,6 +969,9 @@ class CompilerInstance : public ModuleLoader {
GenModuleActionWrapperFunc getGenModuleActionWrapper() const {
return GenModuleActionWrapper;
}
+ void makeModuleAndDependenciesVisible(Module *Mod);
+ Module *loadIPCReceivedHeaderUnit(StringRef FileName,
+ SourceLocation ImportLoc) override;
void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) {
DependencyCollectors.push_back(std::move(Listener));
diff --git a/clang/include/clang/IPC2978/.clang-format-ignore b/clang/include/clang/IPC2978/.clang-format-ignore
new file mode 100644
index 0000000000000..f59ec20aabf58
--- /dev/null
+++ b/clang/include/clang/IPC2978/.clang-format-ignore
@@ -0,0 +1 @@
+*
\ No newline at end of file
diff --git a/clang/include/clang/IPC2978/IPCManagerBS.hpp b/clang/include/clang/IPC2978/IPCManagerBS.hpp
new file mode 100644
index 0000000000000..b9e10d20e9d7d
--- /dev/null
+++ b/clang/include/clang/IPC2978/IPCManagerBS.hpp
@@ -0,0 +1,28 @@
+
+#ifndef IPC_MANAGER_BS_HPP
+#define IPC_MANAGER_BS_HPP
+
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/Messages.hpp"
+
+namespace P2978
+{
+
+// IPC Manager BuildSystem
+class IPCManagerBS : public Manager
+{
+ public:
+ uint64_t writeFd = 0;
+
+ tl::expected<void, std::string> writeInternal(std::string_view buffer) const override;
+
+ explicit IPCManagerBS(uint64_t writeFd_);
+ static tl::expected<void, std::string> receiveMessage(char (&ctbBuffer)[320], CTB &messageType, std::string_view serverReadString) ;
+ [[nodiscard]] tl::expected<void, std::string> sendMessage(const BTCModule &moduleFile) const;
+ [[nodiscard]] tl::expected<void, std::string> sendMessage(const BTCNonModule &nonModule) const;
+ [[nodiscard]] tl::expected<void, std::string> sendMessage(const BTCLastMessage &lastMessage) const;
+ static tl::expected<Mapping, std::string> createSharedMemoryBMIFile(BMIFile &bmiFile);
+ static tl::expected<void, std::string> closeBMIFileMapping(const Mapping &processMappingOfBMIFile);
+};
+} // namespace P2978
+#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..a4b3fda78effc
--- /dev/null
+++ b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp
@@ -0,0 +1,90 @@
+
+#ifndef IPC_MANAGER_COMPILER_HPP
+#define IPC_MANAGER_COMPILER_HPP
+
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/expected.hpp"
+
+struct CompilerTest;
+struct BuildSystemTest;
+namespace P2978
+{
+
+inline std::vector<std::string *> allocations;
+
+enum class FileType : uint8_t
+{
+ MODULE,
+ HEADER_UNIT,
+ HEADER_FILE
+};
+
+struct Response
+{
+ std::string_view filePath;
+ // if type == HEADER_FILE, then fileSize has no meaning
+ Mapping mapping;
+ FileType type;
+ bool isSystem;
+ Response(std::string_view filePath_, const Mapping &mapping_, FileType type_, bool isSystem_);
+};
+
+// IPC Manager Compiler
+class IPCManagerCompiler : Manager
+{
+ friend struct ::CompilerTest;
+ friend struct ::BuildSystemTest;
+
+ tl::expected<std::string_view, std::string> readInternal(char (&buffer)[4096]) const;
+ tl::expected<void, std::string> writeInternal(std::string_view buffer) const override;
+
+ struct BMIFileMapping
+ {
+ BMIFile file;
+ Mapping mapping;
+ };
+
+ tl::expected<BMIFileMapping, std::string> readProcessMappingOfBMIFile(std::string_view message,
+ uint32_t &bytesRead);
+ tl::expected<void, std::string> readLogicalNames(std::string_view message, uint32_t &bytesRead,
+ const BMIFileMapping &mapping, FileType type, bool isSystem);
+
+ // Called by sendCTBLastMessage. Build-system will send this after it has created the BMI file-mapping.
+ [[nodiscard]] tl::expected<void, std::string> receiveBTCLastMessage() const;
+ // This function is called by findResponse if it did not find the module in the IPCManagerCompiler::responses cache.
+ [[nodiscard]] tl::expected<void, std::string> receiveBTCModule(const CTBModule &moduleName);
+ // This function is called by findResponse if it did not find the header-unit or header-file in the
+ // IPCManagerCompiler::responses cache.
+ [[nodiscard]] tl::expected<void, std::string> receiveBTCNonModule(const CTBNonModule &nonModule);
+
+ // Internal cache for the possible future requests.
+ std::unordered_map<std::string_view, Response> responses;
+
+ // Compiler can use this function to read the BMI file. BMI should be read using this function to conserve memory.
+ static tl::expected<Mapping, std::string> readSharedMemoryBMIFile(const BMIFile &file);
+
+ [[nodiscard]] tl::expected<void, std::string> sendCTBLastMessage(uint32_t fileSize) const;
+
+ public:
+ // Compiler process can use this function to close the BMI file-mapping to reduce references to shared memory file.
+ // Not needed as it will be cleared at process exit.
+ static tl::expected<void, std::string> closeBMIFileMapping(const Mapping &processMappingOfBMIFile);
+
+ // Cache mapping between the file-path and bmi-file-mapping. Only to be queried by the compiler. Passed path must be
+ // lexically normal and lower-case on Windows.
+ std::unordered_map<std::string, Mapping> filePathProcessMapping;
+
+ // TODO
+ // For FileType:HEADER_FILE, it could also return FileType::MODULE, but Clang currently does not support it.
+ // For FileType::HEADER_FILE, it can return FileType::HEADER_UNIT, otherwise it will return the request
+ // response. Either it will return from the cache or it will fetch it from the build-system
+ [[nodiscard]] tl::expected<Response, std::string> findResponse(std::string_view logicalName, FileType type);
+
+ // This function should be called only if the compilation succeeded
+ [[nodiscard]] tl::expected<void, std::string> sendCTBLastMessage(const std::string &bmiFile,
+ const std::string &filePath) const;
+};
+
+inline IPCManagerCompiler *managerCompiler;
+} // namespace P2978
+#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..474316d985420
--- /dev/null
+++ b/clang/include/clang/IPC2978/Manager.hpp
@@ -0,0 +1,115 @@
+
+#ifndef MANAGER_HPP
+#define MANAGER_HPP
+
+#include "clang/IPC2978/Messages.hpp"
+#include "clang/IPC2978/expected.hpp"
+
+#include <string>
+#include <vector>
+
+namespace tl
+{
+template <typename T, typename U> class expected;
+}
+
+namespace P2978
+{
+
+// 32-byte delimiter
+inline const char *delimiter = "DELIMITER"
+ "\x5A\xA5\x5A\xA5\x5A\xA5\x5A\xA5\x5A\xA5\x5A\xA5\x5A\xA5"
+ "DELIMITER";
+
+enum class ErrorCategory : uint8_t
+{
+ NONE,
+
+ PARSING_ERROR,
+ // error-category for API errors
+ READ_FILE_ZERO_BYTES_READ,
+ INCORRECT_BTC_LAST_MESSAGE,
+ UNKNOWN_CTB_TYPE,
+};
+
+std::string getErrorString();
+std::string getErrorString(uint32_t bytesRead_, uint32_t bytesProcessed_);
+std::string getErrorString(ErrorCategory errorCategory_);
+// to facilitate error propagation.
+inline std::string getErrorString(std::string err)
+{
+ return err;
+}
+
+struct Mapping
+{
+ std::string_view file;
+#ifdef _WIN32
+ void *mapping;
+ void *view;
+#endif
+};
+
+class Manager
+{
+ public:
+ virtual tl::expected<void, std::string> writeInternal(std::string_view buffer) const = 0;
+ virtual ~Manager() = default;
+#ifndef _WIN32
+ static tl::expected<void, std::string> writeAll(const int fd, const char *buffer, const uint32_t count);
+#endif
+
+ static std::string getBufferWithType(CTB type);
+ static void writeUInt32(std::string &buffer, uint32_t value);
+ static void writeString(std::string &buffer, const std::string_view &str);
+ // path is used in system calls. so it is followed by null character while the normal string is not.
+ static void writePath(std::string &buffer, const std::string_view &str);
+ static void writeBMIFile(std::string &buffer, const BMIFile &file);
+ static void writeModuleDep(std::string &buffer, const ModuleDep &dep);
+ static void writeHuDep(std::string &buffer, const HuDep &dep);
+ static void writeHeaderFile(std::string &buffer, const HeaderFile &dep);
+ static void writeVectorOfStrings(std::string &buffer, const std::vector<std::string_view> &strs);
+ static void writeVectorOfProcessMappingOfBMIFiles(std::string &buffer, const std::vector<BMIFile> &files);
+ static void writeVectorOfModuleDep(std::string &buffer, const std::vector<ModuleDep> &deps);
+ static void writeVectorOfHuDeps(std::string &buffer, const std::vector<HuDep> &deps);
+ static void writeVectorOfHeaderFiles(std::string &buffer, const std::vector<HeaderFile> &headerFiles);
+
+ static tl::expected<bool, std::string> readBool(std::string_view message, uint32_t &bytesRead);
+ static tl::expected<uint32_t, std::string> readUInt32(std::string_view message, uint32_t &bytesRead);
+ static tl::expected<std::string_view, std::string> readString(std::string_view message, uint32_t &bytesRead);
+
+ // path is used in system calls. so it is followed by null character while the normal string is not.
+ static tl::expected<std::string_view, std::string> readPath(std::string_view message, uint32_t &bytesRead);
+};
+
+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 P2978
+#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..8cf94579fa95a
--- /dev/null
+++ b/clang/include/clang/IPC2978/Messages.hpp
@@ -0,0 +1,136 @@
+#ifndef MESSAGES_HPP
+#define MESSAGES_HPP
+
+#include <cstdint>
+#include <signal.h>
+#include <string_view>
+#include <vector>
+
+namespace P2978
+{
+// CTB --> Compiler to Build-System
+// BTC --> Build-System to Compiler
+
+// string_view is 4 bytes that hold the size of the char array, followed by the array.
+// string_view representing the filePath is followed by null terminator.
+// 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
+{
+ std::string_view moduleName;
+};
+
+// This is sent when the compiler needs something else than a module.
+// isHeaderUnit is set when the compiler knows that it is a header-unit.
+struct CTBNonModule
+{
+ bool isHeaderUnit = false;
+ std::string_view logicalName;
+};
+
+// This is the last message sent by the compiler if the compiler
+// has any exported BMI.
+struct CTBLastMessage
+{
+ // This is communicated because the receiving process has no
+ // way to learn the shared memory file size on both Windows
+ // and Linux without a filesystem call.
+ // Meaningless if the compilation does not produce BMI.
+ uint32_t fileSize = UINT32_MAX;
+};
+
+// Build System to Compiler
+// Unlike CTB, this is not written as the first byte
+// since the compiler knows what message it will receive.
+enum class BTC : uint8_t
+{
+ MODULE = 0,
+ NON_MODULE = 1,
+ LAST_MESSAGE = 2,
+};
+
+struct BMIFile
+{
+ std::string_view filePath;
+ uint32_t fileSize = UINT32_MAX;
+};
+
+struct ModuleDep
+{
+ bool isHeaderUnit;
+ BMIFile file;
+ // whether header-unit / module belongs to system (ignore warnings).
+ bool isSystem = true;
+ // if isHeaderUnit == true, then the following might
+ // contain more than one values, as header-unit can be
+ // composed of multiple header-files. And if later,
+ // any of the following logicalNames is included or
+ // imported, this header-unit can be used instead.
+ std::vector<std::string_view> logicalNames;
+};
+
+// Reply for CTBModule
+struct BTCModule
+{
+ BMIFile requested;
+ bool isSystem = true;
+ std::vector<ModuleDep> modDeps;
+};
+
+struct HuDep
+{
+ BMIFile file;
+ // whether header-unit / header-file belongs to system (ignore warnings).
+ bool isSystem = true;
+ // A header-unit can be composed of
+ // multiple header-files. And if later,
+ // any of the following logicalNames is included or
+ // imported, this header-unit can be used instead.
+ std::vector<std::string_view> logicalNames;
+};
+
+struct HeaderFile
+{
+ std::string_view logicalName;
+ std::string_view filePath;
+ bool isSystem = true;
+};
+
+// Reply for CTBNonModule
+struct BTCNonModule
+{
+ bool isHeaderUnit = false;
+ bool isSystem = true;
+ // build-system might send the following on first request, if it knows that a
+ // header-unit is being compiled that compose multiple header-files to reduce
+ // the number of subsequent requests.
+ std::vector<HeaderFile> headerFiles;
+ std::string_view filePath;
+ // if isHeaderUnit == false, the following are meaning-less and are not sent.
+ // if isHeaderUnit == true, fileSize of the requested file.
+ uint32_t fileSize;
+ // A header-unit can be composed of
+ // multiple header-files. And if later,
+ // any of the following logicalNames is included or
+ // imported, this header-unit can be used instead.
+ std::vector<std::string_view> logicalNames;
+ std::vector<HuDep> huDeps;
+};
+
+// Reply for CTBLastMessage if the compilation succeeded.
+struct BTCLastMessage
+{
+};
+} // namespace P2978
+#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..abee315ce9242
--- /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 fd46ea223bae1..8df023d2c4e13 100644
--- a/clang/include/clang/Lex/HeaderSearchOptions.h
+++ b/clang/include/clang/Lex/HeaderSearchOptions.h
@@ -277,6 +277,11 @@ class HeaderSearchOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned ModulesIncludeVFSUsage : 1;
+ /// Whether to coordinate with build-system for building of C++20 Modules \
+ /// and header-units.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned NoScanIPC : 1;
+
/// Whether we should look for a module in module maps only in provided
/// header search paths or if we are allowed to look for module maps in
/// subdirectories of provided paths too.
diff --git a/clang/include/clang/Lex/ModuleLoader.h b/clang/include/clang/Lex/ModuleLoader.h
index 042a5ab1f4a57..175ca6ea665b6 100644
--- a/clang/include/clang/Lex/ModuleLoader.h
+++ b/clang/include/clang/Lex/ModuleLoader.h
@@ -161,6 +161,11 @@ class ModuleLoader {
SourceLocation TriggerLoc) = 0;
static std::string getFlatNameFromPath(ModuleIdPath Path);
+ /// Load C++20 Header-Unit received from IPC it the
+ /// Header-Unit is not already loaded.
+ virtual Module *loadIPCReceivedHeaderUnit(StringRef FileName,
+ SourceLocation ImportLoc) = 0;
+
bool HadFatalFailure = false;
};
@@ -187,6 +192,11 @@ class TrivialModuleLoader : public ModuleLoader {
SourceLocation TriggerLoc) override {
return false;
}
+
+ Module *loadIPCReceivedHeaderUnit(StringRef FileName,
+ SourceLocation ImportLoc) override {
+ return nullptr;
+ }
};
} // namespace clang
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index a752f44374415..b84d782d02152 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -10081,3 +10081,7 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
Group<m_Group>,
HelpText<"Enable the wasm-opt optimizer (default)">,
MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+
+def no_scan_ipc : Flag<["-", "/"], "noScanIPC">,
+ Visibility<[ClangOption, CC1Option, CLOption]>,
+ HelpText<"Enable No scan IPC approach">;
\ No newline at end of file
diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt
index d9d1e4630a416..9271336717466 100644
--- a/clang/lib/CMakeLists.txt
+++ b/clang/lib/CMakeLists.txt
@@ -24,6 +24,7 @@ add_subdirectory(Index)
add_subdirectory(IndexSerialization)
add_subdirectory(InstallAPI)
add_subdirectory(ScalableStaticAnalysisFramework)
+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 db82695f87d6b..8ade428eb8edb 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4092,6 +4092,11 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
Args.ClaimAllArgs(options::OPT_fmodules_reduced_bmi);
Args.ClaimAllArgs(options::OPT_fno_modules_reduced_bmi);
+ if (Args.hasArg(options::OPT_no_scan_ipc)) {
+ CmdArgs.push_back("-noScanIPC");
+ Args.claimAllArgs(options::OPT_no_scan_ipc);
+ }
+
// We need to include the case the input file is a module file here.
// Since the default compilation model for C++ module interface unit will
// create temporary module file and compile the temporary module file
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 33919bc1c4634..41f6b11cbcea1 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -31,6 +31,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/Frontend/VerifyDiagnosticConsumer.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
@@ -1824,9 +1825,22 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST(
// Select the source and filename for loading the named module.
ModuleFileName ModuleFilename;
- ModuleSource Source =
- selectModuleSource(M, ModuleName, ModuleFilename, BuiltModules, HS);
SourceLocation ModuleNameLoc = ModuleNameRange.getBegin();
+ ModuleSource Source;
+
+ if (P2978::managerCompiler) {
+ const auto &Result = P2978::managerCompiler->findResponse(
+ std::string(ModuleName), P2978::FileType::MODULE);
+ if (!Result) {
+ // error happened in receiving message
+ }
+ ModuleFilename = ModuleFileName::makeExplicit(Result->filePath);
+ Source = MS_PrebuiltModulePath;
+ } else {
+ Source =
+ selectModuleSource(M, ModuleName, ModuleFilename, BuiltModules, HS);
+ }
+
if (Source == MS_ModuleNotFound) {
// We can't find a module, error out here.
getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found)
@@ -2348,6 +2362,34 @@ CompilerInstance::lookupMissingImports(StringRef Name,
return false;
}
+
+static SourceLocation EndLoc;
+void CompilerInstance::makeModuleAndDependenciesVisible(Module *Mod) {
+ getASTReader()->makeModuleVisible(Mod, Module::AllVisible, EndLoc);
+ getPreprocessor().makeModuleVisible(Mod, EndLoc);
+ getSema().makeModuleVisible(Mod, EndLoc);
+ for (auto *Import : Mod->Imports) {
+ makeModuleAndDependenciesVisible(Import);
+ }
+}
+
+Module *
+CompilerInstance::loadIPCReceivedHeaderUnit(const StringRef FileName,
+ const SourceLocation ImportLoc) {
+ serialization::ModuleFile *ModuleFile =
+ getASTReader()->getModuleManager().lookupByFileName(ModuleFileName::makeExplicit(FileName));
+ if (!ModuleFile)
+ loadModuleFile(ModuleFileName::makeExplicit(FileName), ModuleFile);
+
+ ModuleFile->Kind = serialization::MK_PrebuiltModule;
+
+ Module *Mod = PP->getHeaderSearchInfo().getModuleMap().findModule(
+ ModuleFile->ModuleName);
+ EndLoc = ImportLoc;
+ makeModuleAndDependenciesVisible(Mod);
+ return Mod;
+}
+
void CompilerInstance::resetAndLeakSema() { llvm::BuryPointer(takeSema()); }
void CompilerInstance::setExternalSemaSource(
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 748c36efefaed..7a1e45cd5a205 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3404,6 +3404,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,
@@ -3524,6 +3527,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..39aae7fb30f2c
--- /dev/null
+++ b/clang/lib/IPC2978/IPCManagerBS.cpp
@@ -0,0 +1,267 @@
+#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 "clang/IPC2978/rapidhash.h"
+#include <Windows.h>
+#else
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#endif
+
+#define TRY_READ(var, func, ...) \
+ const auto &var = func(__VA_ARGS__); \
+ if (!var) \
+ { \
+ return tl::unexpected(var.error()); \
+ }
+
+#define TRY_READ_VAL(var, func, ...) \
+ const auto &var##_result = func(__VA_ARGS__); \
+ if (!var##_result) \
+ { \
+ return tl::unexpected(var##_result.error()); \
+ } \
+ auto &var = *var##_result;
+
+namespace P2978
+{
+
+tl::expected<void, std::string> IPCManagerBS::writeInternal(const std::string_view buffer) const
+{
+#ifdef _WIN32
+ const bool success = WriteFile(reinterpret_cast<HANDLE>(writeFd), // 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(writeFd, buffer.data(), buffer.size()); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+#endif
+ return {};
+}
+
+IPCManagerBS::IPCManagerBS(const uint64_t writeFd_) : writeFd(writeFd_)
+{
+}
+
+tl::expected<void, std::string> IPCManagerBS::receiveMessage(char (&ctbBuffer)[320], CTB &messageType,
+ const std::string_view serverReadString)
+{
+ if (serverReadString.empty())
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+
+ uint32_t bytesRead = 1;
+
+ // read call fails if zero byte is read, so safe to process 1 byte
+ switch (static_cast<CTB>(serverReadString[0]))
+ {
+
+ case CTB::MODULE: {
+ TRY_READ_VAL(r, readString, serverReadString, bytesRead);
+
+ messageType = CTB::MODULE;
+ getInitializedObjectFromBuffer<CTBModule>(ctbBuffer).moduleName = r;
+ }
+ break;
+
+ case CTB::NON_MODULE: {
+ TRY_READ_VAL(r, readBool, serverReadString, bytesRead);
+ TRY_READ_VAL(r2, readString, serverReadString, bytesRead);
+ messageType = CTB::NON_MODULE;
+ auto &[isHeaderUnit, str] = getInitializedObjectFromBuffer<CTBNonModule>(ctbBuffer);
+ isHeaderUnit = r;
+ str = r2;
+ }
+ break;
+
+ case CTB::LAST_MESSAGE: {
+ TRY_READ_VAL(fileSizeExpected, readUInt32, serverReadString, bytesRead);
+
+ messageType = CTB::LAST_MESSAGE;
+ auto &[fileSize] = getInitializedObjectFromBuffer<CTBLastMessage>(ctbBuffer);
+ fileSize = fileSizeExpected;
+ }
+ break;
+
+ default:
+ return tl::unexpected(getErrorString(ErrorCategory::UNKNOWN_CTB_TYPE));
+ }
+
+ if (serverReadString.size() != bytesRead)
+ {
+ return tl::unexpected(getErrorString(serverReadString.size(), bytesRead));
+ }
+
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerBS::sendMessage(const BTCModule &moduleFile) const
+{
+ std::string buffer;
+ writeBMIFile(buffer, moduleFile.requested);
+ buffer.push_back(moduleFile.isSystem);
+ writeVectorOfModuleDep(buffer, moduleFile.modDeps);
+ buffer.append(delimiter, strlen(delimiter));
+ if (const auto &r = writeInternal(buffer); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerBS::sendMessage(const BTCNonModule &nonModule) const
+{
+ std::string buffer;
+ buffer.push_back(nonModule.isHeaderUnit);
+ buffer.push_back(nonModule.isSystem);
+ writeVectorOfHeaderFiles(buffer, nonModule.headerFiles);
+ writePath(buffer, nonModule.filePath);
+ if (nonModule.isHeaderUnit)
+ {
+ writeUInt32(buffer, nonModule.fileSize);
+ writeVectorOfStrings(buffer, nonModule.logicalNames);
+ writeVectorOfHuDeps(buffer, nonModule.huDeps);
+ }
+ buffer.append(delimiter, strlen(delimiter));
+ if (const auto &r = writeInternal(buffer); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerBS::sendMessage(const BTCLastMessage &) const
+{
+ std::string buffer;
+ buffer.push_back(true);
+ buffer.append(delimiter, strlen(delimiter));
+ if (const auto &r = writeInternal(buffer); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ return {};
+}
+
+tl::expected<Mapping, std::string> IPCManagerBS::createSharedMemoryBMIFile(BMIFile &bmiFile)
+{
+ Mapping sharedFile{};
+#ifdef _WIN32
+
+ // mappingName is needed as the Windows kernel object names can't have \\ in them.
+ const uint64_t hash = rapidhash(bmiFile.filePath.data(), bmiFile.filePath.size());
+ char mappingName[17];
+ static constexpr char hex[] = "0123456789abcdef";
+ for (int i = 0; i < 8; i++)
+ {
+ const uint8_t byte = hash >> (56 - i * 8) & 0xFF;
+ mappingName[i * 2] = hex[byte >> 4];
+ mappingName[i * 2 + 1] = hex[byte & 0xF];
+ }
+ mappingName[16] = '\0';
+
+ if (bmiFile.fileSize == UINT32_MAX)
+ {
+ const HANDLE hFile = CreateFileA(bmiFile.filePath.data(), GENERIC_READ,
+ 0, // no sharing during setup
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ LARGE_INTEGER fileSize;
+ if (!GetFileSizeEx(hFile, &fileSize))
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ sharedFile.mapping =
+ CreateFileMappingA(hFile, nullptr, PAGE_READONLY, fileSize.HighPart, fileSize.LowPart, mappingName);
+
+ CloseHandle(hFile);
+
+ if (!sharedFile.mapping)
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ bmiFile.fileSize = fileSize.QuadPart;
+ return sharedFile;
+ }
+
+ // 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
+ mappingName // 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());
+ }
+ if (bmiFile.fileSize == UINT32_MAX)
+ {
+ struct stat st;
+ if (fstat(fd, &st) == -1)
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ bmiFile.fileSize = st.st_size;
+ }
+ void *mapping = mmap(nullptr, bmiFile.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());
+ }
+ sharedFile.file = std::string_view(static_cast<char *>(mapping), bmiFile.fileSize);
+ return sharedFile;
+#endif
+}
+
+tl::expected<void, std::string> IPCManagerBS::closeBMIFileMapping(const Mapping &processMappingOfBMIFile)
+{
+#ifdef _WIN32
+ if (!CloseHandle(processMappingOfBMIFile.mapping))
+ {
+ return tl::unexpected(getErrorString());
+ }
+#else
+ if (munmap((void *)processMappingOfBMIFile.file.data(), processMappingOfBMIFile.file.size()) == -1)
+ {
+ return tl::unexpected(getErrorString());
+ }
+#endif
+ return {};
+}
+
+} // namespace P2978
\ 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..91e6bfc92ecc9
--- /dev/null
+++ b/clang/lib/IPC2978/IPCManagerCompiler.cpp
@@ -0,0 +1,589 @@
+
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
+#include "clang/IPC2978/Manager.hpp"
+#include "clang/IPC2978/Messages.hpp"
+
+#include <string>
+#include <utility>
+
+#ifdef _WIN32
+#include "clang/IPC2978/rapidhash.h"
+#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
+
+#define TRY_READ(var, func, ...) \
+ const auto &var = func(__VA_ARGS__); \
+ if (!var) \
+ { \
+ return tl::unexpected(var.error()); \
+ }
+
+#define TRY_READ_VAL(var, func, ...) \
+ const auto &var##_result = func(__VA_ARGS__); \
+ if (!var##_result) \
+ { \
+ return tl::unexpected(var##_result.error()); \
+ } \
+ auto &var = *var##_result;
+
+namespace P2978
+{
+
+Response::Response(std::string_view filePath_, const Mapping &mapping_, const FileType type_, const bool isSystem_)
+ : filePath(std::move(filePath_)), mapping(mapping_), type(type_), isSystem(isSystem_)
+{
+}
+
+static bool endsWith(const std::string_view str, const std::string &suffix)
+{
+ if (suffix.size() > str.size())
+ {
+ return false;
+ }
+ return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+tl::expected<std::string_view, std::string> IPCManagerCompiler::readInternal(char (&buffer)[4096]) const
+{
+ std::string *output = nullptr;
+ while (true)
+ {
+ uint32_t bytesRead;
+#ifdef _WIN32
+ const bool success = ReadFile((HANDLE)STD_INPUT_HANDLE, // pipe handle
+ buffer, // buffer to receive reply
+ 4096, // 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(STDIN_FILENO, buffer, 4096);
+ if (bytesRead == -1)
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+#endif
+ if (!bytesRead)
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::READ_FILE_ZERO_BYTES_READ));
+ }
+
+ if (!output)
+ {
+ if (bytesRead < strlen(delimiter))
+ {
+ return tl::unexpected("P2978 Error: Received string only has delimiter but not the size of payload\n");
+ }
+ output = new std::string{};
+ allocations.emplace_back(output);
+ }
+
+ output->append(buffer, bytesRead);
+
+ // We return once we receive the delimiter.
+ if (endsWith(*output, delimiter))
+ {
+ return std::string_view{output->data(), output->size() - strlen(delimiter)};
+ }
+ }
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::writeInternal(const std::string_view buffer) const
+{
+#ifdef _WIN32
+ const bool success = WriteFile(reinterpret_cast<HANDLE>(STD_OUTPUT_HANDLE), // 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(STDOUT_FILENO, buffer.data(), buffer.size()); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+#endif
+ return {};
+}
+
+tl::expected<IPCManagerCompiler::BMIFileMapping, std::string> IPCManagerCompiler::readProcessMappingOfBMIFile(
+ const std::string_view message, uint32_t &bytesRead)
+{
+ const auto &r = readPath(message, bytesRead);
+ if (!r)
+ {
+ return tl::unexpected(r.error());
+ }
+ const auto &r2 = readUInt32(message, bytesRead);
+ if (!r2)
+ {
+ return tl::unexpected(r2.error());
+ }
+
+ BMIFile file;
+ file.filePath = *r;
+ file.fileSize = *r2;
+
+ if (const auto &r3 = readSharedMemoryBMIFile(file); r3)
+ {
+ filePathProcessMapping.emplace(file.filePath, r3.value());
+ BMIFileMapping bmiFileMapping;
+ bmiFileMapping.file = file;
+ bmiFileMapping.mapping = *r3;
+ return bmiFileMapping;
+ }
+ else
+ {
+ return tl::unexpected(r3.error());
+ }
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::readLogicalNames(const std::string_view message,
+ uint32_t &bytesRead, const BMIFileMapping &mapping,
+ const FileType type, const bool isSystem)
+{
+ TRY_READ_VAL(logicalNamesSize, readUInt32, message, bytesRead);
+ for (uint32_t i = 0; i < logicalNamesSize; ++i)
+ {
+ TRY_READ_VAL(logicalName, readString, message, bytesRead);
+ responses.emplace(logicalName, Response(mapping.file.filePath, mapping.mapping, type, isSystem));
+ }
+
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::receiveBTCLastMessage() const
+{
+ char buffer[4096];
+ const auto &r = readInternal(buffer);
+ if (!r)
+ {
+ return tl::unexpected(r.error());
+ }
+
+ // The BTCLastMessage must be 1 byte of true signaling that build-system has successfully created a shared memory
+ // mapping of the BMI file.
+ if (buffer[0] != static_cast<char>(true))
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::INCORRECT_BTC_LAST_MESSAGE));
+ }
+
+ if (r->size() != 1)
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::receiveBTCModule(const CTBModule &moduleName)
+{
+ std::string buffer = getBufferWithType(CTB::MODULE);
+ writeString(buffer, moduleName.moduleName);
+ writeUInt32(buffer, buffer.size());
+ buffer.append(delimiter, strlen(delimiter));
+ // This call sends the CTBModule to the build-system.
+ if (const auto &r = writeInternal(buffer); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+
+ char stackBuffer[4096];
+ auto received = readInternal(stackBuffer);
+
+ if (!received)
+ {
+ return tl::unexpected(received.error());
+ }
+ const std::string_view message = *received;
+
+ uint32_t bytesRead = 0;
+
+ TRY_READ_VAL(requested, readProcessMappingOfBMIFile, message, bytesRead);
+ TRY_READ_VAL(isSystem, readBool, message, bytesRead);
+
+ std::string *str = new std::string(moduleName.moduleName);
+ allocations.emplace_back(str);
+ responses.emplace(*str, Response(requested.file.filePath, requested.mapping, FileType::MODULE, isSystem));
+
+ TRY_READ_VAL(modDepsSize, readUInt32, message, bytesRead);
+
+ for (uint32_t i = 0; i < modDepsSize; ++i)
+ {
+ TRY_READ_VAL(isHeaderUnit, readBool, message, bytesRead);
+ TRY_READ_VAL(modDepFile, readProcessMappingOfBMIFile, message, bytesRead);
+ TRY_READ_VAL(modDepIsSytem, readBool, message, bytesRead);
+ if (isHeaderUnit)
+ {
+ if (const auto &r = readLogicalNames(message, bytesRead, modDepFile, FileType::HEADER_UNIT, modDepIsSytem);
+ !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ }
+ else
+ {
+ if (const auto &r = readLogicalNames(message, bytesRead, modDepFile, FileType::MODULE, modDepIsSytem); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ }
+ }
+
+ if (message.size() != bytesRead)
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::receiveBTCNonModule(const CTBNonModule &nonModule)
+{
+ std::string buffer = getBufferWithType(CTB::NON_MODULE);
+ buffer.push_back(nonModule.isHeaderUnit);
+ writeString(buffer, nonModule.logicalName);
+ writeUInt32(buffer, buffer.size());
+ buffer.append(delimiter, strlen(delimiter));
+ // This call sends the CTBNonModule to the build-system.
+ if (const auto &r = writeInternal(buffer); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+
+ char stackBuffer[4096];
+ auto received = readInternal(stackBuffer);
+
+ if (!received)
+ {
+ return tl::unexpected(received.error());
+ }
+
+ std::string_view readCompilerMessage = *received;
+ uint32_t bytesRead = 0;
+
+ TRY_READ_VAL(isHeaderUnit, readBool, readCompilerMessage, bytesRead);
+ TRY_READ_VAL(isSystem, readBool, readCompilerMessage, bytesRead);
+ TRY_READ_VAL(headerFilesSize, readUInt32, readCompilerMessage, bytesRead);
+
+ for (uint32_t i = 0; i < headerFilesSize; ++i)
+ {
+ TRY_READ_VAL(logicalName, readString, readCompilerMessage, bytesRead);
+ TRY_READ_VAL(filePath, readPath, readCompilerMessage, bytesRead);
+ TRY_READ_VAL(isSystemHeaderFile, readBool, readCompilerMessage, bytesRead);
+
+ responses.emplace(logicalName, Response{filePath, {}, FileType::HEADER_FILE, isSystemHeaderFile});
+ }
+
+ std::string *str = new std::string(nonModule.logicalName);
+ allocations.emplace_back(str);
+ if (!isHeaderUnit)
+ {
+ TRY_READ_VAL(filePath, readPath, readCompilerMessage, bytesRead);
+ responses.emplace(*str, Response{filePath, {}, FileType::HEADER_FILE, isSystem});
+ if (readCompilerMessage.size() != bytesRead)
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ return {};
+ }
+
+ TRY_READ_VAL(file, readProcessMappingOfBMIFile, readCompilerMessage, bytesRead);
+ responses.emplace(*str, Response{file.file.filePath, file.mapping, FileType::HEADER_UNIT, isSystem});
+
+ TRY_READ(logicalNames, readLogicalNames, readCompilerMessage, bytesRead, file, FileType::HEADER_UNIT, isSystem);
+
+ TRY_READ_VAL(huDepsSize, readUInt32, readCompilerMessage, bytesRead);
+ for (uint32_t i = 0; i < huDepsSize; ++i)
+ {
+ TRY_READ_VAL(huDepFile, readProcessMappingOfBMIFile, readCompilerMessage, bytesRead);
+ TRY_READ_VAL(huDepIsSystem, readBool, readCompilerMessage, bytesRead);
+ TRY_READ(huDeplogicalNames, readLogicalNames, readCompilerMessage, bytesRead, huDepFile, FileType::HEADER_UNIT,
+ huDepIsSystem);
+ }
+
+ if (readCompilerMessage.size() != bytesRead)
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ return {};
+}
+
+tl::expected<Response, std::string> IPCManagerCompiler::findResponse(std::string_view logicalName, const FileType type)
+{
+#ifdef _WIN32
+ std::string logicalName2{logicalName};
+ if (type != FileType::MODULE)
+ {
+ for (char &c : logicalName2)
+ {
+ c = std::tolower(c);
+ }
+ }
+#endif
+
+ if (const auto &it = responses.find(logicalName);
+ // This requests from the build-system if we don't have an entry for the logicalName or if there is a type
+ // mismatch between the request and the response. Only allowed mismatch is if the request is of header-file and
+ // the response is a header-unit instead. For other mismatches compiler will request the build-system which will
+ // give not found error. HMake at config-time checks for the logicalName collision and also that a file is not
+ // registered as 2 of header-file, header-unit and module.
+ it == responses.end() ||
+ (it->second.type != type && (it->second.type != FileType::HEADER_UNIT || type != FileType::HEADER_FILE)))
+ {
+ if (type == FileType::MODULE)
+ {
+ CTBModule ctbModule;
+ ctbModule.moduleName = logicalName;
+ if (const auto &r2 = receiveBTCModule(ctbModule); !r2)
+ {
+ return tl::unexpected(r2.error());
+ }
+ }
+ else
+ {
+ CTBNonModule ctbNonModule;
+ ctbNonModule.logicalName = logicalName;
+ ctbNonModule.isHeaderUnit = type == FileType::HEADER_UNIT;
+ if (const auto &r2 = receiveBTCNonModule(ctbNonModule); !r2)
+ {
+ return tl::unexpected(r2.error());
+ }
+ }
+
+ return responses.at(logicalName);
+ }
+ else
+ {
+ return it->second;
+ }
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::sendCTBLastMessage(const uint32_t fileSize) const
+{
+ std::string buffer = getBufferWithType(CTB::LAST_MESSAGE);
+ writeUInt32(buffer, fileSize);
+ writeUInt32(buffer, buffer.size());
+ buffer.append(delimiter, strlen(delimiter));
+ if (const auto &r = writeInternal(buffer); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ return {};
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::sendCTBLastMessage(const std::string &bmiFile,
+ const std::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());
+ }
+
+ // mappingName is needed as the Windows kernel object names can't have \\ in them.
+ const uint64_t hash = rapidhash(filePath.data(), filePath.size());
+ char mappingName[17];
+ static constexpr char hex[] = "0123456789abcdef";
+ for (int i = 0; i < 8; i++)
+ {
+ const uint8_t byte = hash >> (56 - i * 8) & 0xFF;
+ mappingName[i * 2] = hex[byte >> 4];
+ mappingName[i * 2 + 1] = hex[byte & 0xF];
+ }
+ mappingName[16] = '\0';
+
+ 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, mappingName);
+ if (!hMap)
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ void *pView = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, bmiFile.size());
+ if (!pView)
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ memcpy(pView, bmiFile.c_str(), bmiFile.size());
+
+ if (!FlushViewOfFile(pView, bmiFile.size()))
+ {
+ return tl::unexpected(getErrorString());
+ }
+
+ UnmapViewOfFile(pView);
+ CloseHandle(hFile);
+
+ if (const auto &r = sendCTBLastMessage(fileSize.QuadPart); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+
+ // Build-system will send the BTCLastMessage after it has created the BMI file-mapping. Compiler process can not
+ // exit before that.
+ 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(fileSize); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+
+ // Build-system will send the BTCLastMessage after it has created the BMI file-mapping. Compiler process can not
+ // exit before that.
+ if (const auto &r = receiveBTCLastMessage(); !r)
+ {
+ return tl::unexpected(r.error());
+ }
+ munmap(mapping, fileSize);
+
+#endif
+
+ return {};
+}
+
+tl::expected<Mapping, std::string> IPCManagerCompiler::readSharedMemoryBMIFile(const BMIFile &file)
+{
+ Mapping f{};
+#ifdef _WIN32
+
+ // mappingName is needed as the Windows kernel object names can't have \\ in them.
+ const uint64_t hash = rapidhash(file.filePath.data(), file.filePath.size());
+ char mappingName[17];
+ static constexpr char hex[] = "0123456789abcdef";
+ for (int i = 0; i < 8; i++)
+ {
+ const uint8_t byte = hash >> (56 - i * 8) & 0xFF;
+ mappingName[i * 2] = hex[byte >> 4];
+ mappingName[i * 2 + 1] = hex[byte & 0xF];
+ }
+ mappingName[16] = '\0';
+
+ // 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
+ mappingName // 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)
+ {
+ 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.file = {static_cast<char *>(mapping), file.fileSize};
+#endif
+ return f;
+}
+
+tl::expected<void, std::string> IPCManagerCompiler::closeBMIFileMapping(const Mapping &processMappingOfBMIFile)
+{
+#ifdef _WIN32
+ UnmapViewOfFile(processMappingOfBMIFile.view);
+ CloseHandle(processMappingOfBMIFile.mapping);
+#else
+ if (munmap((void *)processMappingOfBMIFile.file.data(), processMappingOfBMIFile.file.size()) == -1)
+ {
+ return tl::unexpected(getErrorString());
+ }
+#endif
+ return {};
+}
+
+bool operator==(const CTBNonModule &lhs, const CTBNonModule &rhs)
+{
+ return lhs.isHeaderUnit == rhs.isHeaderUnit && lhs.logicalName == rhs.logicalName;
+}
+} // namespace P2978
\ 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..233b4b38b9dd2
--- /dev/null
+++ b/clang/lib/IPC2978/Manager.cpp
@@ -0,0 +1,262 @@
+
+#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 P2978
+{
+
+std::string getErrorString()
+{
+#ifdef _WIN32
+ const DWORD err = GetLastError();
+
+ char *msg_buf;
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
+ err, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), reinterpret_cast<char *>(&msg_buf), 0, nullptr);
+
+ if (msg_buf == nullptr)
+ {
+ char fallback_msg[128] = {};
+ snprintf(fallback_msg, sizeof(fallback_msg), "GetLastError() = %ld", err);
+ return fallback_msg;
+ }
+
+ std::string msg = msg_buf;
+ LocalFree(msg_buf);
+ return msg;
+#else
+ return {std::strerror(errno)};
+#endif
+}
+
+std::string getErrorString(const uint32_t bytesRead_, const uint32_t bytesProcessed_)
+{
+ return "Error: Bytes Readd vs Bytes Processed Mismatch.\nBytes Read: " + std::to_string(bytesRead_) +
+ ", Bytes Processed: " + std::to_string(bytesProcessed_);
+}
+
+std::string getErrorString(const ErrorCategory errorCategory_)
+{
+ std::string errorString;
+
+ switch (errorCategory_)
+ {
+ case ErrorCategory::PARSING_ERROR:
+ errorString = "P2978 Message Parsing Error.";
+ break;
+ case ErrorCategory::READ_FILE_ZERO_BYTES_READ:
+ errorString = "Error: ReadFile Zero Bytes Read.";
+ break;
+ case ErrorCategory::INCORRECT_BTC_LAST_MESSAGE:
+ errorString = "Error: Incorrect BTC Last Message.";
+ break;
+ case ErrorCategory::UNKNOWN_CTB_TYPE:
+ errorString = "Error: Unknown CTB message received.";
+ break;
+ case ErrorCategory::NONE:
+ std::string str = __FILE__;
+ str += ':';
+ str += __LINE__;
+ errorString = "P2978 IPC API internal error" + str;
+ break;
+ }
+
+ return errorString;
+}
+
+#ifndef _WIN32
+tl::expected<void, std::string> Manager::writeAll(const int fd, const char *buffer, const uint32_t count)
+{
+ uint32_t bytesWritten = 0;
+
+ while (bytesWritten != count)
+ {
+ const int32_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
+
+std::string Manager::getBufferWithType(CTB type)
+{
+ std::string buffer;
+ buffer.push_back(static_cast<uint8_t>(type));
+ return buffer;
+}
+
+void Manager::writeUInt32(std::string &buffer, const uint32_t value)
+{
+ const auto ptr = reinterpret_cast<const char *>(&value);
+ buffer.append(ptr, ptr + 4);
+}
+
+void Manager::writeString(std::string &buffer, const std::string_view &str)
+{
+ writeUInt32(buffer, str.size());
+ buffer.append(str.begin(), str.end()); // Insert all characters
+}
+
+void Manager::writePath(std::string &buffer, const std::string_view &str)
+{
+ writeUInt32(buffer, str.size());
+ buffer.append(str.begin(), str.end()); // Insert all characters
+ buffer.push_back('\0');
+}
+
+void Manager::writeBMIFile(std::string &buffer, const BMIFile &file)
+{
+ writePath(buffer, file.filePath);
+ writeUInt32(buffer, file.fileSize);
+}
+
+void Manager::writeModuleDep(std::string &buffer, const ModuleDep &dep)
+{
+ buffer.push_back(dep.isHeaderUnit);
+ writeBMIFile(buffer, dep.file);
+ buffer.push_back(dep.isSystem);
+ writeVectorOfStrings(buffer, dep.logicalNames);
+}
+
+void Manager::writeHuDep(std::string &buffer, const HuDep &dep)
+{
+ writeBMIFile(buffer, dep.file);
+ buffer.push_back(dep.isSystem);
+ writeVectorOfStrings(buffer, dep.logicalNames);
+}
+
+void Manager::writeHeaderFile(std::string &buffer, const HeaderFile &dep)
+{
+ writeString(buffer, dep.logicalName);
+ writePath(buffer, dep.filePath);
+ buffer.push_back(dep.isSystem);
+}
+
+void Manager::writeVectorOfStrings(std::string &buffer, const std::vector<std::string_view> &strs)
+{
+ writeUInt32(buffer, strs.size());
+ for (const std::string_view &str : strs)
+ {
+ writeString(buffer, str);
+ }
+}
+
+void Manager::writeVectorOfProcessMappingOfBMIFiles(std::string &buffer, const std::vector<BMIFile> &files)
+{
+ writeUInt32(buffer, files.size());
+ for (const BMIFile &file : files)
+ {
+ writeBMIFile(buffer, file);
+ }
+}
+
+void Manager::writeVectorOfModuleDep(std::string &buffer, const std::vector<ModuleDep> &deps)
+{
+ writeUInt32(buffer, deps.size());
+ for (const ModuleDep &dep : deps)
+ {
+ writeModuleDep(buffer, dep);
+ }
+}
+
+void Manager::writeVectorOfHuDeps(std::string &buffer, const std::vector<HuDep> &deps)
+{
+ writeUInt32(buffer, deps.size());
+ for (const HuDep &dep : deps)
+ {
+ writeHuDep(buffer, dep);
+ }
+}
+
+void Manager::writeVectorOfHeaderFiles(std::string &buffer, const std::vector<HeaderFile> &headerFiles)
+{
+ writeUInt32(buffer, headerFiles.size());
+ for (const HeaderFile &headerFile : headerFiles)
+ {
+ writeHeaderFile(buffer, headerFile);
+ }
+}
+
+tl::expected<bool, std::string> Manager::readBool(const std::string_view message, uint32_t &bytesRead)
+{
+ if (bytesRead + 1 > message.size())
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ bool result = *reinterpret_cast<const bool *>(message.data() + bytesRead);
+ bytesRead += 1;
+ return result;
+}
+
+tl::expected<uint32_t, std::string> Manager::readUInt32(const std::string_view message, uint32_t &bytesRead)
+{
+ if (bytesRead + 4 > message.size())
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ uint32_t result = *reinterpret_cast<const uint32_t *>(message.data() + bytesRead);
+ bytesRead += 4;
+ return result;
+}
+
+tl::expected<std::string_view, std::string> Manager::readString(const std::string_view message, uint32_t &bytesRead)
+{
+ auto r = readUInt32(message, bytesRead);
+ if (!r)
+ {
+ return tl::unexpected(r.error());
+ }
+ const uint32_t stringSize = *r;
+ if (bytesRead + stringSize > message.size())
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ std::string_view result = {message.data() + bytesRead, stringSize};
+ bytesRead += stringSize;
+ return result;
+}
+
+tl::expected<std::string_view, std::string> Manager::readPath(const std::string_view message, uint32_t &bytesRead)
+{
+ auto r = readUInt32(message, bytesRead);
+ if (!r)
+ {
+ return tl::unexpected(r.error());
+ }
+ const uint32_t stringSize = *r;
+ if (bytesRead + stringSize > message.size())
+ {
+ return tl::unexpected(getErrorString(ErrorCategory::PARSING_ERROR));
+ }
+ std::string_view result = {message.data() + bytesRead, stringSize};
+ bytesRead += stringSize;
+ // This string is followed by \0
+ bytesRead += 1;
+ return result;
+}
+
+} // namespace P2978
\ 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..32f0be9c5eccc
--- /dev/null
+++ b/clang/lib/IPC2978/setup.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+import os
+import shutil
+
+# The directory where this script resides (lib/clang/IPC2978)
+source_dir = os.path.abspath(os.path.dirname(__file__))
+# Assumed that IPC2978 repo is present in parallel with llvm repo.
+copy_from = os.path.abspath(source_dir + ("../" * 5) + "ipc2978api/") + os.sep
+# The directory for the header-files
+include_dir = (
+ os.path.abspath(os.path.join(source_dir, "../../include/clang/IPC2978")) + os.sep
+)
+# The directory for the unit-tests.
+ipc_test_source_file = os.path.abspath(
+ os.path.join(source_dir, "../../unittests/IPC2978/IPC2978Test.cpp")
+)
+
+shutil.copytree(copy_from + "include", include_dir, dirs_exist_ok=True)
+shutil.copytree(copy_from + "src", source_dir, dirs_exist_ok=True)
+# We'll process files in both include and source directories
+roots = [source_dir, include_dir]
+
+# Gather all header filenames in the include directory (top-level only)
+include_files = [f for f in os.listdir(include_dir) if f.endswith(".hpp")]
+
+files = []
+
+# Iterate through the source and include directories
+for root in roots:
+ # Skipping the CMakeLists.txt and .py files
+ files.extend(
+ [
+ os.path.join(root, x)
+ for x in os.listdir(root)
+ if not os.path.join(root, x).endswith(".txt")
+ and not os.path.join(root, x).endswith(".py")
+ ]
+ )
+
+for file in files:
+ out_lines = []
+ with open(file, "r", encoding="utf-8") as f:
+ lines = f.readlines()
+ # Examine each line for an include directive
+ for line in lines:
+ if line.startswith('#include "'):
+ out_lines.append(
+ line.replace('#include "', '#include "clang/IPC2978/', 1)
+ )
+ else:
+ out_lines.append(line)
+ with open(file, "w", encoding="utf-8") as f:
+ for line in out_lines:
+ f.writelines(line)
+
+shutil.copy(copy_from + "/tests/ClangTest.cpp", ipc_test_source_file)
+
+# Modifying the copied ClangTest.cpp file
+out_lines = []
+with open(ipc_test_source_file, "r", encoding="utf-8") as f:
+ lines = f.readlines()
+ # Examine each line for an include directive
+ for line in lines:
+ if line.startswith("// #define IS_THIS_CLANG_REPO"):
+ out_lines.append("#define IS_THIS_CLANG_REPO")
+ else:
+ out_lines.append(line)
+with open(ipc_test_source_file, "w", encoding="utf-8") as f:
+ for line in out_lines:
+ f.writelines(line)
diff --git a/clang/lib/Lex/CMakeLists.txt b/clang/lib/Lex/CMakeLists.txt
index f61737cd68021..879dc26f5a7a4 100644
--- a/clang/lib/Lex/CMakeLists.txt
+++ b/clang/lib/Lex/CMakeLists.txt
@@ -34,4 +34,5 @@ add_clang_library(clangLex
LINK_LIBS
clangBasic
+ clangIPC2978
)
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index b90c04776ff9e..47058ed792ba2 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -23,6 +23,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TokenKinds.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Lex/CodeCompletionHandler.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/LexDiagnostic.h"
@@ -2419,7 +2420,6 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
SmallString<1024> RelativePath;
// We get the raw path only if we have 'Callbacks' to which we later pass
// the path.
- ModuleMap::KnownHeader SuggestedModule;
SourceLocation FilenameLoc = FilenameTok.getLocation();
StringRef LookupFilename = Filename;
@@ -2434,10 +2434,31 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
BackslashStyle = llvm::sys::path::Style::windows;
}
- OptionalFileEntryRef File = LookupHeaderIncludeOrImport(
- &CurDir, Filename, FilenameLoc, FilenameRange, FilenameTok,
- IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile,
- LookupFilename, RelativePath, SearchPath, SuggestedModule, isAngled);
+ ModuleMap::KnownHeader SuggestedModule;
+ OptionalFileEntryRef File;
+
+ if (P2978::managerCompiler) {
+ const auto &Result = P2978::managerCompiler->findResponse(
+ std::string(Filename), IsImportDecl ? P2978::FileType::HEADER_UNIT
+ : P2978::FileType::HEADER_FILE);
+ if (!Result) {
+ // error happend in receiving message.
+ }
+ File = getFileManager().getOptionalFileRef(Result->filePath);
+ if (Result->isSystem)
+ getHeaderSearchInfo().MarkFileSystemHeader(*File);
+ if (Result->type == P2978::FileType::HEADER_UNIT) {
+ IsImportDecl = true;
+ SuggestedModule = {
+ getModuleLoader().loadIPCReceivedHeaderUnit(Result->filePath, EndLoc),
+ ModuleMap::NormalHeader};
+ }
+ } else {
+ File = LookupHeaderIncludeOrImport(
+ &CurDir, Filename, FilenameLoc, FilenameRange, FilenameTok,
+ IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile,
+ LookupFilename, RelativePath, SearchPath, SuggestedModule, isAngled);
+ }
if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
if (File && isPCHThroughHeader(&File->getFileEntry()))
@@ -2477,8 +2498,9 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
Module *ModuleToImport = SuggestedModule.getModule();
- bool MaybeTranslateInclude = Action == Enter && File && ModuleToImport &&
- !ModuleToImport->isForBuilding(getLangOpts());
+ bool MaybeTranslateInclude =
+ Action == Enter && File && ModuleToImport &&
+ (!ModuleToImport->isForBuilding(getLangOpts()) || IsImportDecl);
// Maybe a usable Header Unit
bool UsableHeaderUnit = false;
@@ -2498,8 +2520,12 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
// Determine whether we should try to import the module for this #include, if
// there is one. Don't do so if precompiled module support is disabled or we
- // are processing this module textually (because we're building the module).
- if (MaybeTranslateInclude && (UsableHeaderUnit || UsableClangHeaderModule)) {
+ // are processing this module textually (because we're building the module)
+ // or when noScanIPC is set as header-unit is already loaded.
+ if (P2978::managerCompiler && IsImportDecl) {
+ Action = Import;
+ } else if (MaybeTranslateInclude &&
+ (UsableHeaderUnit || UsableClangHeaderModule)) {
// If this include corresponds to a module but that module is
// unavailable, diagnose the situation and bail out.
// FIXME: Remove this; loadModule does the same check (but produces
@@ -2638,9 +2664,11 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
}
// Issue a diagnostic if the name of the file on disk has a different case
- // than the one we're about to open.
+ // than the one we're about to open. Not checked if it is an IPC received
+ // module.
const bool CheckIncludePathPortability =
- !IsMapped && !File->getFileEntry().tryGetRealPathName().empty();
+ !IsImportDecl && !IsMapped &&
+ !File->getFileEntry().tryGetRealPathName().empty();
if (CheckIncludePathPortability) {
StringRef Name = LookupFilename;
diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp
index c8ee625eb57ad..070c1148d7b8f 100644
--- a/clang/lib/Parse/ParseAST.cpp
+++ b/clang/lib/Parse/ParseAST.cpp
@@ -15,6 +15,9 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ExternalASTSource.h"
#include "clang/AST/Stmt.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/IPC2978/IPCManagerBS.hpp"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Parse/Parser.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 7c3a6fceb3623..1047bca86d573 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -67,6 +67,7 @@
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Basic/Version.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/MacroInfo.h"
@@ -3488,9 +3489,26 @@ ASTReader::ReadControlBlock(ModuleFile &F,
// explicit name to file mappings. Also, we will still verify the
// size/signature making sure it is essentially the same file but
// perhaps in a different location.
- if (ImportedKind == MK_PrebuiltModule || ImportedKind == MK_ExplicitModule)
- ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName(
- ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule);
+ if (ImportedKind == MK_PrebuiltModule ||
+ ImportedKind == MK_ExplicitModule) {
+ if (P2978::managerCompiler) {
+ if (IsImportingStdCXXModule) {
+ // In case of header-units ImportedFile will be initialized with
+ // ImportedName
+ const auto &Result = P2978::managerCompiler->findResponse(
+ std::string(ImportedName), IsImportingStdCXXModule
+ ? P2978::FileType::MODULE
+ : P2978::FileType::HEADER_UNIT);
+ if (!Result) {
+ // error happened in receiving message
+ }
+ ImportedFile = ModuleFileName::makeExplicit(Result->filePath);
+ }
+ } else {
+ ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName(
+ ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule);
+ }
+ }
if (IsImportingStdCXXModule && ImportedFile.empty()) {
Diag(diag::err_failed_to_find_module_file) << ImportedName;
diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp
index ed7b6cf67674e..83ff3e75d94db 100644
--- a/clang/lib/Serialization/ModuleManager.cpp
+++ b/clang/lib/Serialization/ModuleManager.cpp
@@ -14,6 +14,7 @@
#include "clang/Serialization/ModuleManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LLVM.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/ModuleMap.h"
#include "clang/Serialization/GlobalModuleIndex.h"
@@ -198,15 +199,27 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
return Missing;
}
- // Get a buffer of the file and close the file descriptor when done.
- // The file is volatile because in a parallel build we expect multiple
- // compiler processes to use the same module file rebuilding it if needed.
- //
- // RequiresNullTerminator is false because module files don't need it, and
- // this allows the file to still be mmapped.
- auto Buf = FileMgr.getBufferForFile(*Entry,
- /*IsVolatile=*/true,
- /*RequiresNullTerminator=*/false);
+ auto getBuf = [&] {
+ if (P2978::managerCompiler) {
+ StringRef EntryName = Entry->getName();
+ return llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>(
+ llvm::MemoryBuffer::getMemBuffer(
+ P2978::managerCompiler->filePathProcessMapping
+ .at(std::string(EntryName))
+ .file));
+ }
+
+ // Get a buffer of the file and close the file descriptor when done.
+ // The file is volatile because in a parallel build we expect multiple
+ // compiler processes to use the same module file rebuilding it if needed.
+ //
+ // RequiresNullTerminator is false because module files don't need it, and
+ // this allows the file to still be mmapped.
+ return FileMgr.getBufferForFile(*Entry,
+ /*IsVolatile=*/true,
+ /*RequiresNullTerminator=*/false);
+ };
+ auto Buf = getBuf();
if (!Buf) {
ErrorStr = Buf.getError().message();
diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp
index 0a9ded1cf213a..c06d5cc4a6c38 100644
--- a/clang/tools/driver/cc1_main.cpp
+++ b/clang/tools/driver/cc1_main.cpp
@@ -25,6 +25,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/FrontendTool/Utils.h"
+#include "clang/IPC2978/IPCManagerCompiler.hpp"
#include "clang/Options/Options.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
#include "llvm/ADT/Statistic.h"
@@ -280,6 +281,10 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
}();
Clang->createVirtualFileSystem(std::move(VFS), DiagsBuffer);
+ if (Clang->getHeaderSearchOpts().NoScanIPC) {
+ P2978::managerCompiler = new P2978::IPCManagerCompiler();
+ }
+
// Create the actual diagnostics engine.
Clang->createDiagnostics();
@@ -304,19 +309,15 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// If any timers were active but haven't been destroyed yet, print their
// results now. This happens in -disable-free mode.
- {
- // This isn't a formal input or output of the compiler.
- auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
- std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
- if (Clang->getCodeGenOpts().TimePassesJson) {
- *IOFile << "{\n";
- llvm::TimerGroup::printAllJSONValues(*IOFile, "");
- *IOFile << "\n}\n";
- } else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
- llvm::TimerGroup::printAll(*IOFile);
- }
- llvm::TimerGroup::clearAll();
+ std::unique_ptr<raw_ostream> IOFile = llvm::CreateInfoOutputFile();
+ if (Clang->getCodeGenOpts().TimePassesJson) {
+ *IOFile << "{\n";
+ llvm::TimerGroup::printAllJSONValues(*IOFile, "");
+ *IOFile << "\n}\n";
+ } else if (!Clang->getCodeGenOpts().TimePassesStatsFile) {
+ llvm::TimerGroup::printAll(*IOFile);
}
+ llvm::TimerGroup::clearAll();
if (llvm::timeTraceProfilerEnabled()) {
if (auto profilerOutput = Clang->createOutputFile(
diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index 2ae0906dd1571..90e7e5d710cd2 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -94,6 +94,7 @@ add_subdirectory(Rewrite)
add_subdirectory(ScalableStaticAnalysisFramework)
add_subdirectory(Sema)
add_subdirectory(CodeGen)
+add_subdirectory(IPC2978)
if(HAVE_CLANG_REPL_SUPPORT)
add_subdirectory(Interpreter)
endif()
@@ -128,6 +129,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..620e88b825ff5
--- /dev/null
+++ b/clang/unittests/IPC2978/IPC2978Test.cpp
@@ -0,0 +1,1086 @@
+//===- 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
+#include <cstring>
+#include <iostream>
+#ifdef IS_THIS_CLANG_REPO
+#include "clang/IPC2978/IPCManagerBS.hpp"
+#include "gtest/gtest.h"
+#else
+#include "IPCManagerBS.hpp"
+#include "Testing.hpp"
+#endif
+
+#include <filesystem>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <sys/epoll.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <wordexp.h>
+#endif
+
+using namespace std::filesystem;
+using namespace P2978;
+using namespace std;
+
+#ifdef _WIN32
+#define CLANG_CMD ".\\clang.exe"
+#else
+#define CLANG_CMD "./clang"
+#endif
+
+namespace
+{
+
+void exitFailure(const string &str)
+{
+#ifdef IS_THIS_CLANG_REPO
+ FAIL() << str + '\n';
+#else
+ std::cerr << str << std::endl;
+ exit(EXIT_FAILURE);
+#endif
+}
+
+uint64_t createMultiplex()
+{
+#ifdef _WIN32
+ HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, // handle to associate
+ nullptr, // existing IOCP handle
+ 0, // completion key (use pipe handle)
+ 0 // number of concurrent threads (0 = default)
+ );
+ if (iocp == nullptr)
+ {
+ exitFailure(getErrorString());
+ }
+ return reinterpret_cast<uint64_t>(iocp);
+#else
+ return epoll_create1(0);
+#endif
+}
+
+struct RunCommand
+{
+ uint64_t pid;
+ uint64_t readPipe;
+ uint64_t writePipe;
+ int exitStatus;
+ uint64_t startAsyncProcess(const char *command, uint64_t serverFd);
+ void reapProcess() const;
+};
+
+#ifdef _WIN32
+
+// Copied partially from Ninja
+uint64_t RunCommand::startAsyncProcess(const char *command, uint64_t serverFd)
+{
+ // One BTarget can launch multiple processes so we also append the clock::now().
+ const string read_pipe_name = R"(\\.\pipe\read{}{})";
+ const string write_pipe_name = R"(\\.\pipe\write{}{})";
+
+ // ===== CREATE READ PIPE (for reading child's stdout/stderr - ASYNC) =====
+ HANDLE readPipe_ = CreateNamedPipeA(read_pipe_name.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0, INFINITE, NULL);
+ if (readPipe_ == INVALID_HANDLE_VALUE)
+ {
+ exitFailure(getErrorString());
+ }
+
+ // Register read pipe with IOCP for async operations
+ if (!CreateIoCompletionPort(readPipe_, (HANDLE)serverFd, (ULONG_PTR)readPipe_, 0))
+ {
+ exitFailure(getErrorString());
+ }
+
+ OVERLAPPED readOverlappedIO = {};
+ if (!ConnectNamedPipe(readPipe_, &readOverlappedIO) && GetLastError() != ERROR_IO_PENDING)
+ {
+ exitFailure(getErrorString());
+ }
+
+ // Get the write end for child's stdout/stderr
+ HANDLE outputWriteHandle = CreateFileA(read_pipe_name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (outputWriteHandle == INVALID_HANDLE_VALUE)
+ {
+ exitFailure(getErrorString());
+ }
+
+ {
+ char buffer[4096];
+ DWORD bytesRead = 0;
+ OVERLAPPED overlapped = {0};
+
+ // Wait for the read to complete.
+ ULONG_PTR completionKey = 0;
+ LPOVERLAPPED completedOverlapped = nullptr;
+ if (!GetQueuedCompletionStatus((HANDLE)serverFd, &bytesRead, &completionKey, &completedOverlapped, INFINITE))
+ {
+ exitFailure(getErrorString());
+ }
+ }
+
+ HANDLE childStdoutPipe;
+ if (!DuplicateHandle(GetCurrentProcess(), outputWriteHandle, GetCurrentProcess(), &childStdoutPipe, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ exitFailure(getErrorString());
+ }
+ CloseHandle(outputWriteHandle);
+
+ // ===== CREATE WRITE PIPE (for writing to child's stdin - SYNC) =====
+ // Note: No FILE_FLAG_OVERLAPPED - this makes it synchronous
+ HANDLE writePipe_ = CreateNamedPipeA(write_pipe_name.c_str(),
+ PIPE_ACCESS_OUTBOUND, // No FILE_FLAG_OVERLAPPED
+ PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0, INFINITE, NULL);
+ if (writePipe_ == INVALID_HANDLE_VALUE)
+ {
+ exitFailure(getErrorString());
+ }
+
+ // Get the read end for child's stdin
+ HANDLE inputReadHandle = CreateFileA(write_pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (inputReadHandle == INVALID_HANDLE_VALUE)
+ {
+ exitFailure(getErrorString());
+ }
+
+ HANDLE childStdinPipe;
+ if (!DuplicateHandle(GetCurrentProcess(), inputReadHandle, GetCurrentProcess(), &childStdinPipe, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ exitFailure(getErrorString());
+ }
+ CloseHandle(inputReadHandle);
+
+ STARTUPINFOA startup_info;
+ memset(&startup_info, 0, sizeof(startup_info));
+ startup_info.cb = sizeof(STARTUPINFO);
+
+ bool use_console_ = false;
+ if (!use_console_)
+ {
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = childStdinPipe; // Child reads from this
+ startup_info.hStdOutput = childStdoutPipe; // Child writes to this
+ startup_info.hStdError = childStdoutPipe; // Child writes to this
+ }
+
+ PROCESS_INFORMATION process_info;
+ memset(&process_info, 0, sizeof(process_info));
+
+ // Ninja handles ctrl-c, except for subprocesses in console pools.
+ DWORD process_flags = use_console_ ? 0 : CREATE_NEW_PROCESS_GROUP;
+
+ // Do not prepend 'cmd /c' on Windows, this breaks command
+ // lines greater than 8,191 chars.
+ if (!CreateProcessA(NULL, (char *)command, NULL, NULL,
+ /* inherit handles */ TRUE, process_flags, NULL, NULL, &startup_info, &process_info))
+ {
+ exitFailure(getErrorString());
+ }
+
+ // Close pipe channels only used by the child.
+ CloseHandle(childStdoutPipe);
+ CloseHandle(childStdinPipe);
+
+ CloseHandle(process_info.hThread);
+
+ readPipe = (uint64_t)readPipe_; // Parent reads child's output from this (ASYNC)
+ writePipe = (uint64_t)writePipe_; // Parent writes to child's input via this (SYNC)
+ pid = (uint64_t)process_info.hProcess;
+
+ return readPipe;
+}
+
+void RunCommand::reapProcess() const
+{
+ if (WaitForSingleObject((HANDLE)pid, INFINITE) == WAIT_FAILED)
+ {
+ exitFailure(getErrorString());
+ }
+
+ if (!GetExitCodeProcess((HANDLE)pid, (LPDWORD)&exitStatus))
+ {
+ exitFailure(getErrorString());
+ }
+
+ if (!CloseHandle((HANDLE)pid) || !CloseHandle((HANDLE)readPipe) || !CloseHandle((HANDLE)writePipe))
+ {
+ exitFailure(getErrorString());
+ }
+}
+
+#else
+
+uint64_t RunCommand::startAsyncProcess(const char *command, uint64_t serverFd)
+{
+ // Create pipes for stdout and stderr
+ int stdoutPipesLocal[2];
+ if (pipe(stdoutPipesLocal) == -1)
+ {
+ exitFailure(getErrorString());
+ }
+
+ // Create pipe for stdin
+ int stdinPipesLocal[2];
+ if (pipe(stdinPipesLocal) == -1)
+ {
+ exitFailure(getErrorString());
+ }
+
+ readPipe = stdoutPipesLocal[0];
+ writePipe = stdinPipesLocal[1];
+
+ pid = fork();
+ if (pid == -1)
+ {
+ exitFailure(getErrorString());
+ }
+ if (pid == 0)
+ {
+ // Child process
+
+ // Redirect stdin from the pipe
+ dup2(stdinPipesLocal[0], STDIN_FILENO);
+
+ // Redirect stdout and stderr to the pipes
+ dup2(stdoutPipesLocal[1], STDOUT_FILENO); // Redirect stdout to stdout_pipe
+ dup2(stdoutPipesLocal[1], STDERR_FILENO); // Redirect stderr to stderr_pipe
+
+ // Close unused pipe ends
+ close(stdoutPipesLocal[0]);
+ close(stdoutPipesLocal[1]);
+ close(stdinPipesLocal[0]);
+ close(stdinPipesLocal[1]);
+
+ wordexp_t p;
+ if (wordexp(command, &p, 0) != 0)
+ {
+ perror("wordexp");
+ _exit(127);
+ }
+
+ // p.we_wordv is a NULL-terminated argv suitable for exec*
+ char **argv = p.we_wordv;
+
+ // Use execvp so PATH is searched and environment is inherited
+ execvp(argv[0], argv);
+
+ // If execvp returns, it failed:
+ perror("execvp");
+ wordfree(&p);
+ _exit(127);
+ }
+
+ // Parent process
+ // Close unused pipe ends
+ close(stdoutPipesLocal[1]);
+ close(stdinPipesLocal[0]);
+ return readPipe;
+}
+
+void RunCommand::reapProcess() const
+{
+ if (waitpid(pid, const_cast<int *>(&exitStatus), 0) < 0)
+ {
+ exitFailure(getErrorString());
+ }
+ if (close(readPipe) == -1)
+ {
+ exitFailure(getErrorString());
+ }
+ if (close(writePipe) == -1)
+ {
+ exitFailure(getErrorString());
+ }
+}
+#endif
+
+string compilerTestPrunedOutput;
+uint64_t serverFd;
+CTB type;
+char buffer[320];
+RunCommand runCommand;
+
+bool endsWith(const std::string &str, const std::string &suffix)
+{
+ if (suffix.size() > str.size())
+ {
+ return false;
+ }
+ return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+void readCompilerMessage(const uint64_t serverFd, const uint64_t readFd)
+{
+#ifdef _WIN32
+ HANDLE hIOCP = reinterpret_cast<HANDLE>(static_cast<uintptr_t>(serverFd));
+ HANDLE hPipe = reinterpret_cast<HANDLE>(readFd);
+
+ while (true)
+ {
+ char buffer[4096];
+ DWORD bytesRead = 0;
+ OVERLAPPED overlapped = {0};
+
+ // Initiate async read. Even if it is completed successfully, we will get the completion packet. We don't get
+ // the packet only if it fails immediately with error other than ERROR_IO_PENDING
+ BOOL result = ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, &overlapped);
+
+ DWORD error = GetLastError();
+ if (!result && error != ERROR_IO_PENDING)
+ {
+ if (error == ERROR_BROKEN_PIPE)
+ {
+ // read complete
+ return;
+ }
+ exitFailure(getErrorString());
+ }
+
+ bytesRead = 0;
+
+ // Wait for the read to complete.
+ ULONG_PTR completionKey = 0;
+ LPOVERLAPPED completedOverlapped = nullptr;
+
+ if (!GetQueuedCompletionStatus(hIOCP, &bytesRead, &completionKey, &completedOverlapped, INFINITE))
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ {
+ // completed
+ return;
+ }
+ exitFailure(getErrorString());
+ }
+
+ // Verify completion is for our pipe
+ if (completionKey != (ULONG_PTR)hPipe)
+ {
+ exitFailure("Unexpected completion key");
+ }
+
+ if (bytesRead == 0)
+ {
+ // completed
+ return;
+ }
+
+ // Append read data to string
+ for (DWORD i = 0; i < bytesRead; ++i)
+ {
+ compilerTestPrunedOutput.push_back(buffer[i]);
+ }
+
+ // Check for terminator
+ if (endsWith(compilerTestPrunedOutput, delimiter))
+ {
+ return;
+ }
+ }
+#else
+
+ epoll_event ev{};
+ ev.events = EPOLLIN;
+ if (epoll_ctl(serverFd, EPOLL_CTL_ADD, readFd, &ev) == -1)
+ {
+ exitFailure(getErrorString());
+ }
+
+ epoll_wait(serverFd, &ev, 1, -1);
+ while (true)
+ {
+ char buffer[4096];
+ const int readCount = read(readFd, buffer, 4096);
+ if (readCount == 0)
+ {
+ return;
+ }
+ if (readCount == -1)
+ {
+ exitFailure(getErrorString());
+ }
+ for (uint32_t i = 0; i < readCount; ++i)
+ {
+ compilerTestPrunedOutput.push_back(buffer[i]);
+ }
+
+ if (endsWith(compilerTestPrunedOutput, delimiter))
+ {
+ break;
+ }
+ }
+
+ if (epoll_ctl(serverFd, EPOLL_CTL_DEL, readFd, &ev) == -1)
+ {
+ exitFailure(getErrorString());
+ }
+#endif
+}
+void pruneCompilerOutput(IPCManagerBS &manager)
+{
+ // Prune the compiler output. and make a new string of the compiler-message output.
+ const uint32_t prunedSize = compilerTestPrunedOutput.size();
+ if (prunedSize < 4 + strlen(delimiter))
+ {
+ exitFailure("received string only has delimiter but not the size of payload\n");
+ }
+
+ const uint32_t payloadSize =
+ *reinterpret_cast<uint32_t *>(compilerTestPrunedOutput.data() + (prunedSize - (4 + strlen(delimiter))));
+ const char *payloadStart = compilerTestPrunedOutput.data() + (prunedSize - (4 + strlen(delimiter) + payloadSize));
+ if (const auto &r2 = IPCManagerBS::receiveMessage(buffer, type, string_view{payloadStart, payloadSize}); !r2)
+ {
+ exitFailure(r2.error());
+ }
+ compilerTestPrunedOutput.resize(prunedSize - (4 + strlen(delimiter) + payloadSize));
+}
+
+IPCManagerBS readFirstCompilerStdout(const string_view compileCommand, const bool ctbMessageExpected)
+{
+ // todo
+ // initialize
+
+ serverFd = createMultiplex();
+ runCommand.startAsyncProcess(compileCommand.data(), serverFd);
+ IPCManagerBS manager(runCommand.writePipe);
+
+ if (ctbMessageExpected)
+ {
+ readCompilerMessage(serverFd, runCommand.readPipe);
+ if (!endsWith(compilerTestPrunedOutput, delimiter))
+ {
+ exitFailure("early exit by CompilerTest");
+ }
+ pruneCompilerOutput(manager);
+ }
+ return manager;
+}
+
+void readCompilerStdout(IPCManagerBS &manager)
+{
+ readCompilerMessage(serverFd, runCommand.readPipe);
+ if (!endsWith(compilerTestPrunedOutput, delimiter))
+ {
+ exitFailure("early exit by CompilerTest");
+ }
+ pruneCompilerOutput(manager);
+}
+
+void endCompilerTest()
+{
+ readCompilerMessage(serverFd, runCommand.readPipe);
+ runCommand.reapProcess();
+ std::cout << compilerTestPrunedOutput << std::endl;
+ if (runCommand.exitStatus != EXIT_SUCCESS)
+ {
+ std::cout << "CompilerTest did not exit successfully. ExitCode is" << runCommand.exitStatus << std::endl;
+ }
+}
+
+// main.cpp
+const string mainDotCpp = R"(
+// only one request of Foo will be made as A and big.hpp
+// will be provided with it.
+import Foo;
+import A;
+#include "y.hpp"
+#include "z.hpp"
+
+int main()
+{
+ Hello();
+ World();
+ Foo();
+}
+)";
+
+// Creates all the input files (source files + pcm files) that are needed for the test.
+void setupTest()
+{
+ // a.cpp
+ const string aDotCpp = R"(
+export module A; // primary module interface unit
+
+export import :B; // Hello() is visible when importing 'A'.
+import :C; // WorldImpl() is now visible only for 'a.cpp'.
+// export import :C; // ERROR: Cannot export a module implementation unit.
+
+// World() is visible by any translation unit importing 'A'.
+export char const* World()
+{
+ return WorldImpl();
+}
+)";
+ // a-b.cpp
+ const string aBDotCPP = R"(
+export module A:B; // partition module interface unit
+
+// Hello() is visible by any translation unit importing 'A'.
+export char const* Hello() { return "Hello"; }
+)";
+
+ // a-c.cpp
+ const string aCDotCPP = R"(
+module A:C; // partition module implementation unit
+
+// WorldImpl() is visible by any module unit of 'A' importing ':C'.
+char const* WorldImpl() { return "World"; }
+)";
+
+ // m.hpp, n.hpp and o.hpp are to be used as header-units, header-files
+ // while x.hpp, y.hpp and z.hpp are to be used as big-hu by include big.hpp
+
+ // m.hpp
+ const string mDotHpp = R"(
+// this file can not be included without first defining M_HEADER_FILE
+// this is to demonstrate difference between header-file and header-unit.
+// as macros don't seep into header-units
+
+#ifdef M_HEADER_FILE
+inline int m = 5;
+#else
+fail compilation
+#endif
+)";
+
+ // n.hpp
+ const string nDotHpp = R"(
+// should work just fine as macro should not seep in here while inclusion.
+
+#ifdef N_HEADER_FILE
+fail compilation
+#else
+#define M_HEADER_FILE
+#include "m.hpp"
+inline int n = 5 + m;
+#endif
+
+// COMMAND_MACRO should be defined while compiling this.
+// however, it should still be fine if it is not defined while compiling
+// a file consuming this
+
+#ifndef COMMAND_MACRO
+fail compilation
+#endif
+)";
+
+ // o.hpp
+ const string oDotHpp = R"(
+// TRANSLATING should be defined if /translateInclude is being used.
+// "o.hpp" should still be treated as header-file.
+
+#define M_HEADER_FILE
+#include "m.hpp"
+#ifdef TRANSLATING
+#include "n.hpp"
+#else
+import "n.hpp";
+#endif
+
+inline int o = n + m + 5;
+)";
+
+ // x.hpp
+ const string xDotHpp = R"(
+#ifndef X_HPP
+#define X_HPP
+inline int x = 5;
+#endif
+)";
+
+ // y.hpp
+ const string yDotHpp = R"(
+#ifndef Y_HPP
+#define Y_HPP
+#include "x.hpp"
+inline int y = x + 5;
+#endif
+)";
+
+ // z.hpp
+ const string zDotHpp = R"(
+#ifndef Z_HPP
+#define Z_HPP
+#include "y.hpp"
+inline int z = x + y + 5;
+#endif
+)";
+
+ // big.hpp
+ const string bigDotHpp = R"(
+#include "x.hpp"
+// todo
+// following two should not be requested as big.hpp includes the following as well.
+#include "y.hpp"
+#include "z.hpp"
+)";
+
+ // foo.cpp
+ const string fooDotCpp = R"(
+module;
+#include "x.hpp"
+#include "z.hpp"
+#include "y.hpp"
+#include "big.hpp"
+export module Foo;
+import A;
+
+export void Foo()
+{
+ Hello();
+ World();
+ int s = x + y + z;
+}
+)";
+
+ ofstream("a.cpp") << aDotCpp;
+ ofstream("a-b.cpp") << aBDotCPP;
+ ofstream("a-c.cpp") << aCDotCPP;
+ ofstream("m.hpp") << mDotHpp;
+ ofstream("n.hpp") << nDotHpp;
+ ofstream("o.hpp") << oDotHpp;
+ ofstream("x.hpp") << xDotHpp;
+ ofstream("y.hpp") << yDotHpp;
+ ofstream("z.hpp") << zDotHpp;
+ ofstream("big.hpp") << bigDotHpp;
+ ofstream("foo.cpp") << fooDotCpp;
+ ofstream("main.cpp") << mainDotCpp;
+}
+
+tl::unexpected<string> errorReturn()
+{
+ return tl::unexpected<string>("IPC2978 Test Error: Wrong Message Received\n");
+}
+
+#define CHECK(condition) \
+ if (!(condition)) \
+ { \
+ return errorReturn(); \
+ }
+
+#define CREATE_BMI_MAPPING(varName, filePath) \
+ Mapping varName; \
+ if (const auto &_r_##varName = IPCManagerBS::createSharedMemoryBMIFile(filePath); _r_##varName) \
+ { \
+ varName = *_r_##varName; \
+ } \
+ else \
+ { \
+ return tl::unexpected("failed to created bmi mapping" + _r_##varName.error() + "\n"); \
+ }
+
+#define SEND_MESSAGE(message) \
+ if (const auto &_r_send_##message = manager.sendMessage(message); !_r_send_##message) \
+ { \
+ return tl::unexpected("manager send message failed" + _r_send_##message.error() + "\n"); \
+ }
+
+#define CLOSE_BMI_MAPPING(mapping) \
+ if (const auto &_r_close_##mapping = IPCManagerBS::closeBMIFileMapping(mapping); !_r_close_##mapping) \
+ { \
+ return tl::unexpected("closing bmi-mapping failed"); \
+ }
+
+tl::expected<void, string> runTest()
+{
+ setupTest();
+
+ string str = current_path().string();
+#ifdef _WIN32
+ for (char &c : str)
+ {
+ c = tolower(c);
+ }
+#endif
+
+ path curPath(str);
+
+ string mainFilePath = (curPath / "main .o").string();
+ string modFilePath = (curPath / "mod .pcm").string();
+ string mod1FilePath = (curPath / "mod1 .pcm").string();
+ string mod2FilePath = (curPath / "mod2 .pcm").string();
+ string aCObj = (curPath / "a-c .o").string();
+ string aCPcm = (curPath / "a-c .pcm").string();
+ string aBObj = (curPath / "a-b .o").string();
+ string aBPcm = (curPath / "a-b .pcm").string();
+ string aObj = (curPath / "a .o").string();
+ string aPcm = (curPath / "a .pcm").string();
+ string bObj = (curPath / "b .o").string();
+ string bPcm = (curPath / "b .pcm").string();
+ string mHpp = (curPath / "m.hpp").string();
+ string nHpp = (curPath / "n.hpp").string();
+ string oHpp = (curPath / "o.hpp").string();
+ string nPcm = (curPath / "n .pcm").string();
+ string oPcm = (curPath / "o .pcm").string();
+ string xHpp = (curPath / "x.hpp").string();
+ string yHpp = (curPath / "y.hpp").string();
+ string zHpp = (curPath / "z.hpp").string();
+ string bigHpp = (curPath / "big.hpp").string();
+ string bigPcm = (curPath / "big .pcm").string();
+ string fooPcm = (curPath / "foo .pcm").string();
+ string fooObj = (curPath / "foo .o").string();
+ string mainObj = (curPath / "main .o").string();
+
+ // compiling a-c.cpp
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + aCObj +
+ "\" -noScanIPC -c -xc++-module a-c.cpp -fmodule-output=\"" + aCPcm + "\"";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, false);
+ endCompilerTest();
+ }
+
+ // compiling a-b.cpp
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + aBObj +
+ "\" -noScanIPC -c -xc++-module a-b.cpp -fmodule-output=\"" + aBPcm + "\"";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, false);
+ endCompilerTest();
+ }
+ // compiling a.cpp
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + aObj +
+ "\" -noScanIPC -c -xc++-module a.cpp -fmodule-output=\"" + aPcm + "\"";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::MODULE)
+ const auto &ctbModule = reinterpret_cast<CTBModule &>(buffer);
+ CHECK(ctbModule.moduleName == "A:B")
+
+ BMIFile btcModBMI;
+ btcModBMI.filePath = aBPcm;
+ CREATE_BMI_MAPPING(btcModBmiMapping, btcModBMI)
+
+ BTCModule btcMod;
+ btcMod.requested = btcModBMI;
+
+ BMIFile modDepBMI;
+ modDepBMI.filePath = aCPcm;
+ CREATE_BMI_MAPPING(modDepBmiMapping, modDepBMI)
+
+ ModuleDep modDep;
+ modDep.file = modDepBMI;
+ modDep.logicalNames.emplace_back("A:C");
+ modDep.isHeaderUnit = false;
+ btcMod.modDeps.emplace_back(std::move(modDep));
+
+ SEND_MESSAGE(btcMod)
+
+ endCompilerTest();
+ CLOSE_BMI_MAPPING(btcModBmiMapping)
+ CLOSE_BMI_MAPPING(modDepBmiMapping)
+ }
+
+ // compiling n.hpp
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + nPcm +
+ "\" -noScanIPC -xc++-header n.hpp -DCOMMAND_MACRO";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::NON_MODULE)
+ const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(ctbNonModMHpp.logicalName == "m.hpp" || ctbNonModMHpp.isHeaderUnit == false)
+
+ BTCNonModule nonModMPcm;
+ nonModMPcm.isHeaderUnit = false;
+ nonModMPcm.filePath = mHpp;
+ SEND_MESSAGE(nonModMPcm)
+ endCompilerTest();
+ }
+
+ // compiling o.hpp
+ {
+ string compileCommand =
+ CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + oPcm + "\" -noScanIPC -xc++-header o.hpp";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::NON_MODULE)
+ const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(ctbNonModMHpp.logicalName == "m.hpp" || ctbNonModMHpp.isHeaderUnit == false)
+
+ BTCNonModule nonModMPcm;
+ nonModMPcm.isHeaderUnit = false;
+ nonModMPcm.filePath = mHpp;
+ SEND_MESSAGE(nonModMPcm)
+
+ readCompilerStdout(manager);
+ CHECK(type == CTB::NON_MODULE)
+ const auto &ctbNonModNHpp = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(ctbNonModNHpp.logicalName == "n.hpp" || ctbNonModNHpp.isHeaderUnit == true)
+
+ BTCNonModule nonModNPcm;
+ nonModNPcm.isHeaderUnit = true;
+
+ BMIFile nonModNPcmBmi;
+ nonModNPcmBmi.filePath = nPcm;
+
+ CREATE_BMI_MAPPING(nonModNPcmBmiMapping, nonModNPcmBmi)
+
+ nonModNPcm.filePath = nonModNPcmBmi.filePath;
+ nonModNPcm.fileSize = nonModNPcmBmi.fileSize;
+
+ SEND_MESSAGE(nonModNPcm)
+ endCompilerTest();
+ CLOSE_BMI_MAPPING(nonModNPcmBmiMapping)
+ }
+
+ // compiling o.hpp with include-translation. BTCNonModule for n.hpp will be received with
+ // isHeaderUnit = true.
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + oPcm +
+ "\" -noScanIPC -xc++-header o.hpp -DTRANSLATING";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::NON_MODULE)
+ const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(ctbNonModMHpp.logicalName == "m.hpp" || ctbNonModMHpp.isHeaderUnit == false)
+
+ BTCNonModule nonModMPcm;
+ nonModMPcm.isHeaderUnit = false;
+ nonModMPcm.filePath = mHpp;
+ SEND_MESSAGE(nonModMPcm)
+
+ readCompilerStdout(manager);
+ CHECK(type == CTB::NON_MODULE)
+ const auto &ctbNonModNHpp = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(ctbNonModNHpp.logicalName == "n.hpp" || ctbNonModNHpp.isHeaderUnit == false)
+
+ BTCNonModule nonModNPcm;
+
+ BMIFile nonModNPcmBmi;
+ nonModNPcmBmi.filePath = nPcm;
+
+ CREATE_BMI_MAPPING(nonModNPcmBmiMapping, nonModNPcmBmi)
+
+ nonModNPcm.isHeaderUnit = true;
+ nonModNPcm.filePath = nPcm;
+ nonModNPcm.fileSize = nonModNPcmBmi.fileSize;
+
+ SEND_MESSAGE(nonModNPcm)
+ endCompilerTest();
+ CLOSE_BMI_MAPPING(nonModNPcmBmiMapping)
+ }
+
+ // compiling big.hpp
+ {
+ string compileCommand =
+ CLANG_CMD R"( -std=c++20 -fmodule-header=user -o ")" + bigPcm + "\" -noScanIPC -xc++-header big.hpp";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::NON_MODULE)
+ const auto &ctbNonModMHpp = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(ctbNonModMHpp.logicalName == "x.hpp" || ctbNonModMHpp.isHeaderUnit == false)
+
+ BTCNonModule headerFile;
+ headerFile.isHeaderUnit = false;
+ headerFile.filePath = xHpp;
+ HeaderFile yHeaderFile;
+ yHeaderFile.logicalName = "y.hpp";
+ yHeaderFile.filePath = yHpp;
+ yHeaderFile.isSystem = true;
+ headerFile.headerFiles.emplace_back(yHeaderFile);
+ HeaderFile zHeaderFile;
+ zHeaderFile.logicalName = "z.hpp";
+ zHeaderFile.filePath = zHpp;
+ zHeaderFile.isSystem = true;
+ headerFile.headerFiles.emplace_back(zHeaderFile);
+
+ SEND_MESSAGE(headerFile)
+ endCompilerTest();
+ }
+
+ // compiling foo.cpp
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -fmodules-reduced-bmi -o ")" + fooObj +
+ "\" -noScanIPC -c -xc++-module foo.cpp -fmodule-output=\"" + fooPcm + "\"";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::NON_MODULE)
+ const auto &xHeader = reinterpret_cast<CTBNonModule &>(buffer);
+ CHECK(xHeader.logicalName == "x.hpp" || xHeader.isHeaderUnit == false)
+
+ BTCNonModule bigHu;
+ bigHu.isHeaderUnit = true;
+ bigHu.logicalNames.emplace_back("big.hpp");
+ bigHu.logicalNames.emplace_back("y.hpp");
+ bigHu.logicalNames.emplace_back("z.hpp");
+
+ BMIFile bigHuBmi;
+ bigHuBmi.filePath = bigPcm;
+
+ CREATE_BMI_MAPPING(bigHuBmiMapping, bigHuBmi)
+ bigHu.filePath = bigHuBmi.filePath;
+ bigHu.fileSize = bigHuBmi.fileSize;
+
+ SEND_MESSAGE(bigHu)
+
+ readCompilerStdout(manager);
+ CHECK(type == CTB::MODULE)
+ const auto &aModule = reinterpret_cast<CTBModule &>(buffer);
+ CHECK(aModule.moduleName == "A")
+
+ BMIFile requested;
+ requested.filePath = aPcm;
+ CREATE_BMI_MAPPING(aPcmMapping, requested)
+
+ BMIFile abModDepBmi;
+ abModDepBmi.filePath = aBPcm;
+ CREATE_BMI_MAPPING(aBPcmMapping, abModDepBmi)
+
+ BMIFile acModDepBmi;
+ acModDepBmi.filePath = aCPcm;
+ CREATE_BMI_MAPPING(aCPcmMapping, acModDepBmi)
+
+ BTCModule amod;
+ amod.requested = requested;
+ ModuleDep abModDep;
+ abModDep.isHeaderUnit = false;
+ abModDep.file = abModDepBmi;
+ abModDep.logicalNames.emplace_back("A:B");
+ amod.modDeps.emplace_back(std::move(abModDep));
+ ModuleDep acModDep;
+ acModDep.file = acModDepBmi;
+ acModDep.logicalNames.emplace_back("A:C");
+ amod.modDeps.emplace_back(std::move(acModDep));
+
+ SEND_MESSAGE(amod)
+ endCompilerTest();
+ CLOSE_BMI_MAPPING(bigHuBmiMapping)
+ CLOSE_BMI_MAPPING(aPcmMapping);
+ CLOSE_BMI_MAPPING(aBPcmMapping);
+ CLOSE_BMI_MAPPING(aCPcmMapping);
+ }
+
+ // compiling main.cpp
+ {
+ string compileCommand = CLANG_CMD R"( -std=c++20 -o ")" + mainObj + "\" -noScanIPC -c main.cpp";
+
+ IPCManagerBS manager = readFirstCompilerStdout(compileCommand, true);
+
+ CHECK(type == CTB::MODULE)
+ const auto &ctbModule = reinterpret_cast<CTBModule &>(buffer);
+ CHECK(ctbModule.moduleName == "Foo")
+
+ BMIFile requested;
+ requested.filePath = fooPcm;
+ CREATE_BMI_MAPPING(requestedMapping, requested)
+
+ BMIFile bigHuModDepBmi;
+ bigHuModDepBmi.filePath = bigPcm;
+ CREATE_BMI_MAPPING(bigHuModDepBmiMapping, bigHuModDepBmi)
+
+ BMIFile aModDepBmi;
+ aModDepBmi.filePath = aPcm;
+ CREATE_BMI_MAPPING(aPcmMapping, aModDepBmi)
+
+ BMIFile abModDepBmi;
+ abModDepBmi.filePath = aBPcm;
+ CREATE_BMI_MAPPING(aBPcmMapping, abModDepBmi)
+
+ BMIFile acModDepBmi;
+ acModDepBmi.filePath = aCPcm;
+ CREATE_BMI_MAPPING(aCPcmMapping, acModDepBmi)
+
+ BTCModule foo;
+ foo.requested = requested;
+
+ ModuleDep bigModDep;
+ bigModDep.isHeaderUnit = true;
+ bigModDep.file = bigHuModDepBmi;
+ bigModDep.logicalNames.emplace_back("big.hpp");
+ bigModDep.logicalNames.emplace_back("x.hpp");
+ bigModDep.logicalNames.emplace_back("y.hpp");
+ bigModDep.logicalNames.emplace_back("z.hpp");
+ foo.modDeps.emplace_back(std::move(bigModDep));
+
+ ModuleDep aModDep;
+ aModDep.isHeaderUnit = false;
+ aModDep.file = aModDepBmi;
+ aModDep.logicalNames.emplace_back("A");
+ foo.modDeps.emplace_back(std::move(aModDep));
+
+ ModuleDep bModDep;
+ bModDep.isHeaderUnit = false;
+ bModDep.file = abModDepBmi;
+ bModDep.logicalNames.emplace_back("A:B");
+ foo.modDeps.emplace_back(std::move(bModDep));
+
+ ModuleDep cModDep;
+ cModDep.isHeaderUnit = false;
+ cModDep.file = acModDepBmi;
+ cModDep.logicalNames.emplace_back("A:C");
+ foo.modDeps.emplace_back(std::move(cModDep));
+
+ SEND_MESSAGE(foo)
+
+ endCompilerTest();
+
+ CLOSE_BMI_MAPPING(requestedMapping);
+ CLOSE_BMI_MAPPING(bigHuModDepBmiMapping);
+ CLOSE_BMI_MAPPING(aPcmMapping);
+ CLOSE_BMI_MAPPING(aBPcmMapping);
+ CLOSE_BMI_MAPPING(aCPcmMapping);
+ }
+
+ fflush(stdout);
+ return {};
+}
+} // namespace
+
+#ifdef IS_THIS_CLANG_REPO
+TEST(IPC2978Test, IPC2978Test)
+{
+ const path p = current_path();
+ current_path(LLVM_TOOLS_BINARY_DIR);
+ const path mainFilePath = (LLVM_TOOLS_BINARY_DIR / path("main .o")).lexically_normal();
+ remove(mainFilePath);
+
+ const auto &r = runTest();
+ current_path(p);
+ if (!r)
+ {
+ FAIL() << r.error();
+ }
+ if (!exists(mainFilePath))
+ {
+ FAIL() << "main.o not found\n";
+ }
+}
+#else
+int main()
+{
+ remove(path("main .o"));
+ if (const auto &r = runTest(); !r)
+ {
+ std::cout << r.error() << std::endl;
+ return EXIT_FAILURE;
+ }
+ if (!exists(path("main .o")))
+ {
+ std::cout << "main.o not found" << std::endl;
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+#endif
More information about the cfe-commits
mailing list