microsoft/STL

Determine ABI stability timeline for C++20

StephanTLavavej opened this issue ยท 53 comments

We're in a novel situation: never before have we completed a Standard so rapidly. Adding to this concern, <format> is both ABI-sensitive and performance-sensitive, and WG21 is expected to mandate ABI-breaking changes soon.

(C++17 was different; we finished <charconv> in 2019 having completed all other features much earlier, and while it was performance-sensitive, we had time to extensively test and tune it, and its function-oriented nature means that it has no ABI concerns and can be revised in the future.)

We were initially planning to declare ABI stability when finishing C++20 (along with the addition of /std:c++20 as distinct from the always-experimental /std:c++latest), to align with the user expectation that non-experimental features supported for production use are also ABI stable. However, if this locks us into an implementation that can't keep up with retroactive changes to C++20, or freezes an implementation before it's had a chance for significant performance tuning, that will ultimately be harmful to users.

We should consider asking our bosses and boss-like entities for a change of plans: we will still declare C++20 to be feature-complete and ready for production use when we're done (as indicated by the feature-test macros), but we can announce that its ABI will potentially change for some defined period of time (3 months, 6 months, 1 year?) or number of releases (e.g. Dev17 17.x for some specific value of X). This will have no effect for /std:c++14 and /std:c++17 users, and we can expect that, at least initially, the population of /std:c++20 users will be the most eager upgraders for whom rebuilding the world is the least onerous.

Additionally, to mitigate mix-and-match problems, we could add a #pragma detect_mismatch to <format> right now, allowing us to revise it in the future and have the linker detect any OBJ/static LIB mixing.


Update: We arrived at a plan, see the comment below.

I personally think something like keeping the ABI unstable until the non-preview release of Dev17 would be a good idea.

Freezing it now will lead to major differences between standard libraries because the new changes to the standard library require ABI breakage, meaning some libraries and consumers might be reluctant to adopting C++20 because of inconsistency between compilers/standard library implementations, and this will ultimately be detrimental to the MSVC ecosystem.

As @brevzin noted, there are three Ranges papers in flight with ABI implications as well (split, default construction, and join).

We should consider asking our bosses and boss-like entities for a change of plans: we will still declare C++20 to be feature-complete and ready for production use when we're done (as indicated by the feature-test macros), but we can announce that its ABI will potentially change for some defined period of time

Nothing but hoping for your bosses' good sanity๐Ÿ˜Ž

We're in a novel situation: never before have we completed a Standard so rapidly.

I have high hopes for C++23...

We're in a novel situation: never before have we completed a Standard so rapidly.

I have high hopes for C++23...

Will be finished in 22

If this can help take a decision or convince anybody:
Out of the 5 last clients I worked with, 4 said they stopped using some of STL types due to either runtime performance or compile time performance, that could be fixed by changing ABI such as is done in vNext. I'm thinking mainly about debug iterators and regexp.
If std::format isn't fast enough (to compile or at runtime) compared to fmt::format, I'm afraid this will only keep fueling arguments against using the STL (and std library in general, whatever implementation it is).

While I am sympathetic to having some kind of ABI stability, I don't think "rushing" a release just to be able to put the "c++ latest compliant" stamp is a good idea if QoI improvements can't follow due to the same ABI stability.
An upgrade path must be kept, or some rupture planned to finally take this opportunity to break ABI, just as was done in the past.

Personally, I'm not even that concerned about performance, exactly because there is fmt and the STL is rarely the best performing solution for a given problem anyway.

What I do care about though is correctness and portability. Effectively locking down the ABI before having any significant user contact, real-world testing and (ideally) a second implementation to compare against seems a very risky choice for something so fundamental - especially when people are still trying to "fix" the standard itself.

Personally, I'm not even that concerned about performance, exactly because there is fmt and the STL is rarely the best performing solution for a given problem anyway.

By this logic, there is no value in using STL at all, because every piece of it has more performant counterpart somewhere anyway. The only reason would be that it's available out-of-the box.

If the performance question is not about years of researching or some rocket science, but only delaying ABI locking by month or so I would definitely consider it as the way to go.

By this logic, there is no value in using STL at all, because every piece of it has more performant counterpart somewhere anyway. The only reason would be that it's available out-of-the box.

That's pretty much the way it is. Yes. Except for

there is no value in using STL at all

