[RFC] A compiler flag to enable experimental/unstable language and library features

In that case, should there be a -funstable-abi for users who don’t care about ABI stability but do care about API stability? If not, would you want to use the same ABI namespace as the stable ABI or set it to __unstable<LLVM_version> or something like that?

I think that is an interesting but separate question. Regarding the ABI namespace, we are already using __2 as the inline namespace in the unstable version (which is a shortcut for ABI v2 at this point).

Sorry, it really wasn’t clear what my thought process on __unstable<LLVM_version> was. My idea was basically to have only the unstable parts in a different ABI namespace to try to break users who use it as part of their ABI. I wouldn’t want to block -fexperimental-features over that though, since we should be free to change that at any point later.

We create a -fexperimental-library flag that doesn’t impact language-level features and only enables library TSes and incomplete features like <format> and <ranges>. That flag does not have any impact on the ABI namespace. It handles linking against c++experimental.a and setting any other macro that may be needed for experimental things to work.

That might actually be a bit confusing. For example, <ranges> requires concepts, but they were guarded behind -fconcepts for a while IIRC. Why would I, as a user, have to set both -fexperimental-library and -fconcepts to get ranges? That’s really unintuitve IMO if -fexperimental-library is supposed to give me all of the unstable library features.

Thanks Louis!

I’m in favour of this proposal. (Full disclosure, I’m the main author of <format> in libc++.)
With our current approach vendors decide whether or not to enable these features and I expect all of them to use the default: not shipping these features. That means, that currently libc++ is the only Standard library implementation not shipping incomplete features. MSVC STL uses /std:c++latest and libstdc++ declares everything in C++20 as experimental.
With the proposed change the decision goes from the vendor to the user, which I think is good. The user knows their specific needs and requirements better than the vendor, who has to cater for the common good of all users.

I agree experimental sounds less scary than unstable so in retrospect I think the former is better.

I don’t mind having everything under one flag. When this flag contains “too much” we can add more flags. However items under this flag should become “mainline” by completing the feature/the TS gets adopted in the Standard.

I agree that would be confusing. However this can be solved in Clang by enabling g -fconcepts when -fexperimental-library is set. So it’s something we need to be aware of, but it’s easily caught in the CI.

The need for the static library and other bits of compiler awareness are a good reason to add a compiler flag.

My biggest concern is that we avoid breaking someone’s code by adding a feature the user didn’t intend to use in the first place. All sorts of flags tend to end up in build systems, and it’s easy to end up at a point where nobody really understands why specific flags are used.

If the flag only enables the use of new symbols from the C++ standard library, and doesn’t affect the usage of any existing functionality, then I think I’m okay with allowing all the “current” features without calling out specific features. I’m a little concerned that someone could end up accidentally pulling in functionality by ADL, but I guess that’s unlikely in most cases.

Reconsidering this, while I do think that @philnik has a point about e.g. Ranges needing concepts, I also think that enabling some experimental language features (e.g. modules) should be selectable by users. I can easily imagine someone wanting to compile with modules disabled but with experimental library features enabled. I’m not sure it would make sense to force using modules to these users.

So personally I’m converging towards a simple -fexperimental-library flag that only controls whether the library provides experimental features, leaving all the language-level experimental features to be controlled by other flags, like we do today.

Is that something that everyone would be happy with?

Looks like a good addition, nice!

+1 on the new name. Few questions:

  • How does -fexperimental-library behave in face of non-C++, is -std=c99 -fexperimental-library an invocation error?
  • Should this flag implicitly define a macro? Would be wise/desirable to do so?

I would say it’s not an error. If there are ever experimental C library features, they could be enabled by it, with any additional flag required to make it happen (such as linking against another library) being added by the driver implicitly.

In the current implementation, the flag can be sniffed by inspecting __has_feature(experimental_library), and I think that’s exactly what we want. In particular, defining a libc++ specific macro would tie the flag to libc++, which would be an artificial self-imposed limitation.

1 Like

I rather like to evaluate it on a case by case basis. For example the in the past both <ranges> and <format> used concepts, in that case I think it makes sense that -std=c++20 -fexperimental-library implies -fconcepts. When we don’t do that I expect a lot of bug reports of users who want to experiment with these new features.

On the other hand I would not expect -std=c++17 -fexperimental-library to set -std=c++20 to enable <ranges> and <format>.

I would say the rule of thumb should be: when during developing libc++ of a new experimental feature we need to enable an experimental compiler flags we add that flag to -fexperiential-library.

I consider modules in the same category as -std and should never be enabled by -fexperimental-library . This isn’t just a language feature but a completely different “language” in the same fashion that C++17 and C++20 are a different “language”.

I agree, but I think it can be quite hard to determine what the right choice is.

For example in this case it makes sense right now, but what would be the right thing when we start working on https://wg21.link/P2465 (which is tentatively ready for plenary)? I think the right thing would be to have -fexperimental-library imply -fcxx-modules, but could be overriden with -fno-cxx-modules for the people who don’t want it. For example -std=c++20 -fexperimental-library would imply -fconcepts and -std=c++2b -fexperimental-library would imply -fconcepts -fcxx-modules with the option to pass -fno-X to explicitly disable a feature and the experimental library parts that rely on it.

I think modules might be one of the hard choices. For example, I have no idea how clang-modules and C+±modules interact, or how much code gets broken when C+±modules are enabled. Therefore I think we should “evaluate it on a case by case basis”. In this case I first would like to see whether enabling

Some of the other clang options we had were a lot less controversial, concepts, coroutines, and char8_t come to mind.

1 Like

