RFC: Raise the minimum compiler requirements to move toward C++20

Current Proposed Release Date
clang 5.0 15 2022-09-06
XCode 10.0 14.3 2023-03-30
gcc 7.4 12.2 2022-08-19
msvc 2019 16.8 (1928 _MSC_VER) 2022 17.14 (1944 _MSC_VER) 2022-11-08

We have a general guideline to support building the llvm project with clang and gcc from at least 3 years ago. These versions are the newest versions of clang and gcc that meet that requirement. The XCode version was chosen to match that clang version. There is also an informal guideline of supporting the two most recent versions of msvc, which are 2022 and 2026.

Linux Distributions

This shows the versions of gcc and clang available through the package manager in various distributions. The first version number is the default, later version numbers are available through other packages such as clang-20.

Linux distribution GCC Version Clang Version Release Date Support Status
Ubuntu 18.04 LTS – Bionic Beaver 7.4 10 2018-04-26 Extended Security Maintenance
Ubuntu 20.04 LTS – Focal Fossa 10 10 2020-04-21 Extended Security Maintenance
Ubuntu 22.04 LTS – Jammy Jellyfish 11.2.0, 12.3 14.0, 15.0.7 2022-04-21 Full support
Ubuntu 24.04 LTS – Noble Numbat 13.2.0 18.1.3, 19.1.1, 20.1.2 2024-04-25 Full support
Ubuntu 25.04 – Plucky Puffin 14.2.0 20.1.2 2025-04-17 Full support
Ubuntu 25.10 – Questing Quokka 15.2.0 20.1.2, 21.1.2 2025-10-09 Full support
RHEL 7 4.8, ??? 7.0.1, ??? 2014-06-10 Extended Life Cycle Support
RHEL 8 8, 9, 10, 11, 12, 13, 14, 15 19.1.7 2019-05-07 Maintenance Support
RHEL 9 11, 12, 13, 14, 15 19.1.7 2022-05-18 Full support
RHEL 10 14.2, 15.1 19.1.7 2025-05-20 Full support
Debian 11 Bullseye 10.2.1 11.0.1, 13.0.1, 16.0.6, 19.1.7 2021-08-14 LTS
Debian 12 Bookworm 12.2.0 14.0.6, 15.0.6, 16.0.6, 19.1.7 2023-06-10 Full support
Debian 13 Trixie 14.2.0 19.0, 19.1.7 2025-08-09 Full support
SUSE Enterprise Linux 12.5 4.8, 10, 14.2 3.4, ??? 2019-12-09 Long Term Service Pack Support
SUSE Enterprise Linux 15.7 7.5.0, 12.3.0, 13.3.1, 14.2.0 14.0.6, 15.0.7, 17.0.6, 19.1.7 2025-06-17 Full support

What does this get us

This is mostly sourced from the C++17 compatibility chart and the compatibility chart for all newer versions of C++. For this section, I am mostly just copying over the full set of things as listed in the table without judgment over which of them will be used.

C++17 Language

  • Replacement of class objects containing reference members

C++17 Standard Library

  • C11 standard library
  • Splicing maps and sets
  • std::has_unique_object_representations
  • std::launder
  • Some of std::to_chars and std::from_chars
  • Array support for std::shared_ptr and std::weak_ptr
  • constexpr std::char_traits
  • std::filesystem
  • Partial support for hardware interference size

C++20 Language

  • Allow lambda capture [=, this]
  • __VA_OPT__
  • Full support for designated initializers
  • Explicit template parameters on lambdas
  • Default member initializers for bit fields
  • Initializer list constructors work with CTAD
  • const & pointer to member
  • Concepts
  • Partial support for lambdas in unevaluated contexts
  • <=>
  • Defaulted operator== and implicit operator!=
  • init statement in range-based for
  • default constructible and assignable stateless lambdas
  • Partial support for const mismatch in defaulted special member functions
  • Access checking on specializations
  • ADL and function templates that are not visible
  • Specify when constexpr function definitions are needed for constant evaluation
  • [[likely]] and [[unlikely]]
  • Pack expansion in lambda init-capture
  • [[no_unique_address]]
  • Relaxing structured bindings customization point rules
  • Relaxing range-based for loop customization point finding rules
  • Allow structured bindings to accessible members
  • Destroying operator delete
  • Partial support for class types as non-type template parameters
  • Deprecate implicit capture of this via [=]
  • explicit(bool)
  • Prohibit aggregates with user-declared constructors
  • constexpr virtual functions
  • char8_t
  • std::is_constant_evaluated
  • constexpr for try and catch
  • Partial support for consteval
  • Nested inline namespaces
  • Signed integers are two’s complement
  • dynamic_cast and polymorphic typeid in constant expressions
  • Changing the active member of a union in constexpr
  • Array size deduction in new expressions
  • Explicitly defaulted functions with different exception specifications
  • Partial support for lambda capture of structured bindings
  • Conversions to arrays of unknown bound
  • constexpr allocation
  • Deprecate some uses of volatile
  • constinit
  • Deprecate comma operator in subscripts
  • [[nodiscard]] with message
  • Trivial default initialization in constexpr
  • Unevaluated asm in constexpr
  • using enum
  • [[nodiscard]] for constructors
  • More implicit moves
  • Pseudo-destructors end object lifetimes
  • T * to bool is a narrowing conversion