Just because there is something faster somewhere doesn't mean the STL doesn't have value. Using 3rd party dependencies comes with it's own costs and even in c++ code, performance is not the only relevant metric.

But that gets off topic.

Personally, I'm not even that concerned about performance, exactly because there is fmt and the STL is rarely the best performing solution for a given problem anyway.

It's more complicated in case of fmt/std::format compared to, say, containers because here we have the formatter extension point. It would be really unfortunate if we ended up in the world with two sets of incompatible formatter specializations and resulting interoperability problems. I think it's worth taking time and addressing performance differences before ABI is frozen or at least making sure that they can be addressed in the future without ABI breakage.

It's more complicated in case of fmt/std::format compared to, say, containers because here we have the formatter extension point. It would be really unfortunate if we ended up in the world with two sets of incompatible formatter specializations and resulting interoperability problems.

So far we have rarely been using a library provided operator<< for printing (except for printf debugging or quick prototyping), because most of the time we had our own format requirements anyway. In a similar fashoin, I'm not sure how much I'm going to use a 3rd party fmt/std::formatter specialization in a context, where performance matters. Also, performance aside, I expect fmt to stay ahead of std::format feature and compatibility wise for the forseable future, so we'll probably have to live with this duality for quite some time anyway.

Anyway, as I wrote previously: I'm also very much against locking down the ABI right away - just that performance isn't my primary reason for that.

far we have rarely been using a library provided operator<< for printing

You maybe, others have been using them extensively.

we'll probably have to live with this duality for quite some time anyway.

The important thing is the long term, not the next few years. In the long term I expect fmt::formatter to go away.

You maybe, others have been using them extensively.

Not sure what you are trying to convince me of. I never said, 3rd party overloads of operator<< isn't bein used by others. I said we didn't use it. I also didn't say whatever performance such an ABI freeze would leave on the table isn't a problem for anyone. I said I am not concerned about the performance impact [EDIT: primarily].

The important thing is the long term, not the next few years.

Long term, the current msvc ABI doesn't matter anyway (assuming plans to introduce an new ABI in the Dev17 timeframe come to fruitition). Also, I'd speculate that implementation divergence or unfixable bugs that slipped through will be a much bigger obstacle to the adoption of std::formatter than x% performance difference.

Anyway. I think we agree that we'd prefer ABI not to be frozen right away. Arguing which reason (potential performance difference vs potential divergence/correctness issues or even the danger of preventing improvements to the standard) is "more important" feels silly.

I think std::format is a little less hamstrung by ABI than regex, but these sorts of things are extremely difficult to analyze correctly.

This will have no effect for /std:c++14 and /std:c++17 users, and we can expect that, at least initially, the population of /std:c++20 users will be the most eager upgraders for whom rebuilding the world is the least onerous.

Does this mean that you will not be able to link /std:c++14 code with /std:c++20 code once it is properly released? Or is this about mixing /std:c++20 code compiled with VS 17.0-preview4 with /std:c++20 code compiled with VS 17.6?

The latter

I'm concerned about the VS 2022 blog post saying: "If you want to upgrade to Visual Studio 2022 but are worried about compatibility, binary compatibility with the C++ runtime will make it painless."

Which basically means that they are not going to break the ABI of the current runtime for C++11/14/17, which means that some old bugs and performance issues aren't going to be addressed. Not sure if different runtime will be used for the C++20 mode(where older standard features are used), but if it is, then things are getting really confusing.

@StephanTLavavej Could you shed some light on these matters?

The ABI was not broken for VS 2022, as documented here: #1725 (reply in thread)

And no, C++20 doesn't use a different runtime, so you can link code compiled for C++20 to code compiled for older standards without issue.

The ABI was not broken for VS 2022, as documented here: #1725 (reply in thread)

And no, C++20 doesn't use a different runtime, so you can link code compiled for C++20 to code compiled for older standards without issue.

I see, so unfortunate... Anyway, to be a bit more on the current topic, I also agree that the C++20 ABI should stay unstable a bit more time in order to properly fix issues from WG21.

I've set up a meeting with our bosses and boss-like entities to reach a decision here. Thanks to everyone who's expressed interest in this issue; it is very helpful to be able to point to the number of upvotes ๐Ÿ˜ธ

@StephanTLavavej Great to hear!

FYI, the next plenary is on June 7, the ranges papers should be approved then.
I also would like to point out this issue - having a few more weeks to resolve it would be greatly appreciated LWG3547.