FWIW, for clang-cl environments, the linking aspect of the -fexperimental-library flag is a bit tricky, as in most clang-cl/msvc environments, you don’t call the compiler driver to do linking, but you call link.exe or lld-link directly. But I guess that’s solveable, by making the -fexperimental-library flag embed linker directives to pull in libc++experimental.lib into each object file? (For the regular library, the compiler itself doesn’t embed those directives, but they’re added by the libc++ header in llvm-project/__config at 61d417ceff90c4bd99bfed082d81bcc33ecf5a45 · llvm/llvm-project · GitHub)

We have to add some preprocessing for this anyways, so it should be possible to simply change the code to

#  ifndef _LIBCPP_NO_AUTO_LINK
#    if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_BUILDING_LIBRARY)
#      if !defined(_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
#        pragma comment(lib, "c++.lib")
#        if __has_feature(experimental_library)
#          pragma comment(lib, "c++experimental.lib")
#        endif
#      else
#        pragma comment(lib, "libc++.lib")
#        if __has_feature(experimental_library)
#          pragma comment(lib, "libc++experimental.lib")
#        endif
#      endif
#    endif // defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_BUILDING_LIBRARY)
#  endif   // _LIBCPP_NO_AUTO_LINK

IIUC.

Yep, something along those lines would work (except that the library always is called libc++experimental.lib as there’s no shared version of it).

The fact that libc++experimental only is linked statically while linking the rest of libc++ dynamically is also the reason why libc++experimental doesn’t really work in shared linked clang-cl/msvc configurations anyway - because the headers would be indicating dllimport linkage for all types (via e.g. the _LIBCPP_TYPE_VIS and _LIBCPP_FUNC_VIS macros) even though the stuff in libc++experimental is linked statically and shouldn’t be accessed with dllimport linkage. Fixing that would require adding e.g. _LIBCPP_EXPERIMENTAL_*_VIS for all those functions/types/templates that are provided by the separate experimental library.

Maybe we should add a macro the next time someone puts something into libc++experimental.lib. It’s not that hard. I plan to work on std::pmr during LLVM 16, so it doesn’t make a lot of sense to change that right now. After removing experimental::pmr the library would be empty anyways.

Bulk replying – sorry I’ve been really busy with other stuff.

Regarding the Windows issue, I agree that we should use pragma comment(lib, "libc++experimental.lib") to make sure that the experimental features work on Windows just like they do on other platforms. I think we should also add appropriate visibility annotations to make it work.

I think the only contentious part at this point is whether -fexperimental-library should also turn on various experimental language features. When I started this proposal, my opinion was “yes”. However, it changed completely when I realized that some of those were undesirable, like modules. Similarly, I don’t think we should try to figure out what language features are required by any given library feature and then tweak the Clang driver to account for that. When we make Clang changes, we need to wait for roughly 1 year before we can assume those changes inside libc++ because of our support window.

Instead, I really think -fexperimental-library should exclusively control whether we provide experimental stuff in the library, and users can cherry-pick the other experimental language features they want (or need) to use.

I still have a preference to enable the needed compiler flags. However I see I’m in the minority. Instead I want to propose that instead of enabling the flags by default we validate whether the proper flags are set.

When an experimental header is included without -fexperimental-library all is fine. This can happen with indirect includes. We tried to make that an error in the past and that was reverted due to too many false positives.

When an experimental header is included with -fexperimental-library but without -ffoo required for that specific header then there should be an #error to explain which compiler flag or flags are required to use that header.

That way its easy for the user to set the proper flags.

I think that might not work that well. For example people might want to use the ranges algorithms, so they just include <algorithm> and then wonder why nothing they expect is there. With your idea we would either have to add errors only to the headers which exclusively rely on a feature, or error on almost all headers.
BTW I’m still with you that it would be a good idea to enable the required features per default. We’d have to check for the features explicitly anyways (with __has_feature or FTMs) and I’d expect people who want to try out the latest toys to be on the latest release. Otherwise the reason for not having the latest features is obvious: you just need to update. Same if you disabled specific features. OTOH if you add a flag which is explicitly for getting the experimental parts I wouldn’t expect that I have to enable more flags to actually get the experimental stuff.

Is using libc++ with GCC still supposed to work?

Would a new Clang compiler flag mean that GCC users cannot access the experimental features in libc++? (I’m not objecting if that’s the case, just trying to understand and be prepared to explain that to users.)

IIUC users currently have to add -lc++-experimental to use all the stuff from experimental/ and that wouldn’t change. The only change is that clang users can pass -fexperimental-library to gain access to features currently marked _LIBCPP_HAS_NO_EXPERIMENTAL_*, which GCC users also currently don’t have access to AFAIK.

The only such feature I see in the headers for 15.0.0 is _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES which is indeed unavailable to GCC (because there’s no GCC flag to enable __cpp_coroutines).

My understanding from the discussion above is that it would also enables things like <ranges>, which is guarded by _LIBCPP_HAS_NO_INCOMPLETE_RANGES and so is available to GCC users if libc++ was configured with LIBCXX_ENABLE_INCOMPLETE_FEATURES=ON.

Oops, sorry. I meant _LIBCPP_HAS_NO_INCOMPLETE_*. Yes LIBCXX_ENABLE_INCOMPLETE_FEATURES would be removed I think. But I guess it wouldn’t be that hard to keep some way to enable it via a flag. We could simply add _LIBCPP_ENABLE_INCOMPLETE_FEATURES and use #if __has_feature(experimental_library) || defined(_LIBCPP_ENABLE_INCOMPLETE_FEATURES). That won’t be as nice of an interface of course, since it won’t automatically add any other dependencies.

1 Like