[libcxx-dev] Question about the no-exceptions mode of libc++
Marshall Clow via libcxx-dev
libcxx-dev at lists.llvm.org
Tue Jan 29 10:03:25 PST 2019
On Mon, Jan 28, 2019 at 10:44 AM Louis Dionne via libcxx-dev <
libcxx-dev at lists.llvm.org> wrote:
> Hi,
>
> My understanding is that libc++ has a no-exceptions mode under which we
> never throw and never use try-catch blocks. This mode is enabled by
> defining _LIBCPP_NO_EXCEPTIONS, or by passing LIBCXX_ENABLE_EXCEPTIONS=OFF
> when configuring CMake.
>
> However, I'd like to understand the design of this mode a bit better. It
> seems like we sometimes call abort() instead of throwing, but sometimes
> we just swallow the would-be exception and carry on. For example, instead
> of throwing std::bad_optional_access, we call std::abort():
>
> void __throw_bad_optional_access() {
> #ifndef _LIBCPP_NO_EXCEPTIONS
> throw bad_optional_access();
> #else
> _VSTD::abort();
> #endif
> }
>
> This makes sense to me. However, in other cases, we just swallow the
> exception. For example, in std::map::at when we can't find the key, we
> just don't throw an exception, and we dereference a null pointer unless I'm
> mistaken:
>
> template <class _Key, class _Tp, class _Compare, class _Allocator>
> const _Tp& map<_Key, _Tp, _Compare, _Allocator>::at(const key_type&
> __k) const {
> __parent_pointer __parent;
> __node_base_pointer __child = __tree_.__find_equal(__parent, __k);
> #ifndef _LIBCPP_NO_EXCEPTIONS
> if (__child == nullptr)
> throw out_of_range("map::at: key not found");
> #endif // _LIBCPP_NO_EXCEPTIONS
> return
> static_cast<__node_pointer>(__child)->__value_.__get_value().second;
> }
>
> Here, when __child == nullptr, we do NOT throw an exception and we end up
> dereferencing it, which is UB. I haven't made a full survey of libc++, but
> I'd like to know whether this is by design, and if so, what is the
> rationale for it. Otherwise, I'd like for us to agree on what the behaviour
> should be (I suggest std::abort()) so that we can fix places that don't
> behave correctly.
>
>
std::abort is the "correct" behavior.
We have a "__throw_out_of_range" call that does what we want.
Change the code to:
- #ifndef _LIBCPP_NO_EXCEPTIONS
if (__child == nullptr)
- throw out_of_range("map::at: key not found");
+ __throw_out_of_range("map::at: key not found");
- #endif // _LIBCPP_NO_EXCEPTIONS
AND it gets rid of ifdefs!
-- Marshall
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/libcxx-dev/attachments/20190129/643223f4/attachment.html>
More information about the libcxx-dev
mailing list