noexcept on move ctors (was: C++ "allowed features" changes from the past 10 months)

85 views
Skip to first unread message

Nico Weber

unread,
Sep 5, 2019, 1:43:45 PM9/5/19
to Peter Kasting, cxx
(chromium-dev to bcc, moving to cxx@)

This is a reply to an 1.5 year old email, but there's currently a mass-clang-tidy rewrite to add "noexcept" to all move ctors. I pushed back on this and several people sent me a link to the email this is replying to, so I wanted to open this up for discussion.

This mail says "move ctors should be declared noexcept", but https://google.github.io/styleguide/cppguide.html#noexcept actually says "Specify noexcept when it is useful and correct." -- that sounds more like "it's ok to use this when it makes sense", not "you should use this".

I think doing a mass rewrite isn't a great idea, because:

- There's hope to one day have the same semantic effect automatically when building with -fno-exceptions (e.g. https://reviews.llvm.org/D62228), and at that point this is just busy work

- gcc and clang have different requirements for noexcept, and auto-adding this everywhere increases the cost of keeping the gcc build going

- it adds visual noise

- most of the time it has no benefit


It's fine to add noexcept in cases where it provides measurable perf benefits, but I don't think we should do a mass codebase rewrite. We also should follow the google style guide that says "use when useful and correct", not "should".

Nico



