[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