Loading include/cxx/memory +19 −7 Changes for include/cxx/memory: 19 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -303,7 +303,14 @@ public: typedef T element_type; typedef Deleter deleter_type; unique_ptr() noexcept : _p(nullptr), _d() {} /* constexpr, as the standard has it: a namespace-scope unique_ptr is * then constant-initialized, with no initializer code at all. Without * it, a plugin carrying its own copy of a global the executable also * defines (blogi's letsencrypt.so, from the static libblogidev.a) * re-ran "Cfg = nullptr" through the GOT on dlopen() -- onto the * executable's live object (real gap: blogi SIGSEGV after loading * its plugins). */ constexpr unique_ptr() noexcept : _p(nullptr), _d() {} constexpr unique_ptr(decltype(nullptr)) noexcept : _p(nullptr), _d() {} explicit unique_ptr(T* p) noexcept : _p(p), _d() {} unique_ptr(T* p, const Deleter& d) noexcept : _p(p), _d(d) {} Loading Loading @@ -359,7 +366,11 @@ public: return *this; } ~unique_ptr() { if (_p) _d(_p); } /* leaves the pointer null, as libstdc++'s does: a second destructor * run on the same object -- the executable's global and a plugin's * own copy of it, both registered with __cxa_atexit -- is a no-op, * not a double delete */ ~unique_ptr() { if (_p) _d(_p); _p = nullptr; } T* get() const noexcept { return _p; } Deleter& get_deleter() noexcept { return _d; } Loading Loading @@ -473,7 +484,7 @@ public: typedef T element_type; typedef Deleter deleter_type; unique_ptr() noexcept : _p(nullptr), _d() {} constexpr unique_ptr() noexcept : _p(nullptr), _d() {} constexpr unique_ptr(decltype(nullptr)) noexcept : _p(nullptr), _d() {} explicit unique_ptr(T* p) noexcept : _p(p), _d() {} unique_ptr(T* p, const Deleter& d) noexcept : _p(p), _d(d) {} Loading @@ -488,7 +499,7 @@ public: } unique_ptr& operator=(decltype(nullptr)) noexcept { reset(); return *this; } ~unique_ptr() { if (_p) _d(_p); } ~unique_ptr() { if (_p) _d(_p); _p = nullptr; } T* get() const noexcept { return _p; } Deleter& get_deleter() noexcept { return _d; } Loading Loading @@ -648,12 +659,13 @@ public: template<class U> friend class shared_ptr; template<class U> friend class weak_ptr; shared_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} /* constexpr for constant initialization, see unique_ptr() */ constexpr shared_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} template<class Y, class = enable_if_t<is_convertible<Y*, T*>::value>> explicit shared_ptr(Y* p) : _p(p), _ctrl(__internal::make_owner(p)) { __wire_enable_shared_from_this(p); } shared_ptr(decltype(nullptr)) noexcept : _p(nullptr), _ctrl(nullptr) {} constexpr shared_ptr(decltype(nullptr)) noexcept : _p(nullptr), _ctrl(nullptr) {} /* Real custom-deleter constructors: d(p) runs instead of `delete p` * when the last owner goes away -- also for a null p, as the Loading Loading @@ -917,7 +929,7 @@ class weak_ptr { public: template<class U> friend class weak_ptr; weak_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} constexpr weak_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} weak_ptr(const weak_ptr& o) noexcept : _p(o._p), _ctrl(o._ctrl) { if (_ctrl) __internal::__ref_inc(_ctrl->weak_count); Loading include/cxx/mutex +5 −2 Changes for include/cxx/mutex: 5 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -47,7 +47,10 @@ inline constexpr adopt_lock_t adopt_lock{}; class mutex { public: mutex() noexcept { pthread_mutex_init(&_m, nullptr); } /* constexpr, as the standard has it (constant initialization, see * unique_ptr() in <memory>): the all-zero pthread_mutex_t is exactly * the unlocked state pthread_mutex_init() establishes */ constexpr mutex() noexcept : _m() {} ~mutex() { pthread_mutex_destroy(&_m); } mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; Loading Loading @@ -198,7 +201,7 @@ void lock(L1& l1, L2& l2) { struct once_flag { pthread_mutex_t m{}; bool called = false; once_flag() noexcept { pthread_mutex_init(&m, nullptr); } constexpr once_flag() noexcept {} // all-zero: unlocked, see mutex() }; template<class F, class... Args> Loading src/test/e2e/CMakeLists.txt +2 −1 Changes for src/test/e2e/CMakeLists.txt: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -3711,7 +3711,8 @@ add_test(NAME e2e_lldb_stdlib_gaps71 COMMAND e2e_lldb_stdlib_gaps71) # C++20 calendar types in <chrono> (day/month/year/year_month_day), # from_stream() and std::formatter for sys_time/year_month_day -- real # gap: blogi's sitemap and std::format("{:%Y-%m-%d %T}", now()) -- # and sregex_iterator over a non-const string's begin()/end(). # sregex_iterator over a non-const string's begin()/end(), and constant # initialization of global unique_ptr/shared_ptr/weak_ptr/mutex/once_flag. add_executable(e2e_blogi_stdlib_gaps blogi_stdlib_gaps_test.cpp) set_target_properties(e2e_blogi_stdlib_gaps PROPERTIES OUTPUT_NAME "blogi_stdlib_gaps_test") target_compile_options(e2e_blogi_stdlib_gaps PRIVATE -fPIE) Loading src/test/e2e/blogi_stdlib_gaps_test.cpp +33 −1 Changes for src/test/e2e/blogi_stdlib_gaps_test.cpp: 33 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -3,13 +3,18 @@ * year_month_day), from_stream() and the std::formatter for * sys_time/year_month_day (blogi's sitemap and its * std::format("{:%Y-%m-%d %T}", system_clock::now()) timestamps), and * sregex_iterator over a non-const string's begin()/end(). */ * sregex_iterator over a non-const string's begin()/end(), and constant * initialization of namespace-scope unique_ptr/shared_ptr/weak_ptr/mutex/ * once_flag (a plugin's own copy of such a global re-ran its initializer * onto the executable's object on dlopen(): blogi SIGSEGV). */ #include <stdio.h> #include <string.h> #include <chrono> #include <format> #include <map> #include <memory> #include <mutex> #include <regex> #include <sstream> #include <string> Loading Loading @@ -94,6 +99,32 @@ static int test_format() { return 0; } /* constinit fails to compile unless these are constant-initialized */ constinit std::unique_ptr<std::string> g_unique; constinit std::unique_ptr<int[]> g_unique_array; constinit std::shared_ptr<int> g_shared; constinit std::shared_ptr<int> g_shared_null(nullptr); constinit std::weak_ptr<int> g_weak; constinit std::mutex g_mutex; constinit std::once_flag g_once; static int test_constant_init() { g_unique = std::make_unique<std::string>("x"); CHECK(*g_unique == "x", "constinit unique_ptr is usable"); std::lock_guard<std::mutex> lk(g_mutex); int n = 0; std::call_once(g_once, [&] { n++; }); std::call_once(g_once, [&] { n++; }); CHECK(n == 1, "constinit once_flag runs once"); CHECK(!g_shared && !g_shared_null && g_weak.expired() && !g_unique_array, "constinit pointers start empty"); /* the destructor leaves it null: a second run is a no-op */ alignas(std::unique_ptr<int>) unsigned char buf[sizeof(std::unique_ptr<int>)]; auto* up = new (buf) std::unique_ptr<int>(new int(7)); up->~unique_ptr(); CHECK(up->get() == nullptr, "~unique_ptr leaves the pointer null"); return 0; } static int test_sregex_nonconst() { std::string content = "<div id=\"a\"></div><p id='b'></p>"; std::regex idRegex("\\bid=[\"']([^\"']+)[\"']"); Loading @@ -111,6 +142,7 @@ int main() { if (test_from_stream()) return 1; if (test_format()) return 1; if (test_sregex_nonconst()) return 1; if (test_constant_init()) return 1; printf("PASS: blogi_stdlib_gaps\n"); return 0; } Loading
include/cxx/memory +19 −7 Changes for include/cxx/memory: 19 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -303,7 +303,14 @@ public: typedef T element_type; typedef Deleter deleter_type; unique_ptr() noexcept : _p(nullptr), _d() {} /* constexpr, as the standard has it: a namespace-scope unique_ptr is * then constant-initialized, with no initializer code at all. Without * it, a plugin carrying its own copy of a global the executable also * defines (blogi's letsencrypt.so, from the static libblogidev.a) * re-ran "Cfg = nullptr" through the GOT on dlopen() -- onto the * executable's live object (real gap: blogi SIGSEGV after loading * its plugins). */ constexpr unique_ptr() noexcept : _p(nullptr), _d() {} constexpr unique_ptr(decltype(nullptr)) noexcept : _p(nullptr), _d() {} explicit unique_ptr(T* p) noexcept : _p(p), _d() {} unique_ptr(T* p, const Deleter& d) noexcept : _p(p), _d(d) {} Loading Loading @@ -359,7 +366,11 @@ public: return *this; } ~unique_ptr() { if (_p) _d(_p); } /* leaves the pointer null, as libstdc++'s does: a second destructor * run on the same object -- the executable's global and a plugin's * own copy of it, both registered with __cxa_atexit -- is a no-op, * not a double delete */ ~unique_ptr() { if (_p) _d(_p); _p = nullptr; } T* get() const noexcept { return _p; } Deleter& get_deleter() noexcept { return _d; } Loading Loading @@ -473,7 +484,7 @@ public: typedef T element_type; typedef Deleter deleter_type; unique_ptr() noexcept : _p(nullptr), _d() {} constexpr unique_ptr() noexcept : _p(nullptr), _d() {} constexpr unique_ptr(decltype(nullptr)) noexcept : _p(nullptr), _d() {} explicit unique_ptr(T* p) noexcept : _p(p), _d() {} unique_ptr(T* p, const Deleter& d) noexcept : _p(p), _d(d) {} Loading @@ -488,7 +499,7 @@ public: } unique_ptr& operator=(decltype(nullptr)) noexcept { reset(); return *this; } ~unique_ptr() { if (_p) _d(_p); } ~unique_ptr() { if (_p) _d(_p); _p = nullptr; } T* get() const noexcept { return _p; } Deleter& get_deleter() noexcept { return _d; } Loading Loading @@ -648,12 +659,13 @@ public: template<class U> friend class shared_ptr; template<class U> friend class weak_ptr; shared_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} /* constexpr for constant initialization, see unique_ptr() */ constexpr shared_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} template<class Y, class = enable_if_t<is_convertible<Y*, T*>::value>> explicit shared_ptr(Y* p) : _p(p), _ctrl(__internal::make_owner(p)) { __wire_enable_shared_from_this(p); } shared_ptr(decltype(nullptr)) noexcept : _p(nullptr), _ctrl(nullptr) {} constexpr shared_ptr(decltype(nullptr)) noexcept : _p(nullptr), _ctrl(nullptr) {} /* Real custom-deleter constructors: d(p) runs instead of `delete p` * when the last owner goes away -- also for a null p, as the Loading Loading @@ -917,7 +929,7 @@ class weak_ptr { public: template<class U> friend class weak_ptr; weak_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} constexpr weak_ptr() noexcept : _p(nullptr), _ctrl(nullptr) {} weak_ptr(const weak_ptr& o) noexcept : _p(o._p), _ctrl(o._ctrl) { if (_ctrl) __internal::__ref_inc(_ctrl->weak_count); Loading
include/cxx/mutex +5 −2 Changes for include/cxx/mutex: 5 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -47,7 +47,10 @@ inline constexpr adopt_lock_t adopt_lock{}; class mutex { public: mutex() noexcept { pthread_mutex_init(&_m, nullptr); } /* constexpr, as the standard has it (constant initialization, see * unique_ptr() in <memory>): the all-zero pthread_mutex_t is exactly * the unlocked state pthread_mutex_init() establishes */ constexpr mutex() noexcept : _m() {} ~mutex() { pthread_mutex_destroy(&_m); } mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; Loading Loading @@ -198,7 +201,7 @@ void lock(L1& l1, L2& l2) { struct once_flag { pthread_mutex_t m{}; bool called = false; once_flag() noexcept { pthread_mutex_init(&m, nullptr); } constexpr once_flag() noexcept {} // all-zero: unlocked, see mutex() }; template<class F, class... Args> Loading
src/test/e2e/CMakeLists.txt +2 −1 Changes for src/test/e2e/CMakeLists.txt: 2 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -3711,7 +3711,8 @@ add_test(NAME e2e_lldb_stdlib_gaps71 COMMAND e2e_lldb_stdlib_gaps71) # C++20 calendar types in <chrono> (day/month/year/year_month_day), # from_stream() and std::formatter for sys_time/year_month_day -- real # gap: blogi's sitemap and std::format("{:%Y-%m-%d %T}", now()) -- # and sregex_iterator over a non-const string's begin()/end(). # sregex_iterator over a non-const string's begin()/end(), and constant # initialization of global unique_ptr/shared_ptr/weak_ptr/mutex/once_flag. add_executable(e2e_blogi_stdlib_gaps blogi_stdlib_gaps_test.cpp) set_target_properties(e2e_blogi_stdlib_gaps PROPERTIES OUTPUT_NAME "blogi_stdlib_gaps_test") target_compile_options(e2e_blogi_stdlib_gaps PRIVATE -fPIE) Loading
src/test/e2e/blogi_stdlib_gaps_test.cpp +33 −1 Changes for src/test/e2e/blogi_stdlib_gaps_test.cpp: 33 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -3,13 +3,18 @@ * year_month_day), from_stream() and the std::formatter for * sys_time/year_month_day (blogi's sitemap and its * std::format("{:%Y-%m-%d %T}", system_clock::now()) timestamps), and * sregex_iterator over a non-const string's begin()/end(). */ * sregex_iterator over a non-const string's begin()/end(), and constant * initialization of namespace-scope unique_ptr/shared_ptr/weak_ptr/mutex/ * once_flag (a plugin's own copy of such a global re-ran its initializer * onto the executable's object on dlopen(): blogi SIGSEGV). */ #include <stdio.h> #include <string.h> #include <chrono> #include <format> #include <map> #include <memory> #include <mutex> #include <regex> #include <sstream> #include <string> Loading Loading @@ -94,6 +99,32 @@ static int test_format() { return 0; } /* constinit fails to compile unless these are constant-initialized */ constinit std::unique_ptr<std::string> g_unique; constinit std::unique_ptr<int[]> g_unique_array; constinit std::shared_ptr<int> g_shared; constinit std::shared_ptr<int> g_shared_null(nullptr); constinit std::weak_ptr<int> g_weak; constinit std::mutex g_mutex; constinit std::once_flag g_once; static int test_constant_init() { g_unique = std::make_unique<std::string>("x"); CHECK(*g_unique == "x", "constinit unique_ptr is usable"); std::lock_guard<std::mutex> lk(g_mutex); int n = 0; std::call_once(g_once, [&] { n++; }); std::call_once(g_once, [&] { n++; }); CHECK(n == 1, "constinit once_flag runs once"); CHECK(!g_shared && !g_shared_null && g_weak.expired() && !g_unique_array, "constinit pointers start empty"); /* the destructor leaves it null: a second run is a no-op */ alignas(std::unique_ptr<int>) unsigned char buf[sizeof(std::unique_ptr<int>)]; auto* up = new (buf) std::unique_ptr<int>(new int(7)); up->~unique_ptr(); CHECK(up->get() == nullptr, "~unique_ptr leaves the pointer null"); return 0; } static int test_sregex_nonconst() { std::string content = "<div id=\"a\"></div><p id='b'></p>"; std::regex idRegex("\\bid=[\"']([^\"']+)[\"']"); Loading @@ -111,6 +142,7 @@ int main() { if (test_from_stream()) return 1; if (test_format()) return 1; if (test_sregex_nonconst()) return 1; if (test_constant_init()) return 1; printf("PASS: blogi_stdlib_gaps\n"); return 0; }