Enable _LIBCPP_ENABLE_*_REMOVED_* by default in -std=gnu++NN?

It seems to me like it may be reasonable to increment the default language version in Clang from -std=gnu++14 to -std=gnu++17 soon.

However, one issue is that a lot of code is broken by libc++'s removal of auto_ptr, random_shuffle, bind1st/bind2nd, ptr_fun/mem_fun/mem_fun_ref/etc. (And almost nothing is broken by the removal of {set_,get_,}unexpected, but I’m not sure it makes sense to actually treat that differently.)

Given:

  1. There is a large amount of existing usage of these functions/classes.
  2. GCC has not, and has no plans to, remove them from libstdc++.
  3. We ought to continue incrementing the default C++ language version in clang.
  4. It would be good not to break vast swaths of existing code while doing so.

I propose that we modify the libc++ configuration to define _LIBCPP_ENABLE_CXX17_REMOVED_FEATURES unless STRICT_ANSI is defined.

That define is set only if the user explicitly passes --std=c++XX. In the default compilation mode, the removed functionality would continue to be available, and only users who ask for a strict compliance mode would get an error.

I think that would strike a reasonable balance between not unnecessarily breaking code, and moving things forward.

Thoughts?

It seems to me like it may be reasonable to increment the default language version in Clang from -std=gnu++14 to -std=gnu++17 soon.

However, one issue is that a lot of code is broken by libc++'s removal of auto_ptr, random_shuffle, bind1st/bind2nd, ptr_fun/mem_fun/mem_fun_ref/etc. (And almost nothing is broken by the removal of {set_,get_,}unexpected, but I’m not sure it makes sense to actually treat that differently.)

Given:

  1. There is a large amount of existing usage of these functions/classes.
  2. GCC has not, and has no plans to, remove them from libstdc++.
  1. We ought to continue incrementing the default C++ language version in clang.
  2. It would be good not to break vast swaths of existing code while doing so.

I propose that we modify the libc++ configuration to define _LIBCPP_ENABLE_CXX17_REMOVED_FEATURES unless STRICT_ANSI is defined.

I agree that libc++ should act pragmatically and allow users to upgrade without the additional cost of fixing usages of removed features.I have some concerns with using __STRICT_ANSI__

Currently, there is no distinction in libc++ between -std=c++XX and -std=gnu++xx; I don’t want to double the number of supported
configurations, nor do I want something as cryptic as the difference between gnu++XX and c++XX to cause behavioral changes in libc++.

In the past libc++ has been bitten by similar behavior changes when -pedantic is specified. This feels like it will cause the same sorts of problems.

That define is set only if the user explicitly passes --std=c++XX. In the default compilation mode, the removed functionality would continue to be available, and only users who ask for a strict compliance mode would get an error.

I think that would strike a reasonable balance between not unnecessarily breaking code, and moving things forward.

For libc++ and it’s maintainers, there is no balance to be struck here, and there never will be. I believe the library has already committed to supporting the removed features ad infinitum.
So what cost do we bear for exposing them differently?

Are there other suggestions for how to best gate these features?

Although I understand and support the rationale here, I’d still like some way to opt out of libc++ exposing these removed features. For us, the C++17 upgrade was an excellent opportunity to find and modernize all these issues in our codebase. (I know that ideally you should be doing that on a more regular basis, and that e.g. there’s clang-tidy tools to help with that, but the upgrade was a nice forcing function.)

Exposing some extensions beyond the standard is the intent behind there being a separate -std=gnu++17 flag, vs -std=c++17, so that doesn’t really feel like a cryptic behavioral change to me for libc++ to expose the no-longer-standard functions only in -std=gnu++17.

But it’d also be fine by me to just keep them enabled by default in all modes – the important part is that they get enabled in the default compilation mode. Regarding opt-out – we could perhaps honor the flag being set explicitly to zero, e.g. -D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES=0.

First, I agree with Eric about not adding yet another dimension of configuration to libc++. I know for you it might appear like just a convenience, however the number of configurations supported by libc++ is already too large to maintain, so the only thing we should do is reduce it, not increase it.

On a different level, I believe that libc++ should implement the Standard, and the Standard has decided to remove these features from C++17. Hence, I believe it is entirely reasonable for libc++ not to provide these features in C++17, and in fact I view the fact that we allow un-removing them merely as a gentle approach to help people upgrade. In the future, I believe that we should unconditionally remove these features from C++17.

I think it’s a great idea to update the standard mode for Clang to C++17 (or Gnu++17), however I don’t think libc++ should bend itself in order to make arbitrary code bases compile out-of-the-box when they don’t conform to the Standard mode they are trying to use. It seems to me like either:
(1) these code bases are not maintained anymore and there’s no intent to update them, so it’s better for them to just acknowledge that and compile them as C++14, or
(2) these code bases are maintained, and we’re trying to use these removed features to save the work of actually updating the code base.

In both cases, I think that providing these removed features in C++17 is just playing ostrich with the real issue.

Louis

First, I agree with Eric about not adding yet another dimension of configuration to libc++. I know for you it might appear like just a convenience, however the number of configurations supported by libc++ is already too large to maintain, so the only thing we should do is reduce it, not increase it.

OK, let’s unconditionally re-enable these types/functions, and provide no mechanism to disable them. That reduces the configuration dimensions compared to now. We emit deprecation warnings, in any case.

On a different level, I believe that libc++ should implement the Standard, and the Standard has decided to remove these features from C++17. Hence, I believe it is entirely reasonable for libc++ not to provide these features in C++17, and in fact I view the fact that we allow un-removing them merely as a gentle approach to help people upgrade. In the future, I believe that we should unconditionally remove these features from C++17.

I think it’s a great idea to update the standard mode for Clang to C++17 (or Gnu++17), however I don’t think libc++ should bend itself in order to make arbitrary code bases compile out-of-the-box when they don’t conform to the Standard mode they are trying to use. It seems to me like either:
(1) these code bases are not maintained anymore and there’s no intent to update them, so it’s better for them to just acknowledge that and compile them as C++14, or
(2) these code bases are maintained, and we’re trying to use these removed features to save the work of actually updating the code base.

In both cases, I think that providing these removed features in C++17 is just playing ostrich with the real issue.

Yet very unfortunately, C++ does not make it possible to just compile “old” code in C++14 and the “new” code in C++17. A single dependency which continues mentioning std::bind1st in its header can prevent you from using c++17 for all of your code that depends on it. And with much of C++ living in headers, and with there being no way to declare a standards-version just for one section of code (or, dare I say, “module”), this is problematic. Furthermore, even if the std::bind1st call is in an implementation file, selecting a different standards mode when building only that translation unit carries the risk of ODR issues from entities defined in other headers it includes. You can often get away with this anyways, but it’s better not to…

So, “just fix all the code”? Yes, of course that’s possible, but that’s painful. Maybe very painful, depending on how much of other people’s code you’re depending on.

Given the downsides, what’s the counterbalancing benefit to removing these symbols? There seems to be no maintenance benefit to libc++ itself, because the implementation must remain. It’s not a standards-compliance issue, because the Standard explicitly blesses implementations continuing to provide these removed features, despite having removed them.

I think the argument is that breaking our users’ code is for their own benefit – this way they are made aware that they’re using a feature which was determined to be bad, and are thus forced to stop doing so. I don’t find this a convincing argument – the benefit here this seems very much outweighed by the downsides – but I don’t know how to actually quantify that.