As a matter of personal opinion (not reflective of anyone in WG21), I think giving WG21 until the next plenary to put out the fires and address known defect would be a lovely compromise, I understand that we cannot keep doing that forever.

At the same time, I like to think that it's in the benefit of everyone to address known issues, while we still can.
The long time costs of not doing it would otherwise be paid for many decades by all of C++ developers, or back both WG21 and implementers in an uncomfortable corner.

Mistakes were made, we were blindsided by some issues, and frankly WG21's output also suffers from self-imposed deadlines.
Maybe we should have course corrected sooner, but times are challenging.

I apologize for whatever part I had to play in that lack of reactivity.

We can only try our best. And hope there aren't to many unknown problems that will pop up once C++20 is widely deployed.

Congrats on implementing the standard in record time in the midst of a pandemic,
and thanks for working with your organization on this issue.

Thanks!

One other aspect that's novel and not mentioned explicitly in the OP, but came up in the reddit thread announcing STL's C++20 completeness (by, IIUC, the same author):

someone: Did open sourcing the library help in any way?

@StephanTLavavej: Yes, beyond our wildest dreams. Our amazing GitHub contributors were responsible for many large and difficult features, stressing both domain expertise and attention to detail. It took some time to migrate the STL to GitHub (an ongoing project) and it takes more time to perform detailed code reviews that communicate how to implement/maintain the STL (something that no book directly teaches; I learned it as an apprentice/minion), but ultimately it's saved a ton of time as our contributors have gone on to write way more (and better!) code than the maintainer team working in isolation ever could have in the old closed system. Without being open source, there's no way we'd have been complete now - probably it'd be calendar year 2022. [...]

It might be helpful for that meeting to point out how the community helped put the STL in a position where the option of declaring feature completeness & ABI stability is even on the table that early. It would be a wonderful way to acknowledge that contribution (on the level of the bosses and upwards) by listening when now many of the same people (not including myself...) are asking for STL's help in turn.

@h-vetinari An excellent point, I'll make sure to bring it up!

image

Ok, we've got a plan approved by our bosses and boss-like entities:

  • VS 2019 16.10 will not have /std:c++20. (That compiler option, which was added in a Preview, will be removed before the production release.) Our C++20 implementation will remain available under /std:c++latest, with the STL's sources remaining unchanged. While the STL is still C++20 feature complete, /std:c++latest doesn't promise ABI stability.
  • VS 2019 16.11, the last update in the VS 2019 release series, will add /std:c++20. We'll apply library changes so that almost all C++20 STL features will be available in /std:c++20 mode. The exceptions are that <format>, <ranges>, and the formatting part of <chrono> will remain restricted to /std:c++latest due to WG21's upcoming changes.
  • VS 2022 17.0 will receive the same /std:c++20 changes as 16.11. Then, we'll begin applying ABI-breaking changes to the affected headers guarded by /std:c++latest.
  • When WG21 is finished backporting changes to C++20, and we've implemented those changes in addition to any ABI-breaking performance improvements for <format>, we'll freeze the ABI and VS 2022 17.x will enable <format>, <ranges>, and the formatting part of <chrono> under /std:c++20.
    • I believe that this part of the timeline (i.e. exactly which 17.x release declares stability) is up to the STL maintainers; my tentative expectation is that WG21 and our implementation will be ready for stability by the end of calendar year 2021, whatever 17.x release that ends up flowing into.
  • Finally, we'll backport this work (the ABI-breaking changes for C++20 Defect Reports and performance improvements, plus enabling everything under /std:c++20) to a VS 2019 16.11.y servicing release.

@CaseyCarter is working on the library changes needed to make <format>, <ranges>, and the formatting part of <chrono> available under /std:c++latest only; we'll apply this to the microsoft/STL repo and VS 2022 17.0 simultaneously, then backport to the 16.11 branch internally.

Hope this makes sense! We can answer any questions you might have.

Very well. How is that going to work in/with CMake?

I am not very familiar with CMake, but I've seen our usage of CMAKE_CXX_STANDARD:

set(CMAKE_CXX_STANDARD 20)

According to their documentation for CMAKE_CXX_STANDARD and CXX_STANDARD, "Supported values are 98, 11, 14, 17, 20, 23."

I would expect that CMake should:

  • Map 20 to /std:c++latest for MSVC up to and including 16.10,
  • Map 20 to /std:c++20 for MSVC 16.11, 17.0, and beyond
  • Map 23 to /std:c++latest for all versions of MSVC (until years from now when C++23 is done).