C++20 Library

  • std::endian
  • std::make_shared for arrays
  • constexpr for <algorithm>, <utility>, and some of <complex>
  • std::memory_order is a scoped enum
  • starts_with and ends_with for strings
  • Partial library support for <=>
  • std::remove_cvref
  • Use of std::move in numeric algorithms
  • Utility to convert a pointer to a raw pointer
  • std::span
  • <version>
  • ConstexprIterator requirements
  • std::string::reserve should not shrink
  • map contains
  • Guaranteed copy elision for piecewise construction
  • std::bit_cast
  • Integral power-of-two operations
  • Improving the return type of erase algorithms
  • std::is_convertible
  • std::shift_left and std::shift_right
  • constexpr for swap
  • std::type_identity
  • Concepts library
  • constexpr comparisons for std::array
  • std::unwrap_ref_decay and std::unwrap_reference
  • std::bind_front
  • std::reference_wrapper<incomplete type>
  • std::variant and std::optional propagate triviality of special member functions
  • A sane std::variant converting constructor
  • Ranges
  • Heterogeneous lookup for ordered associative containers
  • More noexcept in <chrono>
  • constexpr in pointer_traits
  • std::assume_aligned
  • More miscellaneous constexpr
  • std::erase and std::erase_if
  • std::lerp and std::midpoint
  • Make std::create_directory intuitive
  • std::ssize
  • Traits for arrays
  • std::to_array
  • Bit operations
  • Math constants
  • constexpr for std::allocator
  • constexpr for std::string
  • constexpr for std::vector
  • constexpr for std::invoke
  • Atomic waiting and notifying
  • constexpr default constructor for std::atomic and std::atomic_flag
  • constexpr numeric algorithms
  • Safe integral comparisons

C++23 Language

  • Literal suffix for size_t
  • Make () more optional in lambdas
  • if consteval
  • Narrowing contextual conversions in static_assert and if constexpr
  • #elifdef and #elifndef
  • Non-literal variables and labels and gotos in constexpr functions
  • Consistent character literal encoding
  • Multidimensional subscript operator
  • Attributes on lambdas
  • static operator()
  • Support UTF-8 as a portable source file encoding

C++23 Library

  • <stdatomic.h>
  • std::is_scoped_enum
  • std::string::contains and std::string_view::contains
  • std::to_underlying
  • Iterator pair constructors for std::stack and std::queue
  • Prohibiting std::string and std::string_view construction from nullptr
  • Range constructor for std::string_view
  • std::byteswap
  • Printing T volatile *
  • std::string::resize_and_overwrite
  • Monadic operations for std::optional
  • Conditional noexcept on std::exchange
  • std::unreachable
  • std::visit for classes derived from std::variant
  • std::views::join should join all views of ranges
  • Range adaptor objects bind arguments by value
  • Partial support for constexpr for std::optional and std::variant
  • view with ownership

C++26 Library

  • Algorithm function objects

Notes on Later Standard Versions

We currently compile with C++17 support. There are jobs that ensure that LLVM can also compile in C++20 mode with a sufficiently new compiler. As of right now, we require clang 17.0.6 at a minimum to compile LLVM in C++20 mode. This is obviously newer than the clang 15 in this proposal. However, looking at the code that caused the problem, I believe that there are workarounds that we could use to allow compiling our code in C++20 mode without a noticeable change in the time it takes to compile LLVM itself. Should we want to update to C++20 as the minimum C++ standard before the next compiler upgrade, I’d be happy to do that work.

