[llvm-dev] RFC: [SmallVector] Adding SVec<T> and Vec<T> convenience wrappers.

Chris Tetreault via llvm-dev llvm-dev at lists.llvm.org
Wed Dec 2 10:36:17 PST 2020


   The LLVM codebase is designed with having exceptions disabled in mind. In such a world, having noexcept everywhere is just noise (“we don’t throw exceptions, so everything is noexcept! yay!”). Furthermore, we provide a CMake build flag to enable exceptions with the intention that user libs with exceptions enabled would link with LLVM and that user lib exceptions would flow through LLVM. If people start adding noexcept everywhere in order to please std::vector, then you will start seeing crashes.

  Since the LLVM codebase largely pretends that exceptions don’t exist, I don’t think it should do anything that will change what actually happens in the presence of exceptions. If this means that we prefer llvm::SmallVector to std::vector for performance reasons, then I say “so be it”.

Thanks,
   Christopher Tetreault

From: llvm-dev <llvm-dev-bounces at lists.llvm.org> On Behalf Of James Y Knight via llvm-dev
Sent: Wednesday, December 2, 2020 9:52 AM
To: Chris Lattner <clattner at nondot.org>
Cc: LLVM Dev <llvm-dev at lists.llvm.org>
Subject: Re: [llvm-dev] RFC: [SmallVector] Adding SVec<T> and Vec<T> convenience wrappers.



On Wed, Dec 2, 2020 at 12:05 AM Chris Lattner <clattner at nondot.org<mailto:clattner at nondot.org>> wrote:
On Dec 1, 2020, at 4:07 PM, Duncan P. N. Exon Smith <dexonsmith at apple.com<mailto:dexonsmith at apple.com>> wrote:

Can you spell this out for me?  Why do we need noexcept if we’re building with -fno-exceptions?  What is going on here?

Sure. It's a bit convoluted. Here's my understanding:

First, here's why std::vector has this behaviour:
- std::vector grow operations need to transfer their existing elements over to the new storage.
- The grow operations are usually required to meet the "strong exception guarantee": if something throws, this function has no effect.
- If move operations throw, you can't provide this guarantee unless you copy (you can't move back the elements that have been half-moved over, in case another exception is thrown; but if it was just a copy, the original storage still has the elements safely unmodified).
- There's a caveat / carve out, that if T cannot be copy-constructed AND T's move constructor is not noexcept, then the guarantee is waived (since there's no way to implement it).
- Implementation is to call std::move_if_noexcept (https://en.cppreference.com/w/cpp/utility/move_if_noexcept), which moves if it's a noexcept operation, or if T is not copy-constructible.

Second, here's why the behaviour doesn't change when -fno-exceptions:
- -fno-exceptions does NOT imply `noexcept` (maybe it should?, but it doesn't).
- This is implemented by detecting via SFINAE whether something is `noexcept` (maybe std::vector::resize/push_back/etc should have a special case? but that's controversial).

IMO, until all the C++ standard libraries and host compilers that we support being built with will consistently use std::move on grow operations in std::vector in -fno-exceptions mode, we should only use std::vector when we absolutely have to. It's not designed for -fno-exceptions codebases

Wow, thank you for the great explanation.  I agree with you that this seems like a pretty credible reason why we can’t depend on every host std::vector to do what we need, so we should use something like an llvm::Vector.

I strongly disagree here. Not wanting to bother to add 'noexcept' to user-defined move-constructors is a poor justification for switching to a different vector type. Perhaps there are other reasons which might justify avoiding std::vector, but not that...
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20201202/29cbc1db/attachment.html>


More information about the llvm-dev mailing list