I am not sure what the current behavior is.

Regarding CMake:

Map 20 to /std:c++20 for MSVC 16.11, 17.0, and beyond

As a casual C++ developer this would be contrary to what I'd expect. According to your explanation I would expect to map 20 to std:latest as long as it is not finalized. So in this case map it to that as long as 17.x can ship with a feature complete and finalized C++20.

Everything else will probably create a lot of headaches if CMake behaves differently for different versions of VS being installed. I can already see people being confused why a more up2date installed VS version is having less features with the same CMakefile.

@thewavelength: stdc++20 with 16.11 and 17.x will provide the full c++20 feature set (at least that's the plan as far as I understand), so I don't get what you are concerned about.

@MikeGitb if that's the case I'm fine with it. But I understood it in a way that (probable) ABI incompatible features like format wouldn't be included in those versions in std:c++20 but only in std:latest until it is finalized by the committee.

I think you are right - I overlooked

  • VS 2019 16.11, the last update in the VS 2019 release series, will add /std:c++20. We'll apply library changes so that almost all C++20 STL features will be available in /std:c++20 mode. The exceptions are that <format>, <ranges>, and the formatting part of <chrono> will remain restricted to /std:c++latest due to WG21's upcoming changes.

Sorry

I am not very familiar with CMake, but I've seen our usage of CMAKE_CXX_STANDARD:

set(CMAKE_CXX_STANDARD 20)

According to their documentation for CMAKE_CXX_STANDARD and CXX_STANDARD, "Supported values are 98, 11, 14, 17, 20, 23."

I would expect that CMake should:

* Map `20` to `/std:c++latest` for MSVC up to and including 16.10,

* Map `20` to `/std:c++20` for MSVC 16.11, 17.0, and beyond

* Map `23` to `/std:c++latest` for all versions of MSVC (until years from now when C++23 is done).

I am not sure what the current behavior is.

@bradking sorry for mention, but could you confirm/refute this? Thanks in advance ๐Ÿ˜Š

CMake's logic for mapping standards to MSVC flags is here. It will need an update for /std:c++20 and C++23. Please see our CONTRIBUTING.rst file and open a merge request for that.

See CMake MR 6174 for this update.

@thewavelength Yeah, your point makes sense.

@bradking Please note that VS 2019 16.10 Preview 4 differs from the final production release - at the last moment, we removed /std:c++20 (which is the subject of this very long thread). If I understand that CMake merge request correctly, it will attempt to use /std:c++20 with 16.10 which will fail to compile with the production release (available right now).

Additionally, note that /std:c++20 has not been removed from 16.11 Previews (we're going to port STL changes to modify which features it enables).

@StephanTLavavej Why weren't these 'experimental/unstable' features hidden behind some temporary 'experimental' switch? If I get it correctly in the case of CMake and maybe other build systems as well: <format> etc. will be available in 16.10 with cxx_std_20 , then in 16.11 the won't but will appear under cxx_std_23 and then sometime in 17.x again with cxx_std_20.
That's probably not a big issue but it will certainly confuse somebody.

My understanding was that all features that require /std:c++latest should be considered unstable. Unfortunately (or maybe fortunately) Cmake doesn't distinguish between stable and unstable features belonging to the same standard version (not sure how useful it would be inpractice).

@StephanTLavavej , @bradking
Assuming that it will not make a difference if /std:c++latest or /std:c++20 is used in 16.11 if I don't use the affected headers in the frist place and also assuming that there won't be c++23 features under c++latest in 16.11, I think my personal preference for cmake would be to not use /std:c++20 for any version of VS2019 series at all and just always map cxx_std_20 to /std:c++latest. That way, the behavior is more consistent and I would assume/hope (but could be wrong) that people requiring ABI stability will anyway take much more direct control over what flags are being used exactly instead of relying cmake to consistently select appropriate flags accross compiler and cmake versions.

Not sure what my preferences would be if those assumptions don't hold.

Would of course be great to get feedback from someone who actually relies on ABI stability for their product.

I can confirm that we will not backport C++23 features to 16.11. We aren't planning to backport any STL changes aside from #1929 itself, and any fixes for critical bugs that we discover (they'd have to be pretty bad, beyond ordinary bugfixes). Because 16.11 is a permanent stabilization release, feature work is specifically not allowed (only with special exceptions).

VS 2019 16.10 Preview 4 differs from the final production release

In CMake MR 6174, the /std:c++20 flag will be used only for cl version 19.29.30129 or higher. VS 16.10 has cl version 19.29.30037, so we won't use /std:c++20 until VS 16.11.

Cmake doesn't distinguish between stable and unstable features belonging to the same standard version

CMake's cxx_std_## meta-features only care about nominally putting the compiler in a mode aware of the corresponding language standard. For unfinished modes, /std:c++latest is the best it can do. Beyond that, it is up to project code to put appropriate preprocessor conditions around use of advanced language features.

Ah, thanks! I didn't inspect the version number closely. ๐Ÿ˜น

Note that the Chrono fix will not land officially before October as we were told rushing it would make no difference on your timeline

if (MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30129 AND CMAKE_VERSION VERSION_GREATER 3.20.3)
    # this change happened in CMake 3.20.4
    set(CMAKE_CXX_STANDARD 23) # /std:c++latest - unlocks the non stable cpp20 features. For new 16.11 versions
else ()
    set(CMAKE_CXX_STANDARD 20) # /std:c++latest for msvc and -std=c++20 for everyone else.
endif ()

I had an issue on cmake. They pointed me to this issue. I ended up using this to keep 16.10 and 16.11 compiling with c++ 20. I'm using <ranges> and such. I got that version number from the Cmake source. https://github.com/Kitware/CMake/blob/71bf838cf35fabc27ff009f2901eeffc786fa753/Modules/Compiler/MSVC-CXX.cmake#L32

I had to update the if statement because older versions of cmake don't know how to handle c++ 23.

4 more defect reports approved for C++2021 ๐Ÿ™ƒ

They all seem to be merged now, so finally we can go for stable full featured C++20(21) mode. :)