Additionally, some of the C++20 features are theoretically supported by the minimum compiler versions, but in actuality users are likely to run into bugs. I decided to not even mention modules as partially supported, for instance, because I know that there is no reasonable use of them until much later versions of clang (and I don’t know enough to judge gcc and msvc). Using the new C++20 and later features as the justification for the upgrade does mean the case is not quite as strong as it appears just looking at the full list, but I expect to still be able to use a large chunk of those features without needing another compiler upgrade, so we are at least preparing for that change.

Other Reasons to Upgrade

With fewer supported versions, we are less likely to run into a bug in any one of them that blocks a build. This simplifies the developer experience and reduces the frustration of trying to debug your code against a very old compiler.

Reasons to Not Upgrade

This requires users of older, completely unsupported versions of distributions (such as RHEL 6) to have a two-stage process to build LLVM: first build clang 15.0 using the system compiler, then use that to build modern LLVM.

This also puts that requirement on Ubuntu 18.04 and 20.04, which are past their normal LTS support window and into the “Extended Security Maintenance” window (meaning the users receive only security-critical updates) and possibly for RHEL 7 as well. I say “possibly” because RHEL 7 is in “Extended Lifecycle Support” (which is the third and final tier before fully unsupported which I believe also means critical security updates only) and therefore the official pages to check which packages are available don’t even list what you can install on RHEL 7.

Debian 11 “Bullseye” would require installing clang to build LLVM from a package-manager-supported compiler, or a two-stage build for gcc.

SUSE Enterprise Linux 12.5 users would probably require a two-stage compile to compile with clang (but not gcc). I say “probably” because it is now in “Long Term Service Pack Support” so the official documentation no longer lists the available packages.

Windows users will possibly need to upgrade their version of Visual Studio.

Mac users will possibly need to upgrade their version of XCode.

Upgrade Process

From LLVM Developer Policy — LLVM 22.0.0git documentation

We intend to require newer toolchains as time goes by. This means LLVM’s codebase can use newer versions of C++ as they get standardized. Requiring newer toolchains to build LLVM can be painful for those building LLVM; therefore, it will only be done through the following process:

  • It is a general goal to support LLVM and GCC versions from the last 3 years at a minimum. This time-based guideline is not strict: we may support much older compilers, or decide to support fewer versions.
  • An RFC is sent to the LLVM Discourse forums
    • Detail upsides of the version increase (e.g. which newer C++ language or library features LLVM should use; avoid miscompiles in particular compiler versions, etc).
    • Detail downsides on important platforms (e.g. Ubuntu LTS status).
  • Once the RFC reaches consensus, update the CMake toolchain version checks as well as the getting started guide. This provides a softer transition path for developers compiling LLVM, because the error can be turned into a warning using a CMake flag. This is an important step: LLVM still doesn’t have code which requires the new toolchains, but it soon will. If you compile LLVM but don’t read the forums, we should tell you!
  • Ensure that at least one LLVM release has had this soft-error. Not all developers compile LLVM top-of-tree. These release-bound developers should also be told about upcoming changes.
  • Turn the soft-error into a hard-error after said LLVM release has branched.
  • Update the coding standards to allow the new features we’ve explicitly approved in the RFC.
  • Start using the new features in LLVM’s codebase.

This RFC is the first step in that process.

Discussion Still Needed

  1. Do we generally want to do this? The change follows existing policy, but that just says we can, not whether we should.
  2. Do we first need to see the changes that allow building in C++20 mode with clang 15, given that we know right now we require clang 17.0.6 to build, or is it acceptable for that to progress separately? I’m happy to do this work, but if the feeling is the upgrade isn’t worth it until we can get clang 17 anyway for other reasons, then there’s no point.
  3. Which C++20 features do we want to allow / reject? The majority of C++20 is theoretically supported, but some of them might be too buggy in the listed versions. Some of that is probably OK to allow anyway (if the bug is just that some more complicated uses of the feature will fail to compile), so this requires some discussion. I’ll include my thoughts on that in a later post.
  4. A fair amount of C++23 is also theoretically supported by all of these compilers. Do we want to jump straight there or do we want to stop in C++20 for now and do a C++23 RFC later?
  5. It’s unclear to me what “Ensure that at least one LLVM release has had this soft-error” means. For instance, would LLVM 21.1.7 count as a “release” should this RFC be approved very quickly (21.1.7 is scheduled for 2025-12-02), or is the next “release” 22.1.0? I’m guessing it means version 22 is the next release, but I want to confirm.

Previous updates

5 Likes

One of the nicest features C++20 provide (IMO) is std::ranges, which can find a lot of uses in LLVM codebase. clang-15 isn’t able to compile even trivial examples of this, so I’d say we should require clang-17.
Ubuntu-22.04 (that ships clang-15 max) standard support ends in April 2027, so I’d suggest we wait at least until then.

