[RFC] Use language extensions from future standards in libc++

In libc++ we currently don’t use any extensions from future standards other than for implementing the extended C++03 support. I’m proposing that we start using these extensions in libc++ to make the code base simpler, and thus easier to maintain (and in a few cases even a bit faster to compile). This would allow us to use

  • variable templates
  • binary literals
  • fold expressions
  • structured bindings
  • inline variables
  • conditional explicit

and probably more.

Some of these extensions don’t make much of a difference, but others can have a very large one. e.g. using conditional explicit would cut the number of constructor overloads for tuple and pair almost in half.

Some of the drawbacks of using these extensions are

  • less portability to new compilers
  • potentially user-visible changes
  • relying on compiler extensions that could be removed in the future
  • this could affect people who build libc++ with a not officially supported compiler

Less portability to new compilers
This is a drawback, but I am not convinced that using these extensions is a big problem for this.

  1. We already rely on a lot of builtins which would have to be ported to a new compiler.
  2. Conforming extensions from future standards are quite likely to be implemented by many compilers. Most of these features are backported by all major compilers, which makes this a non-problem for the most part.
  3. most new compilers are clang-based, which means they inherit the clang extensions. For completely new compilers, it is likely that they will implement many of the extensions from other compilers to allow them to compile many projects without the need to modify the software. This is already the case with clang, which implements a lot of the GNU and MSVC extensions.
  4. It is also possible to only support newer language standards with new compilers.

Potentially user-visible changes
This is very unlikely given the nature of these extensions. Most of these extensions would be used for implementation details, and not for the API. The only one where that’s not the case is conditional explicit. Given that the standard describes tuple and pair with an exposition-only macro EXPLICIT(condition), I don’t think there are many pitfall for this. Still, we would have to be a bit more cautious with that one.

Relying on compiler extensions that could be removed in the future
At least according to @AaronBallman (please point out anything that I get wrong!), this is very unlikely to happen. Basically, if we want to use anything that is not documented, we should ask the clang folks if the extension is known and make them document it. If it’s documented, it’s considered very unlikely to be removed at any point. Compilers are a lot less likely to remove extensions than standard libraries are, because having these kinds of extensions is for the most part the difference between a conditional warning (informing the user that a given construct is an extension) and a conditional error (informing the user that the feature they are trying to use is not available). This makes these extensions very low maintenance for compilers.

this could affect people who build libc++ with a not officially supported compiler
Possible, but unlikely IMO. As already said above, all major implementations already support most of the extension listed above. If you use an unsupported compiler, please comment here, and maybe consider upstreaming support for it, so we know which compilers are used with libc++ and can react to any incompatibilities.

CC @ldionne @mordante @varconst @joeloser @EricWF @crtrott @AaronBallman

1 Like

Edit: This was sent before I was done!

IMO this is a reasonable requirement. If someone is bringing up a new toolchain (some compiler + libc++), it is probably reasonable to require that they only use C++ >= “some version”. This is actually already the case: we only support >= C++11 with GCC. The only compiler where we support all standard modes is Clang.

I agree, but we might want to think about a way to ensure that we don’t start using extensions in a way that makes us non-conforming in older standard modes. FWIW, the intent of using these extensions would be that they are strictly non-observable by end users.

All in all, I support this because I see how much simplification we could achieve in the code base by adopting a few extensions more widely. This would result in a better library (e.g. better compile-times, fewer bugs, less arcane implementation) and it’s not something anybody would be able to observe. However, I think we should be careful and introduce the use of these extensions slowly just to make sure we don’t open the floodgates and screw some things up. Doing it slowly will give us time to learn from any mistakes we might do (which might not be possible to foresee right now).

So +1 from me, definitely. There’s huge benefits, basically no cost, and no negative impact to users.

If this approach materially helps libc++ maintainers, I’m in favor of it.

Once we intentionally expose something as an extension, we’re basically promising we’re going to support it forever (modulo major issues with the extension that cannot otherwise be resolved). However, we’ve historically been extremely bad at explicitly documenting extensions that we intend to expose. That’s why there’s a recommendation to ask us about the extension if you’ve not spotted docs for it – that helps us be thoughtful about the extension and improves the compiler documentation at the same time as helping the libc++ folks.

1 Like

Thanks for @ldionne to point me to this post.

Have you spoken with the GCC devs too? At least for Clang we the latest HEAD in the libc++ CI and update to snapshort every few weeks. For GCC we update only when they release, so there the breakage might be detected later. I noticed GCC-13.1 can’t be used with libc++ HEAD anymore. I haven’t had time to look into it closely, but it seems to be related to compiler builtins. As mentioned earlier, I really like to know the stance in this regard from the GCC devs. That would really help me to make an informed decision.

Personally think there is a difference between using compiler builtins and relying on undocumented extensions. The builtins are usually documented. Implementing certain library features can’t be done purely from the language. For example, certain type traits require information the compiler has. For optional builtins we typically have a library path to do it.Even when we remove it at some point it can be easily revived from the history.

We’ve talked about this during the libc++ meeting and have agreed to allow specific extensions for now. Specifically

  • conditional explicit: This simplifies the overload sets of tuple and pair significantly, making the implementation hundreds of lines shorter.
  • if constexpr: This makes code a lot simpler that has to be different based on compile-time information. This can also improve compile times and runtime performance of unoptimized builds, since we can replace some overload sets with it.
  • variable templates and inline variables: This can improve compile times by reducing the amount of work the compiler has to do for library-internal metafunctions.

We’ll start to use these throughout the library after the next release if there are no problems with the test we have in this release.

1 Like