FYI, I've closed this issue despite that there's still work to be done to backport <ranges> and <format> to VS 2019 16.11, because all of the work that will take place in this repository is now complete.

Since this issue is closed, is #2492 the issue to keep an eye on for the backporting work?

Why I can't use std::format in VS2022? Namespace std has no member format!!!

image

image

std::format under /std:c++20 is available in VS 17.2. For now, 17.2 is in preview. It should release to the stable branch at some point.

Why I can't use std::format in VS2022? Namespace std has no member format!!!

image

image

้œ€่ฆ VS 2019 16.11.14 or VS 2022 17.2 above

@JonSuper It should be present in those versions. Are you sure you're looking at the properties for the configuration that you're actually building? I recommend looking at the build log and verifying whether /std:c++20 is on the command line.

You can also inspect the STL's macros to check its version and Standard mode. Include <version> and test _MSVC_STL_UPDATE - it should be exactly 202105L for VS 2019 16.11.14 or any later patch, and it should be 202203L or greater for VS 2022 17.2 or greater. Finally, after including <version>, you can inspect _HAS_CXX20 which will be defined to 1 if the STL believes that it's in C++20 mode (otherwise it will be 0).

Hopefully this should allow you to track down whether you're using an unexpected version or an unexpected compiler option.

@JonSuper It should be present in those versions. Are you sure you're looking at the properties for the configuration that you're actually building? I recommend looking at the build log and verifying whether /std:c++20 is on the command line.

You can also inspect the STL's macros to check its version and Standard mode. Include <version> and test _MSVC_STL_UPDATE - it should be exactly 202105L for VS 2019 16.11.14 or any later patch, and it should be 202203L or greater for VS 2022 17.2 or greater. Finally, after including <version>, you can inspect _HAS_CXX20 which will be defined to 1 if the STL believes that it's in C++20 mode (otherwise it will be 0).

Hopefully this should allow you to track down whether you're using an unexpected version or an unexpected compiler option.

image
@StephanTLavavej
It will compiled fail while using VS 2022 17.2 with Visual Studio 2019 (v142) Toolset
the yvals_core.h here :

#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 and GH-1814
#define __cpp_lib_format 201907L
#endif // _HAS_CXX23 && defined(__cpp_lib_concepts)

but using VS 2019 16.11.15 with Visual Studio 2019 (v142) Toolset is OK

Ah, then the issue is that the VS 2022 IDE's selectable option of the VS 2019 v142 toolset hasn't been updated to the 16.11.14 or later bits. I believe this will happen at some point but I don't have an ETA.