C++20 in GCC is considered experimental still. This will change with GCC 16 though.

And yes there is an ABI change for C++20 between GCC 15 and GCC 16 (<=> for floating point). So …

I would really like to move to C++20, even without the features only available in more recent compilers.

My only concern with this transition is this partial support. From what I recall the move to C++17 did not have this problem, as the minimum supported compilers supported all of the C++17 features at the time. We would very likely need to ban usage of specific features, but without any way to make it an error during local development. This means it’s a lot more likely that code lands that breaks the older compilers.

I’d like to see at which Clang versions people consider features to be reasonably usable. If we can have simple documentation of what’s allowed then I would be less concerned about the above issue.

I had bookmarked the following summary from the previous thread:

Clang 17.0.0 was released in Sept. 2023, so not yet close to 3 years – OTOH, the developer policy also notes “we may […] decide to support fewer versions.”

C++20 is somewhat special because of how big of an increment it was. A good illustration of this is the massive gap between when support was ready for C++17 resp. C++20; from clang’s C++ conformance page

LLVM 5.0.0 was released in September of 2017, so it took 6 years to get even baseline support in. Given the timelines there, the project IMO could decide to just jump to a minimum clang 17 support.

FWIW, I’ve never been quite convinced by the argument about the existence/size of the demography that is supposed to be protected by keeping things compilable on old distros. If someone wants to build LLVM from source (a pretty advanced task in itself), surely – for the most part – they are also able to get a newer compiler from their distro (or some other channel; there are various), or build one locally.

1 Like

There is a ABI break between clang(18) and gcc for concepts (GCC is not implementing it as far as I know). See https://github.com/llvm/llvm-project/commit/4b163e343cfa54c8d55c9da73c70d58f55ea9df2 for details.

That is even mentioned about GCC switching the default to C++20:

There is still some disagreement with clang over concepts mangling, but
later adjustments in template mangling aren’t a deal-breaker.

Personally, I think an important difference between GCC and Clang/LLVM is, GCC is majorly a compiler but Clang/LLVM are also libraries. libclang and libLLVM had a lot users. I don’t know how bad it is if we had imcompatible libclang.so.20 and libLLVM.so.20

While I support moving versions often, you went from “at a minimum” to “at a maximum”. :rofl:

As an Arch user, my personal preference rarely matches that of the general audience. But I have to use Debian and RedHat based distros for work, which tend to be on the old end of the spectrum.

Since RHEL 7 is on life support for a long time, people already compile their own compilers, so I’d not worry too much there. RHEL 8+ is more modern so, for once, they’re not the ones dragging us back. But both Debian 11 and Ubuntu 20 are still on LTS, and that’s a preference for production systems, so I’d consider those to be a minimum requirement, and that’s GCC/Clang 10.

We’ll always be in between standards, having to do work-arounds. That’s why a lot of the LLVM data structures and algorithms are custom. They eventually move to the standard and we eventually replace the custom pattern with a standard one.

We also have limited efforts doing those transformations and they usually happen when we replace minimum compilers, so if we could keep the delta to a reasonable amount, we could do those movements more easily and more frequently.

Between 10 and 15, and due to our custom patterns, I see no strong reason to bump to 15, and I see enough negatives for people on older supported distros.

Windows and Mac users will need to comment, as I have no idea how hard it is to upgrade on older systems. Also remember, this isn’t just about end-user, but CI and automated systems too, which are a lot harder to upgrade.

I’ve been told that switch to C++17 was accompanied by a significant jump in compile times because of additional contents of the standard library headers. C++20 should have even more of that, so I’m curious to know which workarounds you plan to use to mitigate this issue.

When we switched the default to C++17, we saw significant increase in compile times for the project because of how slow STL headers are to compile. Do we have numbers on how bad this hit will be for us?

We’d also need to know that the post-commit bots (and precommit-CI) can handle the switch. It’s fine if we lose a bot for a short while, it’s not fine if we lose too many bots though (particularly, I’m worried some of our bots are already really slow to turn around results, so will that get significantly worse).

C++20 is not complete in either GCC or Clang, so I think it is still premature to switch to C++20. I don’t want our support matrix to be “C++20, except you have to remember these special cases we can’t yet use”; that’s a burden on reviewers to remember, but also a burden on folks who merge on behalf of others (due to risk of needing reverts from build breakages not caught until post-commit). So I’m currently a -1 for this RFC.