On Thu, Jan 18, 2018 at 10:53 PM 'Peter Kasting' via Chromium-dev <chromi...@chromium.org> wrote:
As many of you are aware, Chromium lists the allowed subsets of C++11/14.  Since not everyone keeps constant track, here are the features that have been allowed since the last email I sent ~10 months ago.  There are a lot!
  • std::make_unique<>(), which should replace all existing usage of base::MakeUnique<>() and WTF::MakeUnique<>().  (And, in general, should be used in preference to bare new.)
  • Binary literals, so masks and bit-based constants can be written as 0b11010001 instead of 0xD1, where doing so would be more readable.
  • Literal separators in numeric constants, so that long constants like 0b10000000100000001000000010000000 can be broken into pieces like 0b10000000'10000000'10000000'10000000.
  • constexpr on more complex code, including code with conditionals and loops.  This allows marking more simple functions as constexpr.
  • Aggregate initialization of structs with default members, which allows structs with default values specified for some/all members to still be initialized with an aggregate of values.  This may be useful for unit tests which define structs to hold test case data, then initialize arrays of such structs, where many cases in the array have some identical fields.
  • std::next() and std::prev(), to increment and decrement copies of iterators by some value; these can be useful with non-random access types such as std::list, or when writing templated code that deals with different kinds of containers.
  • The no-parameter form of std::less<>, a function object for doing comparisons which can avoid some unnecessary temporaries in e.g. heterogeneous lookup.
  • std::shrink_to_fit(), which makes a non-binding request that a container reduce its capacity to match its size.
  • Tuple addressing by type, which allows calling std::get<> with a type parameter instead of a numeric one when doing so is unambiguous.  This can be useful for improving the readability of code which conceptually wants to say "get the string from this tuple" instead of "get the third element".
  • std::cbegin()std::cend()std::crbegin(), and std::crend(), which are constant counterparts to std::begin() and friends (which work like begin() member functions but can operate on e.g. array types as well).
  • The alignas() specifier and alignof() operator, which allow setting and querying the alignment for types.  Note that there are some usage caveats around alignas(); see the doc.
  • Functionality in <atomic>, which provides low-level atomic transaction functionality.  Very few people should need to use this directly; prefer higher-level synchronization primitives.
  • std::integer_sequence<>, which represents a sequence of integers as a type.  This is useful for certain kinds of template programming that I think relate to variable numbers of arguments?  (I dunno, I've never used this.)
Additionally, there are some changes to recommended best practices:
  • Move constructors should be declared noexcept where possible, since despite Chromium compiling with exceptions disabled, this affects things like whether std::vector can move instead of copy these objects.
  • With the removal of libstdc++, various features (e.g. std::get, std::map::emplace, <type_traits>, etc.) no longer have usage caveats around library bugs.
Finally, some features have been banned:
  • std::chrono literals, which are banned since <chrono> is banned.
  • thread_local, to mark a variable as thread-local, due to some surprising effects.  This may be allowed again in the near future, however.
  • Function return type deduction, which allows a function to be declared as returning auto (without a trailing return type).  This can currently cause infinite loops in clang; once that bug is fixed, it will likely be allowed.
  • Generic (or "polymorphic") lambdas, which allow the use of auto in declaring lambda parameter types.  As with function return type deduction, this can cause clang bugs and may be allowed when those are fixed.
As always, to propose allowing/banning your favorite feature, send mail to c...@chromium.org.

PK

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
---
You received this message because you are subscribed to the Google Groups "Chromium-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-dev/CAAHOzFBPmS6Drun0dg5sy1Vw7W35rLc%2BF-oO32FZsPO0rGh7_Q%40mail.gmail.com.

K Moon

unread,
Sep 5, 2019, 2:36:20 PM9/5/19
to Nico Weber, Peter Kasting, cxx
That style guide section also says:

"You may use noexcept when it is useful for performance if it accurately reflects the intended semantics of your function, i.e. that if an exception is somehow thrown from within the function body then it represents a fatal error. You can assume that noexcept on move constructors has a meaningful performance benefit. If you think there is significant performance benefit from specifying noexcept on some other function, please discuss it with your project leads."

My two cents: I think the point here is that, for move constructors, you don't need to justify adding "noexcept": The style guide accepts that this is almost always useful. I think the debate for move constructors might be more between whether to have a move constructor at all, or just a copy constructor, not whether it should be noexcept.

You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAMGbLiEA7nVT1L2Gt1GkXLNAzmoCtuQJpcDdBpL3aW98c_RNvQ%40mail.gmail.com.

Nico Weber

unread,
Sep 5, 2019, 2:42:58 PM9/5/19
to K Moon, Peter Kasting, cxx
That's mostly for storing elements in stl containers though, right? https://reviews.llvm.org/D62228 makes that concern go away.

K Moon

unread,
Sep 5, 2019, 2:54:39 PM9/5/19
to Nico Weber, Peter Kasting, cxx
Yes, I think std::vector is the main example raised for why noexcept move constructors are a good thing.

Looks like D62228 was reverted recently because it caused copy-only types to stop working, but I assume a similar optimization would land at some point, so maybe it stops becoming relevant at that point for -fno-exceptions projects. I would expect noexcept move constructors to remain standard practice in the rest of the C++ world even then, though...


Peter Kasting

unread,
Sep 5, 2019, 4:01:26 PM9/5/19
to Nico Weber, cxx
On Thu, Sep 5, 2019 at 10:43 AM Nico Weber <tha...@chromium.org> wrote:
This is a reply to an 1.5 year old email, but there's currently a mass-clang-tidy rewrite to add "noexcept" to all move ctors. I pushed back on this and several people sent me a link to the email this is replying to, so I wanted to open this up for discussion.

This mail says "move ctors should be declared noexcept", but https://google.github.io/styleguide/cppguide.html#noexcept actually says "Specify noexcept when it is useful and correct." -- that sounds more like "it's ok to use this when it makes sense", not "you should use this".

I think doing a mass rewrite isn't a great idea, because:

- There's hope to one day have the same semantic effect automatically when building with -fno-exceptions (e.g. https://reviews.llvm.org/D62228), and at that point this is just busy work
- gcc and clang have different requirements for noexcept, and auto-adding this everywhere increases the cost of keeping the gcc build going
- it adds visual noise
- most of the time it has no benefit

It's fine to add noexcept in cases where it provides measurable perf benefits, but I don't think we should do a mass codebase rewrite. We also should follow the google style guide that says "use when useful and correct", not "should".

I echo K's response that the Google Style Guide intends to imply that "noexcept is always useful on move constructors", and thus this is always "useful and correct" for us.  I don't think D62228 moots this argument completely, since the optimization there does not apply to non-clang or if we ever enable exceptions (e.g. in a future C++XX world where someone has created a lighter-weight exception system).  It also does not help any other optimizations anyone writes now or in the future that check the noexcept state (this is my way of saying that I agree with the bug comment that it's wrong for upstream to narrowly solve this for std::vector only).  It also makes me uncomfortable to omit semantic information because of a clang- or libc++-only change; in principle I believe we should aim to avoid relying on implementation-specific behavior if it's feasible to do so.  The cross-platform, wider-C++-world way of achieving what you want is to mark move constructors noexcept.

I'm interested in the degree to which this concretely makes life harder for gcc.  Do we have move constructors that violate some of gcc's noexcept requirements?

I agree with you that this adds visual noise.  I don't think that's a significant problem; I assume we have comparatively few move constructors, so this won't hit many sites (in the grand scheme), and the noise seems no worse than e.g. marking a method const.

I would agree that "most of the time this has no benefit", except that I think that concern is levelled more appropriately at move constructors in the first place; if someone is adding one for good reason, then the case of storing such an object in a container is common enough that I think having a simpler guide ("make move constructors noexcept") is better than "do it when it's a perf win".  There is far too much complexity around perf in C++, even for common questions like "should I pass by value"; better not to add more.  That goes for both writing the code ("should I add this") and maintaining it ("should this have been added").

My summary would thus be: doing this unconditionally has small benefits and small costs; doing it only when you think it will help will have smaller benefits and lower codebase but higher cognitive costs.  So I think the unconditional rule is better.  I don't think it's better enough that I would have written a clang-tidy pass to mass-convert, but I don't object to such a pass either.

PK

Nico Weber

unread,
Sep 5, 2019, 4:17:20 PM9/5/19
to Peter Kasting, cxx
On Thu, Sep 5, 2019 at 4:01 PM 'Peter Kasting' via cxx <c...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 10:43 AM Nico Weber <tha...@chromium.org> wrote:
This is a reply to an 1.5 year old email, but there's currently a mass-clang-tidy rewrite to add "noexcept" to all move ctors. I pushed back on this and several people sent me a link to the email this is replying to, so I wanted to open this up for discussion.

This mail says "move ctors should be declared noexcept", but https://google.github.io/styleguide/cppguide.html#noexcept actually says "Specify noexcept when it is useful and correct." -- that sounds more like "it's ok to use this when it makes sense", not "you should use this".

I think doing a mass rewrite isn't a great idea, because:

- There's hope to one day have the same semantic effect automatically when building with -fno-exceptions (e.g. https://reviews.llvm.org/D62228), and at that point this is just busy work
- gcc and clang have different requirements for noexcept, and auto-adding this everywhere increases the cost of keeping the gcc build going
- it adds visual noise
- most of the time it has no benefit

It's fine to add noexcept in cases where it provides measurable perf benefits, but I don't think we should do a mass codebase rewrite. We also should follow the google style guide that says "use when useful and correct", not "should".

I echo K's response that the Google Style Guide intends to imply that "noexcept is always useful on move constructors", and thus this is always "useful and correct" for us.  I don't think D62228 moots this argument completely, since the optimization there does not apply to non-clang or if we ever enable exceptions (e.g. in a future C++XX world where someone has created a lighter-weight exception system).  It also does not help any other optimizations anyone writes now or in the future that check the noexcept state (this is my way of saying that I agree with the bug comment that it's wrong for upstream to narrowly solve this for std::vector only

(I agree with that too, but in the sense that I think we should find a way to make -fno-exceptions implicitly add noexcept on all functions in the compiler.)
 
).  It also makes me uncomfortable to omit semantic information because of a clang- or libc++-only change; in principle I believe we should aim to avoid relying on implementation-specific behavior if it's feasible to do so.  The cross-platform, wider-C++-world way of achieving what you want is to mark move constructors noexcept.

We're already not writing exception safe code -- which we shouldn't, since we don't need it and since it's more work.

If we ever want to turn on some hypothetical future exception mode, that will need lots of prep work and planning -- sprinkling "noexcept" over the code base would be one of the smaller work items. In fact, adding it now even makes that hypothetical future step harder -- if we mechanically add it everywhere now, that might no longer be true in a future where some of our code might throw.

Since there's no standard for "C++ without exceptions", relying on what compilers do seems the only thing we can do wrt exceptions.
 
I'm interested in the degree to which this concretely makes life harder for gcc.  Do we have move constructors that violate some of gcc's noexcept requirements?

There's a steady trickly of noexcept-related changes on https://crbug.com/819294

Peter Kasting

unread,
Sep 5, 2019, 4:34:32 PM9/5/19
to Nico Weber, cxx
On Thu, Sep 5, 2019 at 1:17 PM Nico Weber <tha...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 4:01 PM 'Peter Kasting' via cxx <c...@chromium.org> wrote:
I echo K's response that the Google Style Guide intends to imply that "noexcept is always useful on move constructors", and thus this is always "useful and correct" for us.  I don't think D62228 moots this argument completely, since the optimization there does not apply to non-clang or if we ever enable exceptions (e.g. in a future C++XX world where someone has created a lighter-weight exception system).  It also does not help any other optimizations anyone writes now or in the future that check the noexcept state (this is my way of saying that I agree with the bug comment that it's wrong for upstream to narrowly solve this for std::vector only

(I agree with that too, but in the sense that I think we should find a way to make -fno-exceptions implicitly add noexcept on all functions in the compiler.)

We think along the same lines; my thought was basically to make the noexcept operator always return true with -fno-exceptions.  But at a high level: yes, basically "-fno-exceptions should mean we can assume functions do not throw exceptions".

).  It also makes me uncomfortable to omit semantic information because of a clang- or libc++-only change; in principle I believe we should aim to avoid relying on implementation-specific behavior if it's feasible to do so.  The cross-platform, wider-C++-world way of achieving what you want is to mark move constructors noexcept.

We're already not writing exception safe code -- which we shouldn't, since we don't need it and since it's more work.

True, though with the caveat that some kinds of exception-safety also have other readability or correctness benefits.  (I'm a big proponent of "no bare new", for example.)

If we ever want to turn on some hypothetical future exception mode, that will need lots of prep work and planning -- sprinkling "noexcept" over the code base would be one of the smaller work items. In fact, adding it now even makes that hypothetical future step harder -- if we mechanically add it everywhere now, that might no longer be true in a future where some of our code might throw.

Since there's no standard for "C++ without exceptions", relying on what compilers do seems the only thing we can do wrt exceptions.

That's fair.  Maybe I can rephrase as "to the degree that we're able to rely on common, broadly-supported behaviors, we should prefer those over more-narrowly-supported alternatives".

I'm interested in the degree to which this concretely makes life harder for gcc.  Do we have move constructors that violate some of gcc's noexcept requirements?

There's a steady trickly of noexcept-related changes on https://crbug.com/819294

All but one of these look like cases where gcc's rule is "if your move constructor is noexcept and defaulted, your members must have noexcept move constructors", and thus the fix is to mark the member move constructors as noexcept as well.  That seems like (a) a reasonable change and (b) one that wouldn't be required if we did, in fact, globally declare move constructors noexcept (because that better supports the gcc build than "use opportunistically").

(The one remaining case was https://chromium-review.googlesource.com/c/chromium/src/+/1768419 , which I left a comment on.)

PK

Nico Weber

unread,
Sep 5, 2019, 4:45:25 PM9/5/19
to Peter Kasting, cxx
Thanks for the thought-through responses, Peter.

Would you still want to add noexcept if -fno-exceptions already had the "implicit noexcept" behavior?

Peter Kasting

unread,
Sep 5, 2019, 4:53:50 PM9/5/19
to Nico Weber, cxx
On Thu, Sep 5, 2019 at 1:45 PM Nico Weber <tha...@chromium.org> wrote:
Thanks for the thought-through responses, Peter.

Would you still want to add noexcept if -fno-exceptions already had the "implicit noexcept" behavior?

Probably, because that's still a clang-only sort of thing.  Maybe not if all compilers did this.  I know the standard considers "no exceptions" mode anathema, but if it ever laid out some guidelines for a "no exceptions" mode and this was one of them, that'd immediately convince me to remove all "noexcept"s from our codebase :)

I am also not going to die on this hill.  When the benefits and costs here are this small, I am happy to go with a consensus decision either way.

PK

Nico Weber

unread,
Sep 5, 2019, 5:04:06 PM9/5/19
to Peter Kasting, cxx
On Thu, Sep 5, 2019 at 4:53 PM 'Peter Kasting' via cxx <c...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 1:45 PM Nico Weber <tha...@chromium.org> wrote:
Thanks for the thought-through responses, Peter.

Would you still want to add noexcept if -fno-exceptions already had the "implicit noexcept" behavior?

Probably, because that's still a clang-only sort of thing.  Maybe not if all compilers did this.

"All" being gcc and msvc, or more?

If we ever implement this in clang, I think it's more likely than not that gcc grows a similar feature. MSVC doesn't really have a -fno-exceptions mode.
 
  I know the standard considers "no exceptions" mode anathema, but if it ever laid out some guidelines for a "no exceptions" mode and this was one of them, that'd immediately convince me to remove all "noexcept"s from our codebase :)

I am also not going to die on this hill.  When the benefits and costs here are this small, I am happy to go with a consensus decision either way.

I'm also happy to withdraw my concern if general agreement is that we want explicit noexcepts on move ctors.

Chris Blume

unread,
Sep 5, 2019, 5:18:56 PM9/5/19
to Nico Weber, Peter Kasting, cxx
A nitpicky point of clarification:
-fno-except doesn't mean there are no exceptions.

Imagine statically linking a prebuilt library. Even if we build our code with -fno-except, the library might still throw.
If we (implicitly) mark our function as noexcept then our call into the library will now have a compiler-added catch-all which calls std::terminate.

Implicitly marking functions as noexcept is a change in behavior. I doubt compilers will do this.
I realize in Chrome we compile everything and won't be statically linking to throwing libraries. But we are still beholden to the compiler which can't change behavior for us.

Likewise, implicitly returning true from the noexcept operator is a change in behavior (see std::vector). So I doubt compilers will do that as well.


With all of that said, the only way to not rely on compilers changing our code's behavior (a dangerous notion) is for us to explicitly tell the compiler a function is indeed noexcept. Said another way, noexcept != -fno-except -- they are different tools that accomplish different things.

Chris Blume |
 Software Engineer | cbl...@google.com | +1-614-929-9221


--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.

Nico Weber

unread,
Sep 5, 2019, 5:27:41 PM9/5/19
to Chris Blume, Peter Kasting, cxx
On Thu, Sep 5, 2019 at 5:18 PM 'Chris Blume' via cxx <c...@chromium.org> wrote:
A nitpicky point of clarification:
-fno-except doesn't mean there are no exceptions.

Imagine statically linking a prebuilt library. Even if we build our code with -fno-except, the library might still throw.
If we (implicitly) mark our function as noexcept then our call into the library will now have a compiler-added catch-all which calls std::terminate.

Implicitly marking functions as noexcept is a change in behavior. I doubt compilers will do this.
I realize in Chrome we compile everything and won't be statically linking to throwing libraries. But we are still beholden to the compiler which can't change behavior for us.

Likewise, implicitly returning true from the noexcept operator is a change in behavior (see std::vector). So I doubt compilers will do that as well.

It wouldn't happen by default, but Chromium isn't the only program that ships more or less everything built from source and that only links against C ABI system libraries. If there's a demonstrable benefit, I think this would be a useful feature for many projects.
 
With all of that said, the only way to not rely on compilers changing our code's behavior (a dangerous notion

(We use all kinds of flags that change behavior! There's -fno-exceptions -fno-rtti, but also a long tail of smaller things: We pass -fmerge-all-constants which is even non-conforming in a strict sense. We pass fno-strict-aliasing which disables a technically valid optimization that we're afraid if. We should probably be passing -fno-delete-null-ptr-checks to disable another technically valid but surprising and arguably dangerous optimimzation -- etc.)
 
) is for us to explicitly tell the compiler a function is indeed noexcept. Said another way, noexcept != -fno-except -- they are different tools that accomplish different things.

Chris Blume |
 Software Engineer | cbl...@google.com | +1-614-929-9221


On Thu, Sep 5, 2019 at 2:04 PM Nico Weber <tha...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 4:53 PM 'Peter Kasting' via cxx <c...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 1:45 PM Nico Weber <tha...@chromium.org> wrote:
Thanks for the thought-through responses, Peter.

Would you still want to add noexcept if -fno-exceptions already had the "implicit noexcept" behavior?

Probably, because that's still a clang-only sort of thing.  Maybe not if all compilers did this.

"All" being gcc and msvc, or more?

If we ever implement this in clang, I think it's more likely than not that gcc grows a similar feature. MSVC doesn't really have a -fno-exceptions mode.
 
  I know the standard considers "no exceptions" mode anathema, but if it ever laid out some guidelines for a "no exceptions" mode and this was one of them, that'd immediately convince me to remove all "noexcept"s from our codebase :)

I am also not going to die on this hill.  When the benefits and costs here are this small, I am happy to go with a consensus decision either way.

I'm also happy to withdraw my concern if general agreement is that we want explicit noexcepts on move ctors.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAMGbLiEHK8itak%2Bc0ZnmaW7Hn%3DDHrdVrK9UJ%3D%3DB7n4UQMiHjXQ%40mail.gmail.com.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.

Peter Kasting

unread,
Sep 5, 2019, 5:35:10 PM9/5/19
to Chris Blume, Nico Weber, cxx
On Thu, Sep 5, 2019 at 2:18 PM Chris Blume <cbl...@google.com> wrote:
Imagine statically linking a prebuilt library. Even if we build our code with -fno-except, the library might still throw.
If we (implicitly) mark our function as noexcept then our call into the library will now have a compiler-added catch-all which calls std::terminate.

FWIW, this is why my proposal was to change what the noexcept operator returns instead of marking functions noexcept -- I didn't want to add any catches which could reduce perf.

Likewise, implicitly returning true from the noexcept operator is a change in behavior (see std::vector). So I doubt compilers will do that as well.

The change in behavior is the desired outcome here, so whether compilers would do this seems to map back to the question of whether the general outcome ("assume -fo-nexceptions means nothing throws") is one compilers want to achieve.

However, I wouldn't assume anything like this will happen, either soon or later.  It'd be nice; better not to count on it.

PK

Peter Kasting

unread,
Sep 5, 2019, 5:43:13 PM9/5/19
to Nico Weber, cxx
On Thu, Sep 5, 2019 at 2:04 PM Nico Weber <tha...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 4:53 PM 'Peter Kasting' via cxx <c...@chromium.org> wrote:
On Thu, Sep 5, 2019 at 1:45 PM Nico Weber <tha...@chromium.org> wrote:
Thanks for the thought-through responses, Peter.

Would you still want to add noexcept if -fno-exceptions already had the "implicit noexcept" behavior?

Probably, because that's still a clang-only sort of thing.  Maybe not if all compilers did this.

"All" being gcc and msvc, or more?

Well, I'd really like "all compilers that have a concept of disabling exception support", native x86 or not... I know the Green Hills C++ compiler could, and I don't know about icc, or the variety of embedded toolchains out there... I will freely admit that from a pragmatic perspective, "clang/gcc/msvc" is good enough for Chromium, but then again, from a pragmatic perspective, "clang only" is good enough for (supported) Chromium, so if I were pragmatically inclined, I'd be happy removing noexcept as soon as some sort of change for this lands in clang or libc++.  Part of not wanting to die on this hill is the knowledge that much of my concern is concern-in-principle-but-with-no-practical-effect-on-Chromium.

If we ever implement this in clang, I think it's more likely than not that gcc grows a similar feature. MSVC doesn't really have a -fno-exceptions mode.

Oh?  https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.vcprojectengine.cppexceptionhandling?view=visualstudiosdk-2017 suggests that "no exception handling" is a mode, and https://docs.microsoft.com/en-us/cpp/build/reference/gx-enable-exception-handling?view=vs-2019 seems to say that if you don't pass an /EHsomething switch on a command-line build, you get no exception handling by default.

PK

Chris Blume

unread,
Sep 5, 2019, 5:58:54 PM9/5/19
to Peter Kasting, Nico Weber, cxx
I agree that it would typically be the desired outcome.
I don't think anyone passes -fno-except and actually wants the added catch-all or the "but what if it throws?" std::vector behavior.

You're right that a lot of flags change code's behavior. But in this case it would change the behavior between GCC vCurrent and GCC vNext. Upgrading the compiler would silently break people's code in unexpected ways. So while I doubt -fno-except is going to change, they could add a new flag -fseriously-no-except. I've been waiting on -fseriously-no-except for ...decades now?


IIUC this leaves us at:
  • adding noexcept all over is noisy
  • there is no flag available for what we want
    • except std::vector might be changing soon anyway

I think I am in a similar boat with Peter. My gut wants to say "be explicit about what the code should do, add noexcept specifiers". But in the case of Chrome, this is 100% to satisfy std::vector. I would be willing to wait for std::vector to change, despite what my gut says.


Chris Blume |
 Software Engineer | cbl...@google.com | +1-614-929-9221

Yuta Kitamura

unread,
Sep 6, 2019, 2:39:34 AM9/6/19
to Chris Blume, Peter Kasting, Nico Weber, cxx
I mostly agree with Peter, based on my observation that noexcept for move constructors is like explicit for one-argument constructors (to prevent implicit conversions). They are both workarounds that are needed to address the language standard's cavert. Having to put those keywords to every constructor is unfortunate, yes. Those keywords clutter the source code, yes. But I don't think they are so bad that everyone would go mad about them, given that we live in a world with a lot of explicits sprinkled over the code base.

It'd be nice if we could have a clang plugin that warns the lack of required noexcept.


--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.

lo...@chromium.org

unread,
Sep 6, 2019, 3:42:47 AM9/6/19
to cxx, cbl...@google.com, pkas...@google.com, tha...@chromium.org
Hi, all!

Strong +1 for "we should find a way to make -fno-exceptions implicitly add noexcept on all functions in the compiler"
(up to adding -fseriously-no-except).

I think, "Chromium doesn't deal with exceptions and stack unwinding" is very nice and simple pragmatic model.
That's not new: most interactive multithreaded/multiprocess applications don't want to deal with standard C++ exception mechanisms
(3d games, VR etc).

I still remember all that background with _HAS_EXCEPTIONS macro (MSVC).

It would be super unfortunate if exceptions (or related terms and concepts) would be leaking into our codebase. 
(Imagine that the "Exceptional C++" book becomes applicable to chromium or google3 codebase. Brrr.).

Alexey.

Yuta Kitamura

unread,
Sep 6, 2019, 5:16:33 AM9/6/19
to lo...@chromium.org, cxx, Chris Blume, Peter Kasting, Nico Weber
On Fri, Sep 6, 2019 at 4:42 PM <lo...@chromium.org> wrote:
Hi, all!

Strong +1 for "we should find a way to make -fno-exceptions implicitly add noexcept on all functions in the compiler"
(up to adding -fseriously-no-except).

I think someone has already mentioned this somewhere, but let me explain in my words why adding such an option is difficult and controversial.

Normally, object files generated with different compile options should be able to get linked happily. The auto-noexcept option will give you the different semantics of C++ code relying std::is_nothrow_constructible and similar, which are actually used in many std code. Introduction of the option would make the semantics of the same function differ in different translation units. This is very bad, and linking such object files result undefined behavior -- practically, the linker may arbitrarily throw away object code from some translation units, or the final binary may contain multiple instances of inconsistent code that comes from the same function (due to inlines).

So, if you want to use that option, you basically need to compile all files with the flag set. However this is generally impractical, because you at least need to link standard C++ library to your binary, and the standard library is usually built with exceptions enabled. This also means you can't use any dynamic link library (that uses C++) available in your system.

We, however, compile almost all the code by ourselves (modulo external DLLs), so this might be solvable. However, from compiler developers' perspective, the option makes no sense. Almost nobody uses custom-built standard library. The option will result undefined behavior more than 99% of the time. Adding such an option would give you an endless queue of bugs from people who have innocently used that option.

Thus, simply adding the auto-noexcept flag is a big no-no. To overcome this issue, I think someone smart needs to come up with the ABI (on every platform!) that can accommodate object code coming from both settings, but I doubt that's practically possible.

Nico Weber

unread,
Sep 6, 2019, 12:56:21 PM9/6/19
to Yuta Kitamura, Alexey, cxx, Chris Blume, Peter Kasting
On Fri, Sep 6, 2019 at 5:16 AM Yuta Kitamura <yu...@chromium.org> wrote:


On Fri, Sep 6, 2019 at 4:42 PM <lo...@chromium.org> wrote:
Hi, all!

Strong +1 for "we should find a way to make -fno-exceptions implicitly add noexcept on all functions in the compiler"
(up to adding -fseriously-no-except).

I think someone has already mentioned this somewhere, but let me explain in my words why adding such an option is difficult and controversial.

Normally, object files generated with different compile options should be able to get linked happily. The auto-noexcept option will give you the different semantics of C++ code relying std::is_nothrow_constructible and similar, which are actually used in many std code. Introduction of the option would make the semantics of the same function differ in different translation units. This is very bad, and linking such object files result undefined behavior -- practically, the linker may arbitrarily throw away object code from some translation units, or the final binary may contain multiple instances of inconsistent code that comes from the same function (due to inlines).

So, if you want to use that option, you basically need to compile all files with the flag set. However this is generally impractical, because you at least need to link standard C++ library to your binary, and the standard library is usually built with exceptions enabled. This also means you can't use any dynamic link library (that uses C++) available in your system.

We, however, compile almost all the code by ourselves (modulo external DLLs), so this might be solvable. However, from compiler developers' perspective, the option makes no sense.

Just to be explicit about this, #7 #24 #25 #26 #32 #40 on https://github.com/llvm/llvm-project/graphs/contributors work directly on Chromium, with several others working indirectly on it. If something is useful to Chromium and seems like it would be useful for many others, we're likely to implement it :) (Eventually -- like for everyone else, there's always more work than time.)
 
Almost nobody uses custom-built standard library. The option will result undefined behavior more than 99% of the time. Adding such an option would give you an endless queue of bugs from people who have innocently used that option.

Thus, simply adding the auto-noexcept flag is a big no-no. To overcome this issue, I think someone smart needs to come up with the ABI (on every platform!) that can accommodate object code coming from both settings, but I doubt that's practically possible.

This is all very good advice in general.

It does not apply to Chromium. We set several flags that make it so that our object files make ABI assumptions. The biggest example is probably that we set _LIBCPP_ABI_UNSTABLE which makes it so that our libc++ isn't ABI-compatible with the ABI used by current stable libc++. (You could argue that this only affects the ABI of the standard library and not of the compiler, but that's in my opinion merely an academic difference: Any object files that use std::string have to built with the same compiler flags to be able to interoperate).

I agree that building the standard library isn't super common. However, with C++ releasing new language versions fairly frequently nowadays, it's fairly common to statically link the C++ standard library, and from there it's only a fairly small and surprisingly easy to step to statically link a self-built standard library. (clang even has a dedicated flag to make that a bit easier, -nostdlib++).

And I'm guessing most cloud providers are happy to make this tradeoff as well :)

Greg Thompson

unread,
Mar 8, 2020, 5:11:50 PM3/8/20
to Chromium-dev, pkas...@google.com, c...@chromium.org
Hi Nico. My understanding was that a move-only type required noexcept for it to be used in a vector (whereas an optional optimization path could be taken if the contained type was both copyable and movable w/noexcept). Is this correct? If so, it seems simpler to put noexcept on move-only types so that other folks don't have to come along later and add it when they want to use them in a std container. Wdyt?

Jan Wilken Dörrie

unread,
Mar 9, 2020, 3:58:06 AM3/9/20
to Greg Thompson, Chromium-dev, Peter Kasting, cxx
This understanding is not quite correct. std::vector commonly uses std::move_if_noexcept during its resize operation, which is a copy if the value (!is_nothrow_move_constructible && is_copy_constructible), and a move otherwise.

In particular this means the move constructor of both copyable and moveable should be marked noexcept, as otherwise vector will copy. If the type is move-only to begin with, std::vector will always move, as it can't copy anyway. noexcept doesn't hurt of course, which is why it usually is recommended on move-constructors. The style guide mentions this as well.

Best,
Jan

You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.

Greg Thompson

unread,
Mar 9, 2020, 5:24:33 AM3/9/20
to Jan Wilken Dörrie, Chromium-dev, Peter Kasting, cxx
On Mon, Mar 9, 2020 at 8:58 AM Jan Wilken Dörrie <jdoe...@chromium.org> wrote:
This understanding is not quite correct. std::vector commonly uses std::move_if_noexcept during its resize operation, which is a copy if the value (!is_nothrow_move_constructible && is_copy_constructible), and a move otherwise.

I was asking about move-only types, where falling back to a copy isn't an option. Does this not mean that a move-only type can only be stuffed into a vector if it can be moved (via move assign or move construct) without an exception being thrown? Or does vector have some other path where it'll do the move and let exceptions fly?

In particular this means the move constructor of both copyable and moveable should be marked noexcept, as otherwise vector will copy.

This is where the style guide makes things muddy: "You may use noexcept when it is useful for performance if it accurately reflects the intended semantics of your function". Where do we draw the line? Some (not me) might say "don't use noexcept unless you can demonstrate a performance hit by not using it."

But wait! I just noticed that the style guide follows that with "You can assume that noexcept on move constructors has a meaningful performance benefit." I read that as "always put noexcept on move ctors as long as it's actually correct." Should we not be doing this? If no, why not?

Jan Wilken Dörrie

unread,
Mar 9, 2020, 5:57:01 AM3/9/20
to Greg Thompson, Chromium-dev, Peter Kasting, cxx
On Mon, Mar 9, 2020 at 10:24 AM Greg Thompson <g...@chromium.org> wrote:
On Mon, Mar 9, 2020 at 8:58 AM Jan Wilken Dörrie <jdoe...@chromium.org> wrote:
This understanding is not quite correct. std::vector commonly uses std::move_if_noexcept during its resize operation, which is a copy if the value (!is_nothrow_move_constructible && is_copy_constructible), and a move otherwise.

I was asking about move-only types, where falling back to a copy isn't an option. Does this not mean that a move-only type can only be stuffed into a vector if it can be moved (via move assign or move construct) without an exception being thrown? Or does vector have some other path where it'll do the move and let exceptions fly?

If you put a move-only type in a vector and the move constructor is not marked noexcept std::vector::resize() will move, but waive its strong exception guarantee (this is mentioned in the notes of the linked article).  

In particular this means the move constructor of both copyable and moveable should be marked noexcept, as otherwise vector will copy.

This is where the style guide makes things muddy: "You may use noexcept when it is useful for performance if it accurately reflects the intended semantics of your function". Where do we draw the line? Some (not me) might say "don't use noexcept unless you can demonstrate a performance hit by not using it."

But wait! I just noticed that the style guide follows that with "You can assume that noexcept on move constructors has a meaningful performance benefit." I read that as "always put noexcept on move ctors as long as it's actually correct." Should we not be doing this? If no, why not?

Given that Chrome does not throw exceptions noexcept technically is always correct. I suppose so far we have refrained from a blanket "put noexcept on every move c-tor" because in most cases it is visual noise without much benefit. IIUC it only makes a difference when std::move_is_noexcept is used, and the type is also copyable. So far I only know of std::vector::resize making use of std::move_is_noexcept, so if you don't put your type in a vector you don't gain much.

However, back in the day the style guide wasn't as explicit in recommending noexcept move ctors, so maybe we should change our stance here.

Best,
Jan

Greg Thompson

unread,
Mar 9, 2020, 11:48:52 AM3/9/20
to Peter Kasting, Jan Wilken Dörrie, Chromium-dev, cxx
Now that the style guide is explicitly saying to do it, I think we should unconditionally add them to move ctors for new code where it's correct. This seems in keeping with our general "try not to deviate from Google style" guidance. I don't think this alone requires a mass refactor of existing code, though that would be nice to have.

On Mon, Mar 9, 2020 at 4:21 PM Peter Kasting <pkas...@google.com> wrote:
Note that your final paragraph has circled around to the original cause of this thread: a mass rewrite to do this everywhere and a debate over wherever we should be doing it. Nico makes the case for "why not". My position is still that we should do this everywhere.

PK

K. Moon

unread,
Mar 9, 2020, 12:00:30 PM3/9/20
to Greg Thompson, Peter Kasting, Jan Wilken Dörrie, Chromium-dev, cxx
Due to the lack of exceptions in Chromium, I agree that there's no practical effect (I believe the compiler will do the right thing when exceptions are turned off), so this seems purely a matter of consistency. On that basis, it seems like a good practice to adopt, but it's not urgent or anything.

Greg Thompson

unread,
Mar 9, 2020, 12:22:16 PM3/9/20
to K. Moon, Peter Kasting, Jan Wilken Dörrie, Chromium-dev, cxx
I seem to recall getting a compile error when trying to make a vector of a move-only type that was missing noexcept. Is my memory faulty?

Peter Kasting

unread,
Mar 9, 2020, 12:39:05 PM3/9/20
to K. Moon, Greg Thompson, Jan Wilken Dörrie, Chromium-dev, cxx
On Mon, Mar 9, 2020 at 9:00 AM K. Moon <km...@chromium.org> wrote:
Due to the lack of exceptions in Chromium, I agree that there's no practical effect (I believe the compiler will do the right thing when exceptions are turned off), so this seems purely a matter of consistency. On that basis, it seems like a good practice to adopt, but it's not urgent or anything.

Note that gcc (unlike clang) will fail to compile if you mark a move constructor noexcept when its implementation requires moving members whose move constructors are not themselves (recursively) noexcept.  So to the degree that there is still a community-supported gcc build, we need to be somewhat intentional when adding these (to either add them to whole hierarchies at once, or nowhere).

I think gcc's behavior is better than clang's here, honestly.

PK 

K. Moon

unread,
Mar 9, 2020, 3:45:23 PM3/9/20
to Peter Kasting, Greg Thompson, Jan Wilken Dörrie, Chromium-dev, cxx
I threw together a Compiler Explorer link if anyone wants to play around with this: https://godbolt.org/z/IBvZTn

At least for my example, and with x86-64 clang-trunk, noexcept makes a difference with exceptions (as you'd expect), and makes no difference with -fno-exceptions (also as you'd expect, hopefully).

I poked around a bit with the gcc vs. clang behavior, but wasn't able to trigger the compilation error; I'm assuming I didn't hit the right set of conditions to trigger it.


José Dapena Paz

unread,
Mar 9, 2020, 4:49:33 PM3/9/20
to km...@chromium.org, Peter Kasting, Greg Thompson, Jan Wilken Dörrie, Chromium-dev, cxx
El lun, 09-03-2020 a las 12:45 -0700, K. Moon escribió:
I threw together a Compiler Explorer link if anyone wants to play around with this: https://godbolt.org/z/IBvZTn

At least for my example, and with x86-64 clang-trunk, noexcept makes a difference with exceptions (as you'd expect), and makes no difference with -fno-exceptions (also as you'd expect, hopefully).

I poked around a bit with the gcc vs. clang behavior, but wasn't able to trigger the compilation error; I'm assuming I didn't hit the right set of conditions to trigger it.

GCC always fails when you have a noexcept operator using default implementation, if any of the members operator used for default implementation is not noexcept. No matter if -fno-exception is set. But Clang will not throw those errors if -fno-exception is set.

In this case, I think GCC behavior is right: on code itself, using noexcept on an operator that requires parts that are noexcept is not allowed. It would work on Clang because -fno-exception is passed, but it is still invalid code. If we want to convert something to use noexcept, we really need to make sure it can be really noexcept. Ideally I would like Clang to also report that as an error, and that would definitely help with GCC-Clang compatibility.

Reply all
Reply to author
Forward
0 new messages