From eba98e4e1adcf3ce11e5934e2dce54f29aef1e0a Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Thu, 24 Oct 2024 11:30:18 +0200 Subject: Make Unique even stronger --- src/allocator.h | 6 +++--- src/compat.cpp | 2 +- src/compat.h | 6 +++--- src/intercopycontext.cpp | 10 +++++----- src/intercopycontext.h | 2 +- src/keeper.cpp | 16 ++++++++-------- src/keeper.h | 2 +- src/lanes.cpp | 14 +++++++------- src/linda.cpp | 8 ++++---- src/lindafactory.cpp | 2 +- src/stackindex.hpp | 4 ++++ src/unique.hpp | 9 +++++++++ src/universe.h | 2 +- 13 files changed, 48 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/allocator.h b/src/allocator.h index 0505251..e0ff9d6 100644 --- a/src/allocator.h +++ b/src/allocator.h @@ -16,11 +16,11 @@ namespace lanes { lua_Alloc allocF{ nullptr }; void* allocUD{ nullptr }; - [[nodiscard]] static void* operator new(size_t size_) noexcept = delete; // can't create one outside of a Lua state - [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept { return lua_newuserdatauv(L_, size_, 0); } + [[nodiscard]] static void* operator new(size_t const size_) noexcept = delete; // can't create one outside of a Lua state + [[nodiscard]] static void* operator new(size_t const size_, lua_State* const L_) noexcept { return lua_newuserdatauv(L_, size_, UserValueCount{ 0 }); } // always embedded somewhere else or "in-place constructed" as a full userdata // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception - static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) {} + static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {} AllocatorDefinition(uintptr_t const version_, lua_Alloc const allocF_, void* const allocUD_) noexcept : version{ version_ } diff --git a/src/compat.cpp b/src/compat.cpp index 1be910f..b661b28 100644 --- a/src/compat.cpp +++ b/src/compat.cpp @@ -83,7 +83,7 @@ void luaL_requiref(lua_State* L_, const char* modname_, lua_CFunction openf_, in // ################################################################################################# // ################################################################################################# -void* lua_newuserdatauv(lua_State* const L_, size_t const sz_, [[maybe_unused]] int const nuvalue_) +void* lua_newuserdatauv(lua_State* const L_, size_t const sz_, [[maybe_unused]] UserValueCount const nuvalue_) { LUA_ASSERT(L_, nuvalue_ <= 1); return lua_newuserdata(L_, sz_); diff --git a/src/compat.h b/src/compat.h index af014b1..0b6b12b 100644 --- a/src/compat.h +++ b/src/compat.h @@ -80,7 +80,7 @@ inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_) #if LUA_VERSION_NUM < 504 -void* lua_newuserdatauv(lua_State* L_, size_t sz_, int nuvalue_); +void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_); int lua_getiuservalue(lua_State* L_, StackIndex idx_, int n_); int lua_setiuservalue(lua_State* L_, StackIndex idx_, int n_); @@ -138,7 +138,7 @@ inline LuaType luaG_type(lua_State* const L_, StackIndex const idx_) // use this in place of lua_absindex to save a function call inline StackIndex luaG_absindex(lua_State* const L_, StackIndex const idx_) { - return StackIndex{ (idx_ >= 0 || idx_ <= kIdxRegistry) ? idx_ : lua_gettop(L_) + idx_ + 1 }; + return StackIndex{ (idx_ >= 0 || idx_ <= kIdxRegistry) ? idx_ : StackIndex{ lua_gettop(L_) + idx_ + 1 } }; } // ################################################################################################# @@ -295,7 +295,7 @@ static inline void luaG_newlib(lua_State* const L_, luaL_Reg const (&funcs_)[N]) // ################################################################################################# template -[[nodiscard]] T* luaG_newuserdatauv(lua_State* L_, int nuvalue_) +[[nodiscard]] T* luaG_newuserdatauv(lua_State* L_, UserValueCount nuvalue_) { return static_cast(lua_newuserdatauv(L_, sizeof(T), nuvalue_)); } diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index f54c152..5f3ce7d 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp @@ -738,7 +738,7 @@ LuaType InterCopyContext::processConversion() const lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u } // assign uservalues - int _uvi{ _nuv }; + UserValueIndex _uvi{ _nuv.value() }; while (_uvi > 0) { _c.L1_i = SourceIndex{ luaG_absindex(L1, kIdxTop) }; if (_c.interCopyOne() != InterCopyResult::Success) { // L2: ... u uv @@ -796,7 +796,7 @@ LuaType InterCopyContext::processConversion() const InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name }; StackIndex const _clone_i{ lua_gettop(L2) }; STACK_GROW(L2, _nuv); - int _uvi{ _nuv }; + UserValueIndex _uvi{ _nuv.value() }; while (_uvi) { _c.L1_i = SourceIndex{ luaG_absindex(L1, kIdxTop) }; if (_c.interCopyOne() != InterCopyResult::Success) { // L1: ... deep ... [uv]* L2: deep uv @@ -880,7 +880,7 @@ LuaType InterCopyContext::processConversion() const lua_setmetatable(L2, -2); // L2: ... mt u // transfer and assign uservalues InterCopyContext _c{ *this }; - int _uvi{ _nuv }; + UserValueIndex _uvi{ _nuv.value() }; while (_uvi > 0) { _c.L1_i = SourceIndex{ luaG_absindex(L1, kIdxTop) }; if (_c.interCopyOne() != InterCopyResult::Success) { // L2: ... mt u uv @@ -1276,7 +1276,7 @@ namespace { DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U }); StackIndex const _top_L1{ lua_gettop(L1) }; - int const _available{ (L1_i != 0) ? (_top_L1 - L1_i + 1) : _top_L1 }; + int const _available{ (L1_i != 0) ? (_top_L1 - L1_i + 1) : _top_L1.value() }; if (n_ > _available) { // requesting to copy more than is available? DEBUGSPEW_CODE(DebugSpew(U) << "nothing to copy" << std::endl); @@ -1298,7 +1298,7 @@ namespace { InterCopyResult _copyok{ InterCopyResult::Success }; STACK_CHECK_START_REL(L1, 0); // if L1_i is specified, start here, else take the _n items off the top of the stack - for (StackIndex _i{ L1_i != 0 ? L1_i : (_top_L1 - n_ + 1) }, _j{ 1 }; _j <= n_; ++_i, ++_j) { + for (StackIndex _i{ (L1_i != 0) ? L1_i.value() : (_top_L1 - n_ + 1) }, _j{ 1 }; _j <= n_; ++_i, ++_j) { char _tmpBuf[16]; if (U->verboseErrors) { sprintf(_tmpBuf, "arg_%d", _j.operator int()); diff --git a/src/intercopycontext.h b/src/intercopycontext.h index a9be267..3a5db36 100644 --- a/src/intercopycontext.h +++ b/src/intercopycontext.h @@ -41,7 +41,7 @@ class InterCopyContext [[nodiscard]] std::string_view findLookupName() const; // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error - lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; } + lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2.value() : L1.value(); } [[nodiscard]] LuaType processConversion() const; // for use in copyCachedFunction diff --git a/src/keeper.cpp b/src/keeper.cpp index bea91a7..7deb2b3 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -75,7 +75,7 @@ class KeyUD LindaLimit limit{ -1 }; // a fifo full userdata has one uservalue, the table that holds the actual fifo contents - [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return luaG_newuserdatauv(L_, 1); } + [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return luaG_newuserdatauv(L_, UserValueCount{ 1 }); } // always embedded somewhere else or "in-place constructed" as a full userdata // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } @@ -275,7 +275,7 @@ bool KeyUD::reset(KeeperState const K_) bool const _wasFull{ (limit > 0) && (count >= limit) }; // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit unchanged! // if we have an actual limit, use it to preconfigure the table - lua_createtable(K_, (limit <= 0) ? 0 : limit, 0); // K_: KeysDB key val... KeyUD {} + lua_createtable(K_, (limit <= 0) ? 0 : limit.value(), 0); // K_: KeysDB key val... KeyUD {} lua_setiuservalue(K_, StackIndex{ -2 }, kContentsTableIndex); // K_: KeysDB key val... KeyUD first = 1; count = 0; @@ -646,7 +646,7 @@ KeeperCallResult keeper_call(KeeperState const K_, keeper_api_t const func_, lua lua_pushlightuserdata(K_, linda_); // L: ... args... K_: func_ linda if ( (_args == 0) || - (InterCopyContext{ linda_->U, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.interCopy(_args) == InterCopyResult::Success) + (InterCopyContext{ linda_->U, DestState{ K_.value() }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.interCopy(_args) == InterCopyResult::Success) ) { // L: ... args... K_: func_ linda args... lua_call(K_, 1 + _args, LUA_MULTRET); // L: ... args... K_: result... int const _retvals{ lua_gettop(K_) - _top_K }; @@ -656,7 +656,7 @@ KeeperCallResult keeper_call(KeeperState const K_, keeper_api_t const func_, lua // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) if ( (_retvals == 0) || - (InterCopyContext{ linda_->U, DestState{ L_ }, SourceState{ K_ }, {}, {}, {}, LookupMode::FromKeeper, {} }.interMove(_retvals) == InterCopyResult::Success) + (InterCopyContext{ linda_->U, DestState{ L_ }, SourceState{ K_.value() }, {}, {}, {}, LookupMode::FromKeeper, {} }.interMove(_retvals) == InterCopyResult::Success) ) { // L: ... args... result... K_: result... _result.emplace(_retvals); } @@ -721,7 +721,7 @@ void Keeper::operator delete[](void* p_, Universe* U_) int Keeper::PushLindaStorage(Linda& linda_, DestState const L_) { Keeper* const _keeper{ linda_.whichKeeper() }; - KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ nullptr } }; + KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ static_cast(nullptr) } }; if (_K == nullptr) { return 0; } @@ -740,7 +740,7 @@ int Keeper::PushLindaStorage(Linda& linda_, DestState const L_) STACK_GROW(L_, 5); STACK_CHECK_START_REL(L_, 0); lua_newtable(L_); // _K: KeysDB L_: out - InterCopyContext _c{ linda_.U, L_, SourceState{ _K }, {}, {}, {}, LookupMode::FromKeeper, {} }; + InterCopyContext _c{ linda_.U, L_, SourceState{ _K.value() }, {}, {}, {}, LookupMode::FromKeeper, {} }; lua_pushnil(_K); // _K: KeysDB nil L_: out while (lua_next(_K, -2)) { // _K: KeysDB key KeyUD L_: out KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; @@ -810,7 +810,7 @@ void Keepers::close() } auto _closeOneKeeper = [](Keeper& keeper_) { - lua_State* const _K{ std::exchange(keeper_.K, KeeperState{ nullptr }) }; + lua_State* const _K{ std::exchange(keeper_.K, KeeperState{ static_cast(nullptr) }) }; if (_K) { lua_close(_K); } @@ -928,7 +928,7 @@ void Keepers::initialize(Universe& U_, lua_State* L_, size_t const nbKeepers_, i // copy package.path and package.cpath from the source state if (luaG_getmodule(L, LUA_LOADLIBNAME) != LuaType::NIL) { // L_: settings package _K: // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately - InterCopyContext _c{ U, DestState{ _K }, SourceState{ L }, {}, SourceIndex{ luaG_absindex(L, kIdxTop) }, {}, LookupMode::ToKeeper, {} }; + InterCopyContext _c{ U, DestState{ _K.value() }, SourceState{ L }, {}, SourceIndex{ luaG_absindex(L, kIdxTop) }, {}, LookupMode::ToKeeper, {} }; if (_c.interCopyPackage() != InterCopyResult::Success) { // L_: settings ... error_msg _K: // if something went wrong, the error message is at the top of the stack lua_remove(L, -2); // L_: settings error_msg diff --git a/src/keeper.h b/src/keeper.h index 74bdbf2..7e3e1fa 100644 --- a/src/keeper.h +++ b/src/keeper.h @@ -16,7 +16,7 @@ DECLARE_UNIQUE_TYPE(KeeperIndex, int); struct Keeper { std::mutex mutex; - KeeperState K{ nullptr }; + KeeperState K{ static_cast(nullptr) }; [[nodiscard]] static void* operator new[](size_t size_, Universe* U_) noexcept; // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception diff --git a/src/lanes.cpp b/src/lanes.cpp index 572d8f9..e545ca0 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -329,7 +329,7 @@ LUAG_FUNC(lane_new) DEBUGSPEW_CODE(DebugSpew(lane->U) << "lane_new: preparing lane userdata" << std::endl); STACK_CHECK_START_REL(L, 0); // a Lane full userdata needs a single uservalue - Lane** const _ud{ luaG_newuserdatauv(L, 1) }; // L: ... lane + Lane** const _ud{ luaG_newuserdatauv(L, UserValueCount{ 1 }) }; // L: ... lane *_ud = lane; // don't forget to store the pointer in the userdata! // Set metatable for the userdata @@ -342,7 +342,7 @@ LUAG_FUNC(lane_new) lua_newtable(L); // L: ... lane {uv} // Store the gc_cb callback in the uservalue - StackIndex const _gc_cb_idx{ lua_isnoneornil(L, kGcCbIdx) ? 0 : kGcCbIdx }; + StackIndex const _gc_cb_idx{ lua_isnoneornil(L, kGcCbIdx) ? kIdxNone : kGcCbIdx }; if (_gc_cb_idx > 0) { kLaneGC.pushKey(L); // L: ... lane {uv} k lua_pushvalue(L, _gc_cb_idx); // L: ... lane {uv} k gc_cb @@ -354,7 +354,7 @@ LUAG_FUNC(lane_new) lua_State* const _L2{ lane->L }; STACK_CHECK_START_REL(_L2, 0); - StackIndex const _name_idx{ lua_isnoneornil(L, kNameIdx) ? 0 : kNameIdx }; + StackIndex const _name_idx{ lua_isnoneornil(L, kNameIdx) ? kIdxNone : kNameIdx }; std::string_view const _debugName{ (_name_idx > 0) ? luaG_tostring(L, _name_idx) : std::string_view{} }; if (!_debugName.empty()) { @@ -393,7 +393,7 @@ LUAG_FUNC(lane_new) // On some platforms, -3 is equivalent to -2 and +3 to +2 int const _priority{ std::invoke([L = L_]() { - int const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? 0 : kPrioIdx }; + StackIndex const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? kIdxNone : kPrioIdx }; if (_prio_idx == 0) { return kThreadPrioDefault; } @@ -412,7 +412,7 @@ LUAG_FUNC(lane_new) STACK_CHECK_START_REL(L_, 0); // package - StackIndex const _package_idx{ lua_isnoneornil(L_, kPackIdx) ? 0 : kPackIdx }; + StackIndex const _package_idx{ lua_isnoneornil(L_, kPackIdx) ? kIdxNone : kPackIdx }; if (_package_idx != 0) { DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: update 'package'" << std::endl); // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack @@ -424,7 +424,7 @@ LUAG_FUNC(lane_new) STACK_CHECK(_L2, 0); // modules to require in the target lane *before* the function is transfered! - StackIndex const _required_idx{ lua_isnoneornil(L_, kRequIdx) ? 0 : kRequIdx }; + StackIndex const _required_idx{ lua_isnoneornil(L_, kRequIdx) ? kIdxNone : kRequIdx }; if (_required_idx != 0) { int _nbRequired{ 1 }; DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: process 'required' list" << std::endl); @@ -473,7 +473,7 @@ LUAG_FUNC(lane_new) // Appending the specified globals to the global environment // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... // - StackIndex const _globals_idx{ lua_isnoneornil(L_, kGlobIdx) ? 0 : kGlobIdx }; + StackIndex const _globals_idx{ lua_isnoneornil(L_, kGlobIdx) ? kIdxNone : kGlobIdx }; if (_globals_idx != 0) { DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: transfer globals" << std::endl); if (!lua_istable(L_, _globals_idx)) { diff --git a/src/linda.cpp b/src/linda.cpp index 6655ae4..15e7a2c 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -196,7 +196,7 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_) // acquire the keeper Keeper* const _keeper{ _linda->acquireKeeper() }; - KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ nullptr } }; + KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ static_cast(nullptr) } }; if (_K == nullptr) return 0; @@ -469,7 +469,7 @@ LUAG_FUNC(linda_limit) luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); // make sure we got a numeric limit, or "unlimited", (or nothing) bool const _unlimited{ luaG_tostring(L_, StackIndex{ 3 }) == "unlimited" }; - LindaLimit const _val{ _unlimited ? std::numeric_limits::max() : LindaLimit{ static_cast(luaL_optinteger(L_, 3, 0)) } }; + LindaLimit const _val{ _unlimited ? std::numeric_limits::max() : static_cast(luaL_optinteger(L_, 3, 0)) }; if (_val < 0) { raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0"); } @@ -575,7 +575,7 @@ LUAG_FUNC(linda_receive) Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) }; Keeper* const _keeper{ _linda->whichKeeper() }; - KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ nullptr } }; + KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ static_cast(nullptr) } }; if (_K == nullptr) return 0; @@ -714,7 +714,7 @@ LUAG_FUNC(linda_send) { Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) }; Keeper* const _keeper{ _linda->whichKeeper() }; - KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ nullptr } }; + KeeperState const _K{ _keeper ? _keeper->K : KeeperState{ static_cast(nullptr) } }; if (_K == nullptr) return 0; diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index e5903fb..cb801dd 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp @@ -80,7 +80,7 @@ void LindaFactory::deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) cons Keeper* const _keeper{ _need_acquire_release ? _linda->acquireKeeper() : _myKeeper }; LUA_ASSERT(L_, _keeper == _myKeeper); // should always be the same // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... - [[maybe_unused]] KeeperCallResult const result{ keeper_call(_keeper->K, KEEPER_API(destruct), L_, _linda, StackIndex{ 0 }) }; + [[maybe_unused]] KeeperCallResult const result{ keeper_call(_keeper->K, KEEPER_API(destruct), L_, _linda, kIdxNone) }; LUA_ASSERT(L_, result.has_value() && result.value() == 0); if (_need_acquire_release) { _linda->releaseKeeper(_keeper); diff --git a/src/stackindex.hpp b/src/stackindex.hpp index 1c2ce8c..c414ce2 100644 --- a/src/stackindex.hpp +++ b/src/stackindex.hpp @@ -5,6 +5,9 @@ DECLARE_UNIQUE_TYPE(StackIndex, int); static_assert(std::is_trivial_v); +DECLARE_UNIQUE_TYPE(UserValueIndex, int); +static_assert(std::is_trivial_v); + DECLARE_UNIQUE_TYPE(UserValueCount, int); static_assert(std::is_trivial_v); @@ -14,4 +17,5 @@ static_assert(std::is_trivial_v); // ################################################################################################# static constexpr StackIndex kIdxRegistry{ LUA_REGISTRYINDEX }; +static constexpr StackIndex kIdxNone{ 0 }; static constexpr StackIndex kIdxTop{ -1 }; diff --git a/src/unique.hpp b/src/unique.hpp index 98a9d48..27ea71e 100644 --- a/src/unique.hpp +++ b/src/unique.hpp @@ -10,6 +10,7 @@ class Unique T val; // no default initialization so that std::is_trivial_v> == true public: + using self = Unique; using type = T; ~Unique() = default; @@ -25,12 +26,20 @@ class Unique constexpr Unique& operator=(Unique const&) = default; constexpr Unique& operator=(Unique&&) = default; + // Forbid construction with any other class, especially with types that convert naturally to UnderlyingType. + // For instance, this prevents construction with a float when UnderlyingType is an integer type. + // Conversion will have to be explicit and the developer will be aware of it. + // However we want to keep the same-type copy constructors (including with an inherited class), hence the enable_if stuff. + template >, bool> = true> + Unique(AnyOtherClass&&) = delete; + // can't implicitly affect from base type Unique& operator=(T const&) = delete; constexpr Unique& operator=(T&&) = delete; // cast constexpr operator T() const noexcept { return val; } + constexpr T value() const noexcept { return val; } // pre-increment auto& operator++() noexcept diff --git a/src/universe.h b/src/universe.h index dc8940f..75e2198 100644 --- a/src/universe.h +++ b/src/universe.h @@ -119,7 +119,7 @@ class Universe std::atomic selfdestructingCount{ 0 }; public: - [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, lua_State* L_) noexcept { return luaG_newuserdatauv(L_, 0); }; + [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, lua_State* L_) noexcept { return luaG_newuserdatauv(L_, UserValueCount{ 0 }); }; // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) {} // nothing to do, as nothing is allocated independently -- cgit v1.2.3-55-g6feb