2 Likes

There would be significant value for us in supporting some C++20 features such as in-class bitfield member initializers. Imo that alone is worth updating to C++20 - or at least to bump our minimal toolchain requirement. Of, course no one’s gonna use modules yet - everything else is fairly stable at this point.

self-compile times are indeed a concern - mostly because of <algorithm> doubling in size - but that’s not a problem that’s going to disappear, at least we should have data.

We’d also need to know that the post-commit bots (and precommit-CI) can handle the switch. It’s fine if we lose a bot for a short while, it’s not fine if we lose too many bots though (particularly, I’m worried some of our bots are already really slow to turn around results, so will that get significantly worse).

I am assuming there is a process for that?

We break the C++ ABI (and API) on most commits, and the C ABI isn’t impacted by this change. So there’s no concern with language version changing the ABI.

2 Likes

“no concern” isn’t quite accurate, maybe; plugins still have to be concerned with ABI, though we should be strongly encouraging people to build plugins together with the compiler source. I don’t see any such statements about that in our documentation though (perhaps I’ve missed it though).

I strongly oppose building with C++20 on any version of Clang before Clang 17 – there are simply too many bugs and missing features, it’s not worth the trouble and risk. The thread you linked regarding increasing the requirement to 17.0.6 due to a fix to the semantics of rewriting operator== is not the only reason to specify Clang 17 as a minimum. It might be nice if Clang had a more mature C++20 implementation in earlier releases…but that wasn’t the case.

Strictly speaking, yes. However, with Clang 17.0.6 as a minimum, developers can mostly assume that C++20 features (both compiler and library) are working, other than coroutines and modules. Since those two features are easily separable (and distinguishable in code review), this results in a good developer experience, IME (having deployed this internally within google).

There are some other minor missing features, but they were not generally big-trouble in practice. I think probably the “biggest” other missing piece is that std::atomic_ref wasn’t implemented yet in libc++.

That level of completeness is in line with what we had for C++17 support in our minimum compilers.

I don’t have as much real-world deployment experience with C++20 on GCC as I do with Clang, but it still appears to me that GCC 12 is a sufficient minimum version for real-world C++20 code.

I would suggest that our next goal should be “C++20 minus coroutines and modules” – and we should make this change whenever it’s feasible to increase the minimum versions to Clang 17.0.6 and GCC 12. That change would need to wait until the end of 2026 unless we decide to go below our usual 3-years-ago version support guideline.

1 Like

I don’t see what std::ranges would bring that is not already covered by LLVMs ADTs/helpers in much (much) better and nicer way. At least for current code.

Take a look through our bug database at C++20 issues where we diverge from GCC; there’s roughly double the number of issues there as there are for the same search with C++17. (The same searches can be done against MSVC as well). The problem isn’t just “Clang support isn’t that awesome”, it’s “there’s a lot of disagreement still between various implementations on C++20 features, especially when you consider we’re not talking about the latest releases but older releases that are in LTO support.”

1 Like

Do you have a feeling how many of those are covered by defect reports Vs bugs in clang/gcc? We just may not have this information easily determinable but that does make a difference. If most of these are compiler bugs then once clang fixes our bugs we are in good shape (maybe?). If OTOH these are mostly defect report linked we have some indeterminable timeline before most of these issues are resolved and we might need to think a bit more deeply about how we navigate that because there is a point sometime soon we need to move to C++20.

I didn’t mean a general mitigation, I meant specifically that we have a known issue in C++20 mode that requires clang 17.0.6, but I believe I can change the LLVM code to work around this issue and that workaround is not expensive in compile time.

My expectation is that just turning on C++20 will cause LLVM to compile slower. However, being able to use C++20 features can reduce compile times: I have a project that went from taking 15-20 minutes down to ~4 minutes (if I’m remembering the numbers correctly) mostly from using concepts / requires clauses instead of enable_if and other SFINAE-based approaches. I do not know which of those effects will be larger for LLVM in the long run.

Certainly there’s bugs – but the question is whether they’re serious enough that they should prevent us from adopting C++20.

IME, as of Clang 17, the C++20 support (excluding modules and coroutines) seems to be sufficiently complete such that it won’t cause significant pain for developers who may write code with a new version of Clang, and only test on the older one.

However, I think you’re raising a different issue: the potential for problematic differences in C++20 behaviors between even the latest versions of GCC and Clang, which might make it difficult to write code which can compile properly under both compilers. I haven’t seen any evidence of this being an issue – but I have less experience there.