From 607326ba3e2dfbeea641d56277285bfac12caf0e Mon Sep 17 00:00:00 2001 From: Hassan Sajjad Date: Wed, 9 Jul 2025 14:50:33 +0500 Subject: [PATCH] Added partial support for N2978. --- clang/include/clang/Driver/Options.td | 10 + .../include/clang/Frontend/CompilerInstance.h | 5 + .../clang/IPC2978/.clang-format-ignore | 1 + clang/include/clang/IPC2978/IPCManagerBS.hpp | 39 + .../clang/IPC2978/IPCManagerCompiler.hpp | 129 + clang/include/clang/IPC2978/Manager.hpp | 135 + clang/include/clang/IPC2978/Messages.hpp | 122 + clang/include/clang/IPC2978/expected.hpp | 2444 +++++++++++++++++ clang/include/clang/IPC2978/rapidhash.h | 574 ++++ clang/include/clang/Lex/HeaderSearchOptions.h | 5 + clang/lib/CMakeLists.txt | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 5 + clang/lib/Frontend/CMakeLists.txt | 1 + clang/lib/Frontend/CompilerInstance.cpp | 26 + clang/lib/Frontend/CompilerInvocation.cpp | 5 + clang/lib/IPC2978/.clang-format-ignore | 1 + clang/lib/IPC2978/CMakeLists.txt | 6 + clang/lib/IPC2978/IPCManagerBS.cpp | 352 +++ clang/lib/IPC2978/IPCManagerCompiler.cpp | 347 +++ clang/lib/IPC2978/Manager.cpp | 444 +++ clang/lib/IPC2978/setup.py | 58 + clang/unittests/CMakeLists.txt | 7 +- clang/unittests/IPC2978/.clang-format-ignore | 1 + clang/unittests/IPC2978/CMakeLists.txt | 5 + clang/unittests/IPC2978/IPC2978Test.cpp | 320 +++ 25 files changed, 5042 insertions(+), 1 deletion(-) create mode 100644 clang/include/clang/IPC2978/.clang-format-ignore create mode 100644 clang/include/clang/IPC2978/IPCManagerBS.hpp create mode 100644 clang/include/clang/IPC2978/IPCManagerCompiler.hpp create mode 100644 clang/include/clang/IPC2978/Manager.hpp create mode 100644 clang/include/clang/IPC2978/Messages.hpp create mode 100644 clang/include/clang/IPC2978/expected.hpp create mode 100644 clang/include/clang/IPC2978/rapidhash.h create mode 100644 clang/lib/IPC2978/.clang-format-ignore create mode 100644 clang/lib/IPC2978/CMakeLists.txt create mode 100644 clang/lib/IPC2978/IPCManagerBS.cpp create mode 100644 clang/lib/IPC2978/IPCManagerCompiler.cpp create mode 100644 clang/lib/IPC2978/Manager.cpp create mode 100644 clang/lib/IPC2978/setup.py create mode 100644 clang/unittests/IPC2978/.clang-format-ignore create mode 100644 clang/unittests/IPC2978/CMakeLists.txt create mode 100644 clang/unittests/IPC2978/IPC2978Test.cpp diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 77379f1130149..6a6010b4baa44 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -9416,3 +9416,13 @@ def wasm_opt : Flag<["--"], "wasm-opt">, Group, HelpText<"Enable the wasm-opt optimizer (default)">, MarshallingInfoNegativeFlag>; + +def no_scan_ipc : Flag<["-"], "noScanIPC">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable No scan IPC approach">; +def translate_include : Flag<["-"], "translateInclude">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Consider header includes as header-units">; +def find_include : Flag<["-"], "findIncludes">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"In No Scan IPC approach, compiler will relay the finding includes responsibility to the build-system">; diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 0ae490f0e8073..84a524a549e02 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -16,6 +16,7 @@ #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" #include "clang/Frontend/Utils.h" +#include "clang/IPC2978/IPCManagerCompiler.hpp" #include "clang/Lex/DependencyDirectivesScanner.h" #include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/ModuleLoader.h" @@ -180,6 +181,10 @@ class CompilerInstance : public ModuleLoader { /// The stream for verbose output. raw_ostream *VerboseOutputStream = &llvm::errs(); + /// Pointer for managing communication with build-system if noScan flag is + /// set. + N2978::IPCManagerCompiler *ipcManager = nullptr; + /// Holds information about the output file. /// /// If TempFilename is not empty we must rename it to Filename at the end. diff --git a/clang/include/clang/IPC2978/.clang-format-ignore b/clang/include/clang/IPC2978/.clang-format-ignore new file mode 100644 index 0000000000000..f59ec20aabf58 --- /dev/null +++ b/clang/include/clang/IPC2978/.clang-format-ignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/clang/include/clang/IPC2978/IPCManagerBS.hpp b/clang/include/clang/IPC2978/IPCManagerBS.hpp new file mode 100644 index 0000000000000..f9222a55f3fc4 --- /dev/null +++ b/clang/include/clang/IPC2978/IPCManagerBS.hpp @@ -0,0 +1,39 @@ + +#ifndef IPC_MANAGER_BS_HPP +#define IPC_MANAGER_BS_HPP + +#include "clang/IPC2978/Manager.hpp" +#include "clang/IPC2978/Messages.hpp" + +namespace N2978 +{ + +// IPC Manager BuildSystem +class IPCManagerBS : Manager +{ + friend tl::expected makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath); + bool connectedToCompiler = false; + +#ifdef _WIN32 + explicit IPCManagerBS(void *hPipe_); +#else + explicit IPCManagerBS(int fdSocket_); +#endif + + public: + IPCManagerBS(const IPCManagerBS &) = default; + IPCManagerBS &operator=(const IPCManagerBS &) = default; + IPCManagerBS(IPCManagerBS &&) = default; + IPCManagerBS &operator=(IPCManagerBS &&) = default; + tl::expected receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const; + [[nodiscard]] tl::expected sendMessage(const BTCModule &moduleFile) const; + [[nodiscard]] tl::expected sendMessage(const BTCNonModule &nonModule) const; + [[nodiscard]] tl::expected sendMessage(const BTCLastMessage &lastMessage) const; + static tl::expected createSharedMemoryBMIFile(const BMIFile &bmiFile); + static tl::expected closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile); + void closeConnection() const; +}; + +tl::expected makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath); +} // namespace N2978 +#endif // IPC_MANAGER_BS_HPP diff --git a/clang/include/clang/IPC2978/IPCManagerCompiler.hpp b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp new file mode 100644 index 0000000000000..6aba39822d5ac --- /dev/null +++ b/clang/include/clang/IPC2978/IPCManagerCompiler.hpp @@ -0,0 +1,129 @@ + +#ifndef IPC_MANAGER_COMPILER_HPP +#define IPC_MANAGER_COMPILER_HPP + +#include "clang/IPC2978/Manager.hpp" +#include "clang/IPC2978/expected.hpp" + +using std::string_view; +namespace N2978 +{ + +// IPC Manager Compiler +class IPCManagerCompiler : Manager +{ + template tl::expected receiveMessage() const; + // This is not exposed. sendCTBLastMessage calls this. + [[nodiscard]] tl::expected receiveBTCLastMessage() const; + + public: +#ifdef _WIN32 + explicit IPCManagerCompiler(void *hPipe_); +#else + explicit IPCManagerCompiler(int fdSocket_); +#endif + [[nodiscard]] tl::expected receiveBTCModule(const CTBModule &moduleName) const; + [[nodiscard]] tl::expected receiveBTCNonModule(const CTBNonModule &nonModule) const; + [[nodiscard]] tl::expected sendCTBLastMessage(const CTBLastMessage &lastMessage) const; + [[nodiscard]] tl::expected sendCTBLastMessage(const CTBLastMessage &lastMessage, + const string &bmiFile, const string &filePath) const; + static tl::expected readSharedMemoryBMIFile(const BMIFile &file); + static tl::expected closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile); + void closeConnection() const; +}; + +template tl::expected IPCManagerCompiler::receiveMessage() const +{ + // Read from the pipe. + char buffer[BUFFERSIZE]; + uint32_t bytesRead; + if (const auto &r = readInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + else + { + bytesRead = *r; + } + + uint32_t bytesProcessed = 0; + + if constexpr (std::is_same_v) + { + const auto &r = readProcessMappingOfBMIFileFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + const auto &r2 = readVectorOfModuleDepFromPipe(buffer, bytesRead, bytesProcessed); + if (!r2) + { + return tl::unexpected(r2.error()); + } + + BTCModule moduleFile; + moduleFile.requested = *r; + moduleFile.deps = *r2; + if (bytesRead == bytesProcessed) + { + return moduleFile; + } + } + else if constexpr (std::is_same_v) + { + const auto &r = readBoolFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r2) + { + return tl::unexpected(r2.error()); + } + + const auto &r3 = readBoolFromPipe(buffer, bytesRead, bytesProcessed); + if (!r3) + { + return tl::unexpected(r3.error()); + } + + const auto &r4 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!r4) + { + return tl::unexpected(r4.error()); + } + + const auto &r5 = readVectorOfHuDepFromPipe(buffer, bytesRead, bytesProcessed); + if (!r5) + { + return tl::unexpected(r5.error()); + } + + BTCNonModule nonModule; + nonModule.isHeaderUnit = *r; + nonModule.filePath = *r2; + nonModule.angled = *r3; + nonModule.fileSize = *r4; + nonModule.deps = *r5; + + if (bytesRead == bytesProcessed) + { + return nonModule; + } + } + else + { + static_assert(false && "Unknown type\n"); + } + + if (bytesRead != bytesProcessed) + { + return tl::unexpected(getErrorString(bytesRead, bytesProcessed)); + } +} +[[nodiscard]] tl::expected makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath); +} // namespace N2978 +#endif // IPC_MANAGER_COMPILER_HPP diff --git a/clang/include/clang/IPC2978/Manager.hpp b/clang/include/clang/IPC2978/Manager.hpp new file mode 100644 index 0000000000000..580e1878b3ef3 --- /dev/null +++ b/clang/include/clang/IPC2978/Manager.hpp @@ -0,0 +1,135 @@ + +#ifndef MANAGER_HPP +#define MANAGER_HPP + +#include "clang/IPC2978/Messages.hpp" +#include "clang/IPC2978/expected.hpp" + +#include +#include + +using std::string, std::vector, std::string_view; + +#define BUFFERSIZE 4096 + +#ifdef _WIN32 +// The following variable is used in CreateNamedFunction. +#define PIPE_TIMEOUT 5000 +#endif + +namespace tl +{ +template class expected; +} + +namespace N2978 +{ + +enum class ErrorCategory : uint8_t +{ + NONE, + + // error-category for API errors + READ_FILE_ZERO_BYTES_READ, + INCORRECT_BTC_LAST_MESSAGE, + UNKNOWN_CTB_TYPE, +}; + +string getErrorString(); +string getErrorString(uint32_t bytesRead_, uint32_t bytesProcessed_); +string getErrorString(ErrorCategory errorCategory_); +// to facilitate error propagation. +inline string getErrorString(string err) +{ + return err; +} + +struct ProcessMappingOfBMIFile +{ + string_view file; +#ifdef _WIN32 + void *mapping; + void *view; +#else + void *mapping; + uint32_t mappingSize; +#endif +}; + +class Manager +{ + public: +#ifdef _WIN32 + void *hPipe = nullptr; +#else + int fdSocket = 0; +#endif + + tl::expected readInternal(char (&buffer)[BUFFERSIZE]) const; + tl::expected writeInternal(const vector &buffer) const; + + static vector getBufferWithType(CTB type); + static void writeUInt32(vector &buffer, uint32_t value); + static void writeString(vector &buffer, const string &str); + static void writeProcessMappingOfBMIFile(vector &buffer, const BMIFile &file); + static void writeModuleDep(vector &buffer, const ModuleDep &dep); + static void writeHuDep(vector &buffer, const HuDep &dep); + static void writeVectorOfStrings(vector &buffer, const vector &strs); + static void writeVectorOfProcessMappingOfBMIFiles(vector &buffer, const vector &files); + static void writeVectorOfModuleDep(vector &buffer, const vector &deps); + static void writeVectorOfHuDep(vector &buffer, const vector &deps); + + tl::expected readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected readUInt32FromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected readProcessMappingOfBMIFileFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected, string> readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected readModuleDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected, string> readVectorOfModuleDepFromPipe(char (&buffer)[BUFFERSIZE], + uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected readHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected, string> readVectorOfHuDepFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const; + tl::expected readNumberOfBytes(char *output, uint32_t size, char (&buffer)[BUFFERSIZE], + uint32_t &bytesRead, uint32_t &bytesProcessed) const; +}; + +template constexpr T *construct_at(T *p, Args &&...args) +{ + return ::new (static_cast(p)) T(std::forward(args)...); +} + +template T &getInitializedObjectFromBuffer(char (&buffer)[320]) +{ + T &t = reinterpret_cast(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(v >> ((7 - i) * 8)); + // high nibble: + out[2 * i] = lut[byte >> 4]; + // low nibble: + out[2 * i + 1] = lut[byte & 0xF]; + } + return out; +} + +} // namespace N2978 +#endif // MANAGER_HPP diff --git a/clang/include/clang/IPC2978/Messages.hpp b/clang/include/clang/IPC2978/Messages.hpp new file mode 100644 index 0000000000000..c575e2b3fe09d --- /dev/null +++ b/clang/include/clang/IPC2978/Messages.hpp @@ -0,0 +1,122 @@ +#ifndef MESSAGES_HPP +#define MESSAGES_HPP + +#include +#include +#include +#include + +using std::string, std::vector; + +namespace N2978 +{ + +// CTB --> Compiler to Build-System +// BTC --> Build-System to Compiler + +// string is 4 bytes that hold the size of the char array, followed by the array. +// vector is 4 bytes that hold the size of the array, followed by the array. +// All fields are sent in declaration order, even if meaningless. + +// Compiler to Build System +// This is the first byte of the compiler to build-system message. +enum class CTB : uint8_t +{ + MODULE = 0, + NON_MODULE = 1, + LAST_MESSAGE = 2, +}; + +// This is sent when the compiler needs a module. +struct CTBModule +{ + string moduleName; +}; + +// This is sent when the compiler needs something else than a module. +// isHeaderUnit is set when the compiler knows that it is a header-unit. +// If findInclude flag is provided, then the compiler sends logicalName, +// Otherwise the compiler sends the full path. +struct CTBNonModule +{ + bool isHeaderUnit = false; + string str; +}; + +// This is the last message sent by the compiler. +struct CTBLastMessage +{ + // Whether the compilation succeeded or failed. + bool exitStatus = false; + // Following fields are meaningless if the compilation failed. + // header-includes discovered during compilation. + vector headerFiles; + // compiler output + string output; + // compiler error output. + // Any IPC related error output should be reported on stderr. + string errorOutput; + // exported module name if any. + string logicalName; + // This is communicated because the receiving process has no + // way to learn the shared memory file size on both Windows + // and Linux without a filesystem call. + // Meaningless if the file compiled is not a module interface unit + // or a header-unit. + uint32_t fileSize = UINT32_MAX; +}; + +// Build System to Compiler +// Unlike CTB, this is not written as the first byte +// since the compiler knows what message it will receive. +enum class BTC : uint8_t +{ + MODULE = 0, + NON_MODULE = 1, + LAST_MESSAGE = 2, +}; + +struct BMIFile +{ + string filePath; + uint32_t fileSize = UINT32_MAX; +}; + +struct ModuleDep +{ + BMIFile file; + string logicalName; +}; + +// Reply for CTBModule +struct BTCModule +{ + BMIFile requested; + vector deps; +}; + +struct HuDep +{ + BMIFile file; + string logicalName; + bool angled = false; +}; + +// Reply for CTBNonModule +struct BTCNonModule +{ + bool isHeaderUnit = false; + string filePath; + // if isHeaderUnit == false, the following three are meaning-less. + bool angled = false; + // if isHeaderUnit == true, fileSize of the requested file. + uint32_t fileSize; + vector deps; +}; + +// Reply for CTBLastMessage if the compilation succeeded. +struct BTCLastMessage +{ +}; +} // namespace N2978 +#endif // MESSAGES_HPP diff --git a/clang/include/clang/IPC2978/expected.hpp b/clang/include/clang/IPC2978/expected.hpp new file mode 100644 index 0000000000000..1f92b6b3cd85a --- /dev/null +++ b/clang/include/clang/IPC2978/expected.hpp @@ -0,0 +1,2444 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@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 +// . +/// + +#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 +#include +#include +#include + +#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 +#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 +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// 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 + +// 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 +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#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 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 unexpected { +public: + static_assert(!std::is_same::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 ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(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 unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(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 using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, 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 +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : 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::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template 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 tag swap(T &, T &); +template 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 std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // 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 ::value, + bool = std::is_trivially_destructible::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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected 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 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected 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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(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 ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + 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 ::value && + std::is_nothrow_move_constructible::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(); + 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 ::value && + !std::is_nothrow_move_constructible::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(); + +#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 ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#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(); + 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(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(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 &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template 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 void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(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 >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::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::value) + : expected_copy_base(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 >::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 { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::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 , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::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::value + &&std::is_nothrow_move_assignable::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 ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::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 +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 = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + 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 +struct expected_delete_ctor_base { + 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 ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::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 +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 = delete; +}; + +template +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 &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +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 &) = 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 ::value || std::is_void::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 struct expected_default_ctor_base { + 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 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` 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 expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::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 *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(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 ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(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::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::type{}, + typename std::is_nothrow_move_constructible::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 + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::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::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 ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::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 ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(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 constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif \ No newline at end of file diff --git a/clang/include/clang/IPC2978/rapidhash.h b/clang/include/clang/IPC2978/rapidhash.h new file mode 100644 index 0000000000000..06ee8220f97d2 --- /dev/null +++ b/clang/include/clang/IPC2978/rapidhash.h @@ -0,0 +1,574 @@ +/* + * rapidhash V3 - Very fast, high quality, platform-independent hashing algorithm. + * + * Based on 'wyhash', by Wang Yi + * + * 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 + #include + #if defined(_MSC_VER) + # include + # 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>32)+(rm1>>32)+c; + #ifdef RAPIDHASH_PROTECTED + *A^=lo; *B^=hi; + #else + *A=lo; *B=hi; + #endif + #endif + } + + /* + * Multiply and xor mix function. + * + * @param A 64-bit number. + * @param B 64-bit number. + * + * Calculates 128-bit C = A * B. + * Returns 64-bit xor between high and low 64 bits of C. + */ + RAPIDHASH_INLINE_CONSTEXPR uint64_t rapid_mix(uint64_t A, uint64_t B) RAPIDHASH_NOEXCEPT { rapid_mum(&A,&B); return A^B; } + + /* + * Read functions. + */ + #ifdef RAPIDHASH_LITTLE_ENDIAN + RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return v;} + RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return v;} + #elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return __builtin_bswap64(v);} + RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return __builtin_bswap32(v);} + #elif defined(_MSC_VER) + RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return _byteswap_uint64(v);} + RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return _byteswap_ulong(v);} + #else + RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { + uint64_t v; memcpy(&v, p, 8); + return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000)); + } + RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { + uint32_t v; memcpy(&v, p, 4); + return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000)); + } + #endif + + /* + * rapidhash main function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * @param seed 64-bit seed used to alter the hash result predictably. + * @param secret Triplet of 64-bit secrets used to alter hash result predictably. + * + * Returns a 64-bit hash. + */ +RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT { + const uint8_t *p=(const uint8_t *)key; + seed ^= rapid_mix(seed ^ secret[2], secret[1]); + uint64_t a=0, b=0; + size_t i = len; + if (_likely_(len <= 16)) { + if (len >= 4) { + seed ^= len; + if (len >= 8) { + const uint8_t* plast = p + len - 8; + a = rapid_read64(p); + b = rapid_read64(plast); + } else { + const uint8_t* plast = p + len - 4; + a = rapid_read32(p); + b = rapid_read32(plast); + } + } else if (len > 0) { + a = (((uint64_t)p[0])<<45)|p[len-1]; + b = p[len>>1]; + } else + a = b = 0; + } else { + uint64_t see1 = seed, see2 = seed; + uint64_t see3 = seed, see4 = seed; + uint64_t see5 = seed, see6 = seed; +#ifdef RAPIDHASH_COMPACT + if (i > 112) { + do { + seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); + see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); + see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); + see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); + see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); + see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5); + see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6); + p += 112; + i -= 112; + } while(i > 112); + seed ^= see1; + see2 ^= see3; + see4 ^= see5; + seed ^= see6; + see2 ^= see4; + seed ^= see2; + } +#else + if (i > 224) { + do { + seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); + see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); + see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); + see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); + see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); + see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5); + see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6); + seed = rapid_mix(rapid_read64(p + 112) ^ secret[0], rapid_read64(p + 120) ^ seed); + see1 = rapid_mix(rapid_read64(p + 128) ^ secret[1], rapid_read64(p + 136) ^ see1); + see2 = rapid_mix(rapid_read64(p + 144) ^ secret[2], rapid_read64(p + 152) ^ see2); + see3 = rapid_mix(rapid_read64(p + 160) ^ secret[3], rapid_read64(p + 168) ^ see3); + see4 = rapid_mix(rapid_read64(p + 176) ^ secret[4], rapid_read64(p + 184) ^ see4); + see5 = rapid_mix(rapid_read64(p + 192) ^ secret[5], rapid_read64(p + 200) ^ see5); + see6 = rapid_mix(rapid_read64(p + 208) ^ secret[6], rapid_read64(p + 216) ^ see6); + p += 224; + i -= 224; + } while (i > 224); + } + if (i > 112) { + seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); + see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); + see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); + see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); + see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); + see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5); + see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6); + p += 112; + i -= 112; + } + seed ^= see1; + see2 ^= see3; + see4 ^= see5; + seed ^= see6; + see2 ^= see4; + seed ^= see2; +#endif + if (i > 16) { + seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed); + if (i > 32) { + seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed); + if (i > 48) { + seed = rapid_mix(rapid_read64(p + 32) ^ secret[1], rapid_read64(p + 40) ^ seed); + if (i > 64) { + seed = rapid_mix(rapid_read64(p + 48) ^ secret[1], rapid_read64(p + 56) ^ seed); + if (i > 80) { + seed = rapid_mix(rapid_read64(p + 64) ^ secret[2], rapid_read64(p + 72) ^ seed); + if (i > 96) { + seed = rapid_mix(rapid_read64(p + 80) ^ secret[1], rapid_read64(p + 88) ^ seed); + } + } + } + } + } + } + a=rapid_read64(p+i-16) ^ i; b=rapid_read64(p+i-8); + } + a ^= secret[1]; + b ^= seed; + rapid_mum(&a, &b); + return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i); +} + + /* + * rapidhashMicro main function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * @param seed 64-bit seed used to alter the hash result predictably. + * @param secret Triplet of 64-bit secrets used to alter hash result predictably. + * + * Returns a 64-bit hash. + */ + RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT { + const uint8_t *p=(const uint8_t *)key; + seed ^= rapid_mix(seed ^ secret[2], secret[1]); + uint64_t a=0, b=0; + size_t i = len; + if (_likely_(len <= 16)) { + if (len >= 4) { + seed ^= len; + if (len >= 8) { + const uint8_t* plast = p + len - 8; + a = rapid_read64(p); + b = rapid_read64(plast); + } else { + const uint8_t* plast = p + len - 4; + a = rapid_read32(p); + b = rapid_read32(plast); + } + } else if (len > 0) { + a = (((uint64_t)p[0])<<45)|p[len-1]; + b = p[len>>1]; + } else + a = b = 0; + } else { + if (i > 80) { + uint64_t see1 = seed, see2 = seed; + uint64_t see3 = seed, see4 = seed; + do { + seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); + see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); + see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); + see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); + see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); + p += 80; + i -= 80; + } while(i > 80); + seed ^= see1; + see2 ^= see3; + seed ^= see4; + seed ^= see2; + } + if (i > 16) { + seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed); + if (i > 32) { + seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed); + if (i > 48) { + seed = rapid_mix(rapid_read64(p + 32) ^ secret[1], rapid_read64(p + 40) ^ seed); + if (i > 64) { + seed = rapid_mix(rapid_read64(p + 48) ^ secret[1], rapid_read64(p + 56) ^ seed); + } + } + } + } + a=rapid_read64(p+i-16) ^ i; b=rapid_read64(p+i-8); + } + a ^= secret[1]; + b ^= seed; + rapid_mum(&a, &b); + return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i); + } + + /* + * rapidhashNano main function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * @param seed 64-bit seed used to alter the hash result predictably. + * @param secret Triplet of 64-bit secrets used to alter hash result predictably. + * + * Returns a 64-bit hash. + */ + RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT { + const uint8_t *p=(const uint8_t *)key; + seed ^= rapid_mix(seed ^ secret[2], secret[1]); + uint64_t a=0, b=0; + size_t i = len; + if (_likely_(len <= 16)) { + if (len >= 4) { + seed ^= len; + if (len >= 8) { + const uint8_t* plast = p + len - 8; + a = rapid_read64(p); + b = rapid_read64(plast); + } else { + const uint8_t* plast = p + len - 4; + a = rapid_read32(p); + b = rapid_read32(plast); + } + } else if (len > 0) { + a = (((uint64_t)p[0])<<45)|p[len-1]; + b = p[len>>1]; + } else + a = b = 0; + } else { + if (i > 48) { + uint64_t see1 = seed, see2 = seed; + do { + seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); + see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); + see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); + p += 48; + i -= 48; + } while(i > 48); + seed ^= see1; + seed ^= see2; + } + if (i > 16) { + seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed); + if (i > 32) { + seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed); + } + } + a=rapid_read64(p+i-16) ^ i; b=rapid_read64(p+i-8); + } + a ^= secret[1]; + b ^= seed; + rapid_mum(&a, &b); + return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i); + } + +/* + * rapidhash seeded hash function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * @param seed 64-bit seed used to alter the hash result predictably. + * + * Calls rapidhash_internal using provided parameters and default secrets. + * + * Returns a 64-bit hash. + */ +RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT { + return rapidhash_internal(key, len, seed, rapid_secret); +} + +/* + * rapidhash general purpose hash function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * + * Calls rapidhash_withSeed using provided parameters and the default seed. + * + * Returns a 64-bit hash. + */ +RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash(const void *key, size_t len) RAPIDHASH_NOEXCEPT { + return rapidhash_withSeed(key, len, 0); +} + +/* + * rapidhashMicro seeded hash function. + * + * Designed for HPC and server applications, where cache misses make a noticeable performance detriment. + * Clang-18+ compiles it to ~140 instructions without stack usage, both on x86-64 and aarch64. + * Faster for sizes up to 512 bytes, just 15%-20% slower for inputs above 1kb. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * @param seed 64-bit seed used to alter the hash result predictably. + * + * Calls rapidhash_internal using provided parameters and default secrets. + * + * Returns a 64-bit hash. + */ + RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT { + return rapidhashMicro_internal(key, len, seed, rapid_secret); +} + +/* + * rapidhashMicro hash function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * + * Calls rapidhash_withSeed using provided parameters and the default seed. + * + * Returns a 64-bit hash. + */ +RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro(const void *key, size_t len) RAPIDHASH_NOEXCEPT { + return rapidhashMicro_withSeed(key, len, 0); +} + +/* + * rapidhashNano seeded hash function. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * @param seed 64-bit seed used to alter the hash result predictably. + * + * Calls rapidhash_internal using provided parameters and default secrets. + * + * Returns a 64-bit hash. + */ + RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT { + return rapidhashNano_internal(key, len, seed, rapid_secret); +} + +/* + * rapidhashNano hash function. + * + * Designed for Mobile and embedded applications, where keeping a small code size is a top priority. + * Clang-18+ compiles it to less than 100 instructions without stack usage, both on x86-64 and aarch64. + * The fastest for sizes up to 48 bytes, but may be considerably slower for larger inputs. + * + * @param key Buffer to be hashed. + * @param len @key length, in bytes. + * + * Calls rapidhash_withSeed using provided parameters and the default seed. + * + * Returns a 64-bit hash. + */ +RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano(const void *key, size_t len) RAPIDHASH_NOEXCEPT { + return rapidhashNano_withSeed(key, len, 0); +} \ No newline at end of file diff --git a/clang/include/clang/Lex/HeaderSearchOptions.h b/clang/include/clang/Lex/HeaderSearchOptions.h index 2f33c0749f02a..5370639b44ab5 100644 --- a/clang/include/clang/Lex/HeaderSearchOptions.h +++ b/clang/include/clang/Lex/HeaderSearchOptions.h @@ -277,6 +277,11 @@ class HeaderSearchOptions { LLVM_PREFERRED_TYPE(bool) unsigned ModulesIncludeVFSUsage : 1; + /// Whether to coordinate with build-system for building of C++20 Modules \ + /// and header-units. + LLVM_PREFERRED_TYPE(bool) + unsigned NoScanIPC : 1; + /// Whether we should look for a module in module maps only in provided /// header search paths or if we are allowed to look for module maps in /// subdirectories of provided paths too. diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 4f2218b583e41..73da7e0973574 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(DirectoryWatcher) add_subdirectory(Index) add_subdirectory(IndexSerialization) add_subdirectory(InstallAPI) +add_subdirectory(IPC2978) add_subdirectory(StaticAnalyzer) add_subdirectory(Format) if(CLANG_INCLUDE_TESTS) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 0dbe46319247b..3ce724b14b085 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4118,6 +4118,11 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D, } } + if (Args.hasArg(options::OPT_no_scan_ipc)) { + CmdArgs.push_back("-noScanIPC"); + Args.claimAllArgs(options::OPT_no_scan_ipc); + } + // Noop if we see '-fmodules-reduced-bmi' with other translation // units than module units. This is more user friendly to allow end uers to // enable this feature without asking for help from build systems. diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index a916667208845..626bf06bd6efe 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -53,6 +53,7 @@ add_clang_library(clangFrontend clangBasic clangDriver clangEdit + clangIPC2978 clangLex clangParse clangSema diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 09a66b652518f..1ffe9a5c75511 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1879,6 +1879,32 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST( if (M) checkConfigMacros(getPreprocessor(), M, ImportLoc); + if (getHeaderSearchOpts().NoScanIPC) { + if (!ipcManager) { + if (const auto &r = N2978::makeIPCManagerCompiler(OutputFiles.begin()->Filename); r) { + ipcManager = new N2978::IPCManagerCompiler(r.value()); + } + } + N2978::CTBModule mod; + mod.moduleName = ModuleName; + if (const auto &r = ipcManager->receiveBTCModule(std::move(mod)); r) { + auto &[requested, deps] = r.value(); + + // in case of noScanIPC, PrebuiltModuleFiles is empty, so we receive it + // from the build-system here, so the selectModuleSource() call + // later-on will return ModuleSource::MS_PrebuiltModulePath. + auto &PrebuiltModuleFiles = const_cast> &>(HS.getHeaderSearchOpts().PrebuiltModuleFiles); + PrebuiltModuleFiles.emplace(std::move(ModuleName) ,std::move(requested.filePath)); + for (const auto &[file, logicalName] : deps) { + PrebuiltModuleFiles.emplace(std::move(logicalName) ,std::move(file.filePath)); + } + } + else { + string errorMessage = r.error(); + ModuleName = errorMessage; + } + } + // Select the source and filename for loading the named module. std::string ModuleFilename; ModuleSource Source = diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index b9f75796ecc16..b14a792840a6a 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3389,6 +3389,9 @@ static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts, for (const std::string &F : Opts.VFSOverlayFiles) GenerateArg(Consumer, OPT_ivfsoverlay, F); + + if (Opts.NoScanIPC) + GenerateArg(Consumer, OPT_no_scan_ipc); } static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, @@ -3521,6 +3524,8 @@ static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, for (const auto *A : Args.filtered(OPT_ivfsoverlay, OPT_vfsoverlay)) Opts.AddVFSOverlayFile(A->getValue()); + Opts.NoScanIPC = Args.hasArg(OPT_no_scan_ipc); + return Diags.getNumErrors() == NumErrorsBefore; } diff --git a/clang/lib/IPC2978/.clang-format-ignore b/clang/lib/IPC2978/.clang-format-ignore new file mode 100644 index 0000000000000..f59ec20aabf58 --- /dev/null +++ b/clang/lib/IPC2978/.clang-format-ignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/clang/lib/IPC2978/CMakeLists.txt b/clang/lib/IPC2978/CMakeLists.txt new file mode 100644 index 0000000000000..1ea37f9c9983a --- /dev/null +++ b/clang/lib/IPC2978/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_clang_library(clangIPC2978 STATIC + Manager.cpp + IPCManagerBS.cpp + IPCManagerCompiler.cpp +) \ No newline at end of file diff --git a/clang/lib/IPC2978/IPCManagerBS.cpp b/clang/lib/IPC2978/IPCManagerBS.cpp new file mode 100644 index 0000000000000..2281cd7adca2f --- /dev/null +++ b/clang/lib/IPC2978/IPCManagerBS.cpp @@ -0,0 +1,352 @@ +#include "clang/IPC2978/IPCManagerBS.hpp" +#include "clang/IPC2978/Manager.hpp" +#include "clang/IPC2978/Messages.hpp" +#include "clang/IPC2978/expected.hpp" +#include +#include + +#ifdef _WIN32 +#include +#else +#include "clang/IPC2978/rapidhash.h" +#include +#include +#include +#include +#include +#endif +using std::string; + +namespace N2978 +{ + +tl::expected makeIPCManagerBS(string BMIIfHeaderUnitObjOtherwisePath) +{ +#ifdef _WIN32 + BMIIfHeaderUnitObjOtherwisePath = R"(\\.\pipe\)" + BMIIfHeaderUnitObjOtherwisePath; + void *hPipe = CreateNamedPipeA(BMIIfHeaderUnitObjOtherwisePath.c_str(), // pipe name + PIPE_ACCESS_DUPLEX | // read/write access + FILE_FLAG_FIRST_PIPE_INSTANCE, // overlapped mode + PIPE_TYPE_MESSAGE | // message-type pipe + PIPE_READMODE_MESSAGE | // message read mode + PIPE_WAIT, // blocking mode + 1, // unlimited instances + BUFFERSIZE * sizeof(TCHAR), // output buffer size + BUFFERSIZE * sizeof(TCHAR), // input buffer size + PIPE_TIMEOUT, // client time-out + nullptr); // default security attributes + if (hPipe == INVALID_HANDLE_VALUE) + { + return tl::unexpected(getErrorString()); + } + return IPCManagerBS(hPipe); + +#else + + // Named Pipes are used but Unix Domain sockets could have been used as well. The tradeoff is that a file is created + // and there needs to be bind, listen, accept calls which means that an extra fd is created is temporarily on the + // server side. it can be closed immediately after. + + const int fdSocket = socket(AF_UNIX, SOCK_STREAM, 0); + + // Create server socket + if (fdSocket == -1) + { + return tl::unexpected(getErrorString()); + } + + // Prepare address structure + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + // We use file hash to make a file path smaller, since there is a limit of NAME_MAX that is generally 108 bytes. + // TODO + // Have an option to receive this path in constructor to make it compatible with Android and IOS. + string prependDir = "/tmp/"; + const uint64_t hash = rapidhash(BMIIfHeaderUnitObjOtherwisePath.c_str(), BMIIfHeaderUnitObjOtherwisePath.size()); + prependDir.append(to16charHexString(hash)); + std::copy(prependDir.begin(), prependDir.end(), addr.sun_path); + + // Remove any existing socket + unlink(prependDir.c_str()); + + // Bind socket to the file system path + if (bind(fdSocket, reinterpret_cast(&addr), sizeof(addr)) == -1) + { + return tl::unexpected(getErrorString()); + } + if (chmod(prependDir.c_str(), 0666) == -1) + { + return tl::unexpected(getErrorString()); + } + + // Listen for incoming connections + if (listen(fdSocket, 1) == -1) + { + close(fdSocket); + return tl::unexpected(getErrorString()); + } + + return IPCManagerBS(fdSocket); +#endif +} + +#ifdef _WIN32 +IPCManagerBS::IPCManagerBS(void *hPipe_) +{ + hPipe = hPipe_; +} +#else +IPCManagerBS::IPCManagerBS(const int fdSocket_) +{ + fdSocket = fdSocket_; +} +#endif + +bool checked = false; +tl::expected IPCManagerBS::receiveMessage(char (&ctbBuffer)[320], CTB &messageType) const +{ + if (!connectedToCompiler) + { +#ifdef _WIN32 + if (!ConnectNamedPipe(hPipe, nullptr)) + { + // Is the client already connected? + if (GetLastError() != ERROR_PIPE_CONNECTED) + { + return tl::unexpected(getErrorString()); + } + } +#else + const int fd = accept(fdSocket, nullptr, nullptr); + close(fdSocket); + if (fd == -1) + { + return tl::unexpected(getErrorString()); + } + if (checked) + { + bool breakpoint = true; + } + checked = true; + const_cast(fdSocket) = fd; +#endif + const_cast(connectedToCompiler) = true; + } + // raise(SIGTRAP); // At the location of the BP. + + // Read from the pipe. + char buffer[BUFFERSIZE]; + uint32_t bytesRead; + if (const auto &r = readInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + else + { + bytesRead = *r; + } + + uint32_t bytesProcessed = 1; + + // read call fails if zero byte is read, so safe to process 1 byte + switch (static_cast(buffer[0])) + { + + case CTB::MODULE: { + + const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + messageType = CTB::MODULE; + getInitializedObjectFromBuffer(ctbBuffer).moduleName = *r; + } + + break; + + case CTB::NON_MODULE: { + + const auto &r = readBoolFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r2) + { + return tl::unexpected(r.error()); + } + + messageType = CTB::NON_MODULE; + auto &[isHeaderUnit, str] = getInitializedObjectFromBuffer(ctbBuffer); + isHeaderUnit = *r; + str = *r2; + } + + break; + + case CTB::LAST_MESSAGE: { + + const auto &exitStatusExpected = readBoolFromPipe(buffer, bytesRead, bytesProcessed); + if (!exitStatusExpected) + { + return tl::unexpected(exitStatusExpected.error()); + } + + const auto &headerFilesExpected = readVectorOfStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!headerFilesExpected) + { + return tl::unexpected(headerFilesExpected.error()); + } + + const auto &outputExpected = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!outputExpected) + { + return tl::unexpected(outputExpected.error()); + } + + const auto &errorOutputExpected = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!errorOutputExpected) + { + return tl::unexpected(errorOutputExpected.error()); + } + + const auto &logicalNameExpected = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!logicalNameExpected) + { + return tl::unexpected(logicalNameExpected.error()); + } + + const auto &fileSizeExpected = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!fileSizeExpected) + { + return tl::unexpected(fileSizeExpected.error()); + } + + messageType = CTB::LAST_MESSAGE; + + auto &[exitStatus, headerFiles, output, errorOutput, logicalName, fileSize] = + getInitializedObjectFromBuffer(ctbBuffer); + + exitStatus = *exitStatusExpected; + headerFiles = *headerFilesExpected; + output = *outputExpected; + errorOutput = *errorOutputExpected; + logicalName = *logicalNameExpected; + fileSize = *fileSizeExpected; + } + break; + + default: + + return tl::unexpected(getErrorString(ErrorCategory::UNKNOWN_CTB_TYPE)); + } + + if (bytesRead != bytesProcessed) + { + return tl::unexpected(getErrorString(bytesRead, bytesProcessed)); + } + + return {}; +} + +tl::expected IPCManagerBS::sendMessage(const BTCModule &moduleFile) const +{ + vector buffer; + writeProcessMappingOfBMIFile(buffer, moduleFile.requested); + writeVectorOfModuleDep(buffer, moduleFile.deps); + if (const auto &r = writeInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + return {}; +} + +tl::expected IPCManagerBS::sendMessage(const BTCNonModule &nonModule) const +{ + vector buffer; + buffer.emplace_back(nonModule.isHeaderUnit); + writeString(buffer, nonModule.filePath); + buffer.emplace_back(nonModule.angled); + writeUInt32(buffer, nonModule.fileSize); + writeVectorOfHuDep(buffer, nonModule.deps); + if (const auto &r = writeInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + return {}; +} + +tl::expected IPCManagerBS::sendMessage(const BTCLastMessage &) const +{ + vector buffer; + buffer.emplace_back(true); + if (const auto &r = writeInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + return {}; +} + +tl::expected IPCManagerBS::createSharedMemoryBMIFile(const BMIFile &bmiFile) +{ + ProcessMappingOfBMIFile sharedFile{}; +#ifdef _WIN32 + // 1) Open the existing file‐mapping object (must have been created by another process) + sharedFile.mapping = OpenFileMappingA(FILE_MAP_READ, // read‐only access + FALSE, // do not inherit handle + bmiFile.filePath.c_str() // name of mapping + ); + + if (sharedFile.mapping == nullptr) + { + return tl::unexpected(getErrorString()); + } + + return sharedFile; +#else + const int fd = open(bmiFile.filePath.data(), O_RDONLY); + if (fd == -1) + { + return tl::unexpected(getErrorString()); + } + sharedFile.mapping = mmap(nullptr, bmiFile.fileSize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); + if (close(fd) == -1) + { + return tl::unexpected(getErrorString()); + } + if (sharedFile.mapping == MAP_FAILED) + { + return tl::unexpected(getErrorString()); + } + sharedFile.mappingSize = bmiFile.fileSize; + return sharedFile; +#endif +} + +tl::expected IPCManagerBS::closeBMIFileMapping(const ProcessMappingOfBMIFile &processMappingOfBMIFile) +{ +#ifdef _WIN32 + CloseHandle(processMappingOfBMIFile.mapping); +#else + if (munmap(processMappingOfBMIFile.mapping, processMappingOfBMIFile.mappingSize) == -1) + { + return tl::unexpected(getErrorString()); + } +#endif + return {}; +} + +void IPCManagerBS::closeConnection() const +{ +#ifdef _WIN32 + CloseHandle(hPipe); +#else + close(fdSocket); +#endif +} + +} // namespace N2978 \ No newline at end of file diff --git a/clang/lib/IPC2978/IPCManagerCompiler.cpp b/clang/lib/IPC2978/IPCManagerCompiler.cpp new file mode 100644 index 0000000000000..44bdf66e944bd --- /dev/null +++ b/clang/lib/IPC2978/IPCManagerCompiler.cpp @@ -0,0 +1,347 @@ + +#include "clang/IPC2978/IPCManagerCompiler.hpp" +#include "clang/IPC2978/Manager.hpp" +#include "clang/IPC2978/Messages.hpp" +#include "clang/IPC2978/rapidhash.h" + +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +using std::string; + +namespace N2978 +{ + +tl::expected makeIPCManagerCompiler(string BMIIfHeaderUnitObjOtherwisePath) +{ +#ifdef _WIN32 + BMIIfHeaderUnitObjOtherwisePath = R"(\\.\pipe\)" + BMIIfHeaderUnitObjOtherwisePath; + HANDLE hPipe = CreateFileA(BMIIfHeaderUnitObjOtherwisePath.data(), // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + nullptr, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + nullptr); // no template file + + // Break if the pipe handle is valid. + + if (hPipe == INVALID_HANDLE_VALUE) + { + return tl::unexpected(getErrorString()); + } + + return IPCManagerCompiler(hPipe); +#else + + const int fdSocket = socket(AF_UNIX, SOCK_STREAM, 0); + + if (fdSocket == -1) + { + return tl::unexpected(getErrorString()); + } + + // Prepare address structure + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + + // We use file hash to make a file path smaller, since there is a limit of NAME_MAX that is generally 108 bytes. + // TODO + // Have an option to receive this path in constructor to make it compatible with Android and IOS. + string prependDir = "/tmp/"; + const uint64_t hash = rapidhash(BMIIfHeaderUnitObjOtherwisePath.c_str(), BMIIfHeaderUnitObjOtherwisePath.size()); + prependDir.append(to16charHexString(hash)); + std::copy(prependDir.begin(), prependDir.end(), addr.sun_path); + + if (connect(fdSocket, reinterpret_cast(&addr), sizeof(addr)) == -1) + { + return tl::unexpected(getErrorString()); + } + return IPCManagerCompiler(fdSocket); + +#endif +} + +#ifdef _WIN32 +IPCManagerCompiler::IPCManagerCompiler(void *hPipe_) +{ + hPipe = hPipe_; +} +#else +IPCManagerCompiler::IPCManagerCompiler(const int fdSocket_) +{ + fdSocket = fdSocket_; +} +#endif + +tl::expected IPCManagerCompiler::receiveBTCLastMessage() const +{ + char buffer[BUFFERSIZE]; + uint32_t bytesRead; + if (const auto &r = readInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + else + { + bytesRead = *r; + } + + if (buffer[0] != static_cast(true)) + { + return tl::unexpected(getErrorString(ErrorCategory::INCORRECT_BTC_LAST_MESSAGE)); + } + + if (constexpr uint32_t bytesProcessed = 1; bytesRead != bytesProcessed) + { + return tl::unexpected(getErrorString(bytesRead, bytesProcessed)); + } + + return {}; +} + +tl::expected IPCManagerCompiler::receiveBTCModule(const CTBModule &moduleName) const +{ + + // raise(SIGTRAP); // At the location of the BP. + vector buffer = getBufferWithType(CTB::MODULE); + writeString(buffer, moduleName.moduleName); + if (const auto &r = writeInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + + return receiveMessage(); +} + +tl::expected IPCManagerCompiler::receiveBTCNonModule(const CTBNonModule &nonModule) const +{ + vector buffer = getBufferWithType(CTB::NON_MODULE); + buffer.emplace_back(nonModule.isHeaderUnit); + writeString(buffer, nonModule.str); + if (const auto &r = writeInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + return receiveMessage(); +} + +tl::expected IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage) const +{ + vector buffer = getBufferWithType(CTB::LAST_MESSAGE); + buffer.emplace_back(lastMessage.exitStatus); + writeVectorOfStrings(buffer, lastMessage.headerFiles); + writeString(buffer, lastMessage.output); + writeString(buffer, lastMessage.errorOutput); + writeString(buffer, lastMessage.logicalName); + writeUInt32(buffer, lastMessage.fileSize); + if (const auto &r = writeInternal(buffer); !r) + { + return tl::unexpected(r.error()); + } + return {}; +} + +tl::expected IPCManagerCompiler::sendCTBLastMessage(const CTBLastMessage &lastMessage, + const string &bmiFile, const string &filePath) const +{ +#ifdef _WIN32 + const HANDLE hFile = CreateFileA(filePath.c_str(), GENERIC_READ | GENERIC_WRITE, + 0, // no sharing during setup + nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + { + return tl::unexpected(getErrorString()); + } + + LARGE_INTEGER fileSize; + fileSize.QuadPart = bmiFile.size(); + // 3) Create a RW mapping of that file: + const HANDLE hMap = + CreateFileMappingA(hFile, nullptr, PAGE_READWRITE, fileSize.HighPart, fileSize.LowPart, filePath.c_str()); + if (!hMap) + { + CloseHandle(hFile); + return tl::unexpected(getErrorString()); + } + + void *pView = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, bmiFile.size()); + if (!pView) + { + CloseHandle(hFile); + CloseHandle(hMap); + return tl::unexpected(getErrorString()); + } + + memcpy(pView, bmiFile.c_str(), bmiFile.size()); + + if (!FlushViewOfFile(pView, bmiFile.size())) + { + UnmapViewOfFile(pView); + CloseHandle(hFile); + CloseHandle(hMap); + return tl::unexpected(getErrorString()); + } + + UnmapViewOfFile(pView); + CloseHandle(hFile); + + if (const auto &r = sendCTBLastMessage(lastMessage); !r) + { + return tl::unexpected(r.error()); + } + + if (lastMessage.exitStatus == EXIT_SUCCESS) + { + if (const auto &r = receiveBTCLastMessage(); !r) + { + return tl::unexpected(r.error()); + } + } + + CloseHandle(hMap); +#else + + const uint64_t fileSize = bmiFile.size(); + // 1. Open & size + const int fd = open(filePath.c_str(), O_RDWR | O_CREAT, 0666); + if (fd == -1) + { + return tl::unexpected(getErrorString()); + } + if (ftruncate(fd, fileSize) == -1) + { + return tl::unexpected(getErrorString()); + } + + // 2. Map for write + void *mapping = mmap(nullptr, fileSize, PROT_WRITE, MAP_SHARED, fd, 0); + if (mapping == MAP_FAILED) + { + return tl::unexpected(getErrorString()); + } + + // 3. We no longer need the FD + close(fd); + + memcpy(mapping, bmiFile.data(), bmiFile.size()); + + // 4. Flush to disk synchronously + if (msync(mapping, fileSize, MS_SYNC) == -1) + { + return tl::unexpected(getErrorString()); + } + + if (const auto &r = sendCTBLastMessage(lastMessage); !r) + { + return tl::unexpected(r.error()); + } + + if (lastMessage.exitStatus == EXIT_SUCCESS) + { + if (const auto &r = receiveBTCLastMessage(); !r) + { + return tl::unexpected(r.error()); + } + } + + munmap(mapping, fileSize); +#endif + + return {}; +} + +tl::expected IPCManagerCompiler::readSharedMemoryBMIFile(const BMIFile &file) +{ + ProcessMappingOfBMIFile f{}; +#ifdef _WIN32 + // 1) Open the existing file‐mapping object (must have been created by another process) + const HANDLE mapping = OpenFileMappingA(FILE_MAP_READ, // read‐only access + FALSE, // do not inherit a handle + file.filePath.data() // name of mapping + ); + + if (mapping == nullptr) + { + return tl::unexpected(getErrorString()); + } + + // 2) Map a view of the file into our address space + const LPVOID view = MapViewOfFile(mapping, // handle to mapping object + FILE_MAP_READ, // read‐only view + 0, // file offset high + 0, // file offset low + file.fileSize // number of bytes to map (0 maps the whole file) + ); + + if (view == nullptr) + { + CloseHandle(mapping); + return tl::unexpected(getErrorString()); + } + + f.mapping = mapping; + f.view = view; + f.file = {static_cast(view), file.fileSize}; +#else + const int fd = open(file.filePath.data(), O_RDONLY); + if (fd == -1) + { + return tl::unexpected(getErrorString()); + } + void *mapping = mmap(nullptr, file.fileSize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); + + if (close(fd) == -1) + { + return tl::unexpected(getErrorString()); + } + + if (mapping == MAP_FAILED) + { + return tl::unexpected(getErrorString()); + } + + f.mapping = mapping; + f.mappingSize = file.fileSize; + f.file = {static_cast(mapping), file.fileSize}; +#endif + return f; +} + +tl::expected IPCManagerCompiler::closeBMIFileMapping( + const ProcessMappingOfBMIFile &processMappingOfBMIFile) +{ +#ifdef _WIN32 + UnmapViewOfFile(processMappingOfBMIFile.view); + CloseHandle(processMappingOfBMIFile.mapping); +#else + if (munmap(processMappingOfBMIFile.mapping, processMappingOfBMIFile.mappingSize) == -1) + { + return tl::unexpected(getErrorString()); + } +#endif + return {}; +} + +void IPCManagerCompiler::closeConnection() const +{ +#ifdef _WIN32 + CloseHandle(hPipe); +#else + close(fdSocket); +#endif +} + +} // namespace N2978 \ No newline at end of file diff --git a/clang/lib/IPC2978/Manager.cpp b/clang/lib/IPC2978/Manager.cpp new file mode 100644 index 0000000000000..412909f323ecf --- /dev/null +++ b/clang/lib/IPC2978/Manager.cpp @@ -0,0 +1,444 @@ + +#include "clang/IPC2978/Manager.hpp" +#include "clang/IPC2978/Messages.hpp" +#include "clang/IPC2978/expected.hpp" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +namespace N2978 +{ + +string getErrorString() +{ +#ifdef _WIN32 + const DWORD err = GetLastError(); + + char *msg_buf; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, + err, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), reinterpret_cast(&msg_buf), 0, nullptr); + + if (msg_buf == nullptr) + { + char fallback_msg[128] = {}; + snprintf(fallback_msg, sizeof(fallback_msg), "GetLastError() = %d", err); + return fallback_msg; + } + + string msg = msg_buf; + LocalFree(msg_buf); + return msg; +#else + return {std::strerror(errno)}; +#endif +} + +string getErrorString(const uint32_t bytesRead_, const uint32_t bytesProcessed_) +{ + return "Error: Bytes Readd vs Bytes Processed Mismatch.\nBytes Read: " + std::to_string(bytesRead_) + + ", Bytes Processed: " + std::to_string(bytesProcessed_); +} + +string getErrorString(const ErrorCategory errorCategory_) +{ + string errorString; + + switch (errorCategory_) + { + case ErrorCategory::READ_FILE_ZERO_BYTES_READ: + errorString = "Error: ReadFile Zero Bytes Read."; + break; + case ErrorCategory::INCORRECT_BTC_LAST_MESSAGE: + errorString = "Error: Incorrect BTC Last Message."; + break; + case ErrorCategory::UNKNOWN_CTB_TYPE: + errorString = "Error: Unknown CTB message received."; + break; + } + + return errorString; +} + +tl::expected Manager::readInternal(char (&buffer)[BUFFERSIZE]) const +{ + uint32_t bytesRead; + +#ifdef _WIN32 + const bool success = ReadFile(hPipe, // pipe handle + buffer, // buffer to receive reply + BUFFERSIZE, // size of buffer + LPDWORD(&bytesRead), // number of bytes read + nullptr); // not overlapped + + if (const uint32_t lastError = GetLastError(); !success && lastError != ERROR_MORE_DATA) + { + return tl::unexpected(getErrorString()); + } + +#else + bytesRead = read(fdSocket, buffer, BUFFERSIZE); + if (bytesRead == -1) + { + return tl::unexpected(getErrorString()); + } +#endif + + if (!bytesRead) + { + return tl::unexpected(getErrorString(ErrorCategory::READ_FILE_ZERO_BYTES_READ)); + } + + return bytesRead; +} + +#ifndef _WIN32 +tl::expected writeAll(const int fd, const char *buffer, const uint32_t count) +{ + uint32_t bytesWritten = 0; + + while (bytesWritten != count) + { + const uint32_t result = write(fd, buffer + bytesWritten, count - bytesWritten); + if (result == -1) + { + if (errno == EINTR) + { + // Interrupted by signal: retry + continue; + } + return tl::unexpected(getErrorString()); + } + if (result == 0) + { + // According to POSIX, write() returning 0 is only valid for count == 0 + return tl::unexpected(getErrorString()); + } + bytesWritten += result; + } + + return {}; +} +#endif + +tl::expected Manager::writeInternal(const vector &buffer) const +{ +#ifdef _WIN32 + const bool success = WriteFile(hPipe, // pipe handle + buffer.data(), // message + buffer.size(), // message length + nullptr, // bytes written + nullptr); // not overlapped + if (!success) + { + return tl::unexpected(getErrorString()); + } +#else + if (const auto &r = writeAll(fdSocket, buffer.data(), buffer.size()); !r) + { + return tl::unexpected(r.error()); + } +#endif + return {}; +} + +vector Manager::getBufferWithType(CTB type) +{ + vector buffer; + buffer.emplace_back(static_cast(type)); + return buffer; +} + +void Manager::writeUInt32(vector &buffer, const uint32_t value) +{ + const auto ptr = reinterpret_cast(&value); + buffer.insert(buffer.end(), ptr, ptr + 4); +} + +void Manager::writeString(vector &buffer, const string &str) +{ + writeUInt32(buffer, str.size()); + buffer.insert(buffer.end(), str.begin(), str.end()); // Insert all characters +} + +void Manager::writeProcessMappingOfBMIFile(vector &buffer, const BMIFile &file) +{ + writeString(buffer, file.filePath); + writeUInt32(buffer, file.fileSize); +} + +void Manager::writeModuleDep(vector &buffer, const ModuleDep &dep) +{ + writeProcessMappingOfBMIFile(buffer, dep.file); + writeString(buffer, dep.logicalName); +} + +void Manager::writeHuDep(vector &buffer, const HuDep &dep) +{ + writeProcessMappingOfBMIFile(buffer, dep.file); + writeString(buffer, dep.logicalName); + buffer.emplace_back(dep.angled); +} + +void Manager::writeVectorOfStrings(vector &buffer, const vector &strs) +{ + writeUInt32(buffer, strs.size()); + for (const string &str : strs) + { + writeString(buffer, str); + } +} + +void Manager::writeVectorOfProcessMappingOfBMIFiles(vector &buffer, const vector &files) +{ + writeUInt32(buffer, files.size()); + for (const BMIFile &file : files) + { + writeProcessMappingOfBMIFile(buffer, file); + } +} + +void Manager::writeVectorOfModuleDep(vector &buffer, const vector &deps) +{ + writeUInt32(buffer, deps.size()); + for (const ModuleDep &dep : deps) + { + writeModuleDep(buffer, dep); + } +} + +void Manager::writeVectorOfHuDep(vector &buffer, const vector &deps) +{ + writeUInt32(buffer, deps.size()); + for (const HuDep &dep : deps) + { + writeHuDep(buffer, dep); + } +} + +tl::expected Manager::readBoolFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + bool result; + const auto &r = + readNumberOfBytes(reinterpret_cast(&result), sizeof(result), buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + return result; +} + +tl::expected Manager::readUInt32FromPipe(char (&buffer)[4096], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + uint32_t size; + if (const auto &r = readNumberOfBytes(reinterpret_cast(&size), 4, buffer, bytesRead, bytesProcessed); !r) + { + return tl::unexpected(r.error()); + } + return size; +} + +tl::expected Manager::readStringFromPipe(char (&buffer)[BUFFERSIZE], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + auto r = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + const uint32_t stringSize = *r; + string str(stringSize, 'a'); + if (const auto &r2 = readNumberOfBytes(str.data(), stringSize, buffer, bytesRead, bytesProcessed); !r2) + { + return tl::unexpected(r2.error()); + } + return str; +} + +tl::expected Manager::readProcessMappingOfBMIFileFromPipe(char (&buffer)[4096], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + const auto &r2 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + + BMIFile file; + file.filePath = *r; + file.fileSize = *r2; + return file; +} + +tl::expected, string> Manager::readVectorOfStringFromPipe(char (&buffer)[BUFFERSIZE], + uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + const auto &r = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + const uint32_t vectorSize = *r; + vector vec; + vec.reserve(vectorSize); + for (uint32_t i = 0; i < vectorSize; ++i) + { + const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r2) + { + return tl::unexpected(r2.error()); + } + vec.emplace_back(*r2); + } + return vec; +} + +tl::expected Manager::readModuleDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + const auto &r = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + const auto &r2 = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!r2) + { + return tl::unexpected(r2.error()); + } + + const auto &r3 = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r3) + { + return tl::unexpected(r3.error()); + } + + ModuleDep modDep; + + modDep.file.filePath = *r; + modDep.file.fileSize = *r2; + modDep.logicalName = *r3; + + return modDep; +} + +tl::expected, string> Manager::readVectorOfModuleDepFromPipe(char (&buffer)[4096], + uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!vectorSize) + { + return tl::unexpected(vectorSize.error()); + } + + vector vec; + vec.reserve(*vectorSize); + for (uint32_t i = 0; i < *vectorSize; ++i) + { + const auto &r = readModuleDepFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + vec.emplace_back(*r); + } + return vec; +} + +tl::expected Manager::readHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + const auto &r = readProcessMappingOfBMIFileFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + + const auto &r2 = readStringFromPipe(buffer, bytesRead, bytesProcessed); + if (!r2) + { + return tl::unexpected(r2.error()); + } + + const auto &r3 = readBoolFromPipe(buffer, bytesRead, bytesProcessed); + if (!r3) + { + return tl::unexpected(r3.error()); + } + + HuDep huDep; + huDep.file = *r; + huDep.logicalName = *r2; + huDep.angled = *r3; + return huDep; +} + +tl::expected, string> Manager::readVectorOfHuDepFromPipe(char (&buffer)[4096], uint32_t &bytesRead, + uint32_t &bytesProcessed) const +{ + const auto &vectorSize = readUInt32FromPipe(buffer, bytesRead, bytesProcessed); + if (!vectorSize) + { + return tl::unexpected(vectorSize.error()); + } + + vector vec; + vec.reserve(*vectorSize); + for (uint32_t i = 0; i < *vectorSize; ++i) + { + const auto &r = readHuDepFromPipe(buffer, bytesRead, bytesProcessed); + if (!r) + { + return tl::unexpected(r.error()); + } + vec.emplace_back(*r); + } + return vec; +} + +tl::expected Manager::readNumberOfBytes(char *output, const uint32_t size, char (&buffer)[BUFFERSIZE], + uint32_t &bytesRead, uint32_t &bytesProcessed) const +{ + uint32_t pendingSize = size; + uint32_t offset = 0; + while (true) + { + const uint32_t bytesAvailable = bytesRead - bytesProcessed; + if (bytesAvailable >= pendingSize) + { + memcpy(output + offset, buffer + bytesProcessed, pendingSize); + bytesProcessed += pendingSize; + break; + } + + if (bytesAvailable) + { + memcpy(output + offset, buffer + bytesProcessed, bytesAvailable); + offset += bytesAvailable; + pendingSize -= bytesAvailable; + } + + bytesProcessed = 0; + if (const auto &r = readInternal(buffer); r) + { + bytesRead = *r; + } + else + { + return tl::unexpected(r.error()); + } + } + return {}; +} +} // namespace N2978 \ No newline at end of file diff --git a/clang/lib/IPC2978/setup.py b/clang/lib/IPC2978/setup.py new file mode 100644 index 0000000000000..b1a99fd4f14e3 --- /dev/null +++ b/clang/lib/IPC2978/setup.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +import os +import shutil + +# The directory where this script resides (lib/clang/IPC2978) +source_dir = os.path.abspath(os.path.dirname(__file__)) +# Assumed that IPC2978 repo is present in parallel with llvm repo. +copy_from = os.path.abspath(source_dir + ('../' * 5) + 'ipc2978api/') + os.sep +# The directory for the header-files +include_dir = os.path.abspath(os.path.join(source_dir, '../../include/clang/IPC2978')) + os.sep +# The directory for the unit-tests. +ipc_test_source_file = os.path.abspath(os.path.join(source_dir, '../../unittests/IPC2978/IPC2978Test.cpp')) + +shutil.copytree(copy_from + 'include', include_dir, dirs_exist_ok=True) +shutil.copytree(copy_from + 'src', source_dir, dirs_exist_ok=True) +# We'll process files in both include and source directories +roots = [source_dir, include_dir] + +# Gather all header filenames in the include directory (top-level only) +include_files = [f for f in os.listdir(include_dir) if f.endswith(".hpp")] + +files = [] + +# Iterate through the source and include directories +for root in roots: + # Skipping the CMakeLists.txt and .py files + files.extend([os.path.join(root, x) for x in os.listdir(root) if + not os.path.join(root, x).endswith(".txt") and not os.path.join(root, x).endswith(".py")]) + +for file in files: + out_lines = [] + with open(file, 'r', encoding='utf-8') as f: + lines = f.readlines() + # Examine each line for an include directive + for line in lines: + if line.startswith('#include "'): + out_lines.append(line.replace('#include "', '#include "clang/IPC2978/', 1)) + else: + out_lines.append(line) + with open(file, 'w', encoding='utf-8') as f: + for line in out_lines: + f.writelines(line) + +shutil.copy(copy_from + "/tests/ClangTest.cpp", ipc_test_source_file) + +# Modifying the copied ClangTest.cpp file +out_lines = [] +with open(ipc_test_source_file, 'r', encoding='utf-8') as f: + lines = f.readlines() + # Examine each line for an include directive + for line in lines: + if line.startswith('// #define IS_THIS_CLANG_REPO'): + out_lines.append('#define IS_THIS_CLANG_REPO') + else: + out_lines.append(line) +with open(ipc_test_source_file, 'w', encoding='utf-8') as f: + for line in out_lines: + f.writelines(line) diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt index 54c781a35c20c..631f881d745e1 100644 --- a/clang/unittests/CMakeLists.txt +++ b/clang/unittests/CMakeLists.txt @@ -92,12 +92,13 @@ add_subdirectory(Frontend) add_subdirectory(Rewrite) add_subdirectory(Sema) add_subdirectory(CodeGen) +add_subdirectory(IPC2978) if(HAVE_CLANG_REPL_SUPPORT) add_subdirectory(Interpreter) endif() # FIXME: libclang unit tests are disabled on Windows due # to failures, mostly in libclang.VirtualFileOverlay_*. -if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) +if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) add_subdirectory(libclang) endif() add_subdirectory(DirectoryWatcher) @@ -126,6 +127,10 @@ add_distinct_clang_unittest(AllClangUnitTests ${LLVM_COMPONENTS} ) +# Needed for the IPC2978Test +target_compile_definitions(AllClangUnitTests PRIVATE LLVM_TOOLS_BINARY_DIR="${LLVM_TOOLS_BINARY_DIR}") +add_dependencies(AllClangUnitTests clang) + # The Tooling library has some internal headers. Make those work. If we like # the merged clang unit test binary, we can update the include paths and make # this the default. diff --git a/clang/unittests/IPC2978/.clang-format-ignore b/clang/unittests/IPC2978/.clang-format-ignore new file mode 100644 index 0000000000000..f59ec20aabf58 --- /dev/null +++ b/clang/unittests/IPC2978/.clang-format-ignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/clang/unittests/IPC2978/CMakeLists.txt b/clang/unittests/IPC2978/CMakeLists.txt new file mode 100644 index 0000000000000..5f0a50a13e62d --- /dev/null +++ b/clang/unittests/IPC2978/CMakeLists.txt @@ -0,0 +1,5 @@ +add_clang_unittest(IPC2978Test + IPC2978Test.cpp + CLANG_LIBS + clangIPC2978 +) \ No newline at end of file diff --git a/clang/unittests/IPC2978/IPC2978Test.cpp b/clang/unittests/IPC2978/IPC2978Test.cpp new file mode 100644 index 0000000000000..0c0399121bf3f --- /dev/null +++ b/clang/unittests/IPC2978/IPC2978Test.cpp @@ -0,0 +1,320 @@ +//===- unittests/IPC2978/IPC2978Test.cpp - Tests IPC2978 Support -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// The following line is uncommented by clang/lib/IPC2978/setup.py for clang/unittests/IPC2978/IPC2978.cpp + +#define IS_THIS_CLANG_REPO +#ifdef IS_THIS_CLANG_REPO +#include "clang/IPC2978/IPCManagerBS.hpp" +#include "gtest/gtest.h" +template void printMessage(const T &, bool) +{ +} +#else +#include "IPCManagerBS.hpp" +#include "Testing.hpp" +#include "fmt/printf.h" +#endif + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +using namespace std::filesystem; +using namespace N2978; +using namespace std; + +#ifdef _WIN32 +#define CLANG_CMD ".\\clang.exe" +#else +#define CLANG_CMD "./clang" +#endif + +namespace +{ +#ifdef _WIN32 +PROCESS_INFORMATION pi; +tl::expected Run(const string &command) +{ + STARTUPINFOA si = {sizeof(si)}; + if (!CreateProcessA(nullptr, // lpApplicationName + const_cast(command.c_str()), // lpCommandLine + nullptr, // lpProcessAttributes + nullptr, // lpThreadAttributes + FALSE, // bInheritHandles + 0, // dwCreationFlags + nullptr, // lpEnvironment + nullptr, // lpCurrentDirectory + &si, // lpStartupInfo + &pi // lpProcessInformation + )) + { + return tl::unexpected("CreateProcess" + getErrorString()); + } + return {}; +} +#else + +int procStatus; +int procId; +/// Start a process and gather its raw output. Returns its exit code. +/// Crashes (calls Fatal()) on error. +tl::expected Run(const string &command) +{ + if (procId = fork(); procId == -1) + { + return tl::unexpected("fork" + getErrorString()); + } + if (procId == 0) + { + // Child process + exit(WEXITSTATUS(system(command.c_str()))); + } + return {}; +} +#endif + +[[nodiscard]] tl::expected setupTest() +{ + + const string mod = R"(export module mod; +import mod2; + +export void func() +{ + EmptyClass2 a; + func2(); + // does nothing +} + +export struct EmptyClass +{ +};)"; + + const string mod1 = R"(module; + +export module mod1; +import mod2;)"; + + const string mod2 = R"(export module mod2; + +export void func2() +{ + // does nothing +} + +export struct EmptyClass2 +{ +};)"; + + const string main = R"(import mod; +import mod1; + +int main() +{ + func(); + EmptyClass a; +})"; + + ofstream("mod.cppm") << mod; + ofstream("mod1.cppm") << mod1; + ofstream("mod2.cppm") << mod2; + ofstream("main.cpp") << main; + + // compile main.cpp which imports B.cpp which imports A.cpp. + + if (system(CLANG_CMD + R"( -std=c++20 mod2.cppm -c -fmodule-output="mod2 .pcm" -fmodules-reduced-bmi -o "mod2 .o")") != + EXIT_SUCCESS) + { + return tl::unexpected("could not run the first command\n"); + } + + if (system( + CLANG_CMD + R"( -std=c++20 mod.cppm -c -fmodule-output="mod .pcm" -fmodules-reduced-bmi -o "mod .o" -fmodule-file=mod2="./mod2 .pcm")") != + EXIT_SUCCESS) + { + return tl::unexpected("could not run the second command\n"); + } + + if (system( + CLANG_CMD + R"( -std=c++20 mod1.cppm -c -fmodule-output="mod1 .pcm" -fmodules-reduced-bmi -o "mod1 .o" -fmodule-file=mod2="./mod2 .pcm")") != + EXIT_SUCCESS) + { + tl::unexpected("could not run the second command\n"); + } + return {}; +} + +tl::expected runTest() +{ + + if (const auto &r = setupTest(); !r) + { + return tl::unexpected(r.error()); + } + + string current = current_path().generic_string() + '/'; + string mainFilePath = current + "main .o"; + string modFilePath = current + "mod .pcm"; + string mod1FilePath = current + "mod1 .pcm"; + string mod2FilePath = current + "mod2 .pcm"; + + if (const auto &r = makeIPCManagerBS(std::move(mainFilePath)); r) + { + uint8_t messageCount = 0; + const IPCManagerBS &manager = *r; + + string objFile = (current_path() / "main .o").generic_string() + "\""; + string compileCommand = CLANG_CMD R"( -std=c++20 -c main.cpp -noScanIPC -o ")" + objFile; + if (const auto &r2 = Run(compileCommand); !r2) + { + return tl::unexpected(r2.error()); + } + + while (true) + { + CTB type; + char buffer[320]; + if (const auto &r2 = manager.receiveMessage(buffer, type); !r2) + { + string str = r2.error(); + return tl::unexpected("manager receive message failed" + r2.error() + "\n"); + } + + switch (type) + { + + case CTB::MODULE: { + const auto &ctbModule = reinterpret_cast(buffer); + printMessage(ctbModule, false); + + if (!messageCount) + { + ++messageCount; + + BMIFile mod; + mod.filePath = modFilePath; + mod.fileSize = 0; + + BMIFile mod2; + mod2.filePath = mod2FilePath; + mod2.fileSize = 0; + ModuleDep mod2Dep; + mod2Dep.file = std::move(mod2); + mod2Dep.logicalName = "mod2"; + + BTCModule b; + b.requested = std::move(mod); + b.deps.emplace_back(std::move(mod2Dep)); + + if (const auto &r2 = manager.sendMessage(b); !r2) + { + return tl::unexpected(r2.error()); + } + printMessage(b, true); + } + else if (messageCount == 1) + { + ++messageCount; + BMIFile mod1; + mod1.filePath = mod1FilePath; + mod1.fileSize = 0; + + BTCModule b; + b.requested = std::move(mod1); + + if (const auto &r2 = manager.sendMessage(b); !r2) + { + return tl::unexpected(r2.error()); + } + printMessage(b, true); + } + } + + break; + + case CTB::NON_MODULE: { + } + + case CTB::LAST_MESSAGE: { + } + + return tl::unexpected("Unexpected message received"); + } + + if (messageCount == 2) + { + break; + } + } + } + else + { + return tl::unexpected("creating manager failed" + r.error() + "\n"); + } + +#ifdef _WIN32 + if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) + { + return tl::unexpected("WaitForSingleObject" + getErrorString()); + } +#else + if (waitpid(procId, &procStatus, 0) == -1) + { + return tl::unexpected("waitpid" + getErrorString()); + } +#endif + + return {}; +} +} // namespace +#ifdef IS_THIS_CLANG_REPO +TEST(IPC2978Test, IPC2978Test) +{ + const path p = current_path(); + current_path(LLVM_TOOLS_BINARY_DIR); + const path mainFilePath = (LLVM_TOOLS_BINARY_DIR / path("main .o")).lexically_normal(); + remove(mainFilePath); + + const auto &r = runTest(); + current_path(p); + if (!r) + { + FAIL() << r.error(); + } + if (!exists(mainFilePath)) + { + FAIL() << "main.o not found\n"; + } +} +#else +int main() +{ + remove(path("main .o")); + if (const auto &r = runTest(); !r) + { + fmt::print("{}\n", r.error()); + return EXIT_FAILURE; + } + if (!exists(path("main .o"))) + { + fmt::print("main.o not found\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} +#endif