diff mbox series

[RFC] c-family: Implement __has_feature and __has_extension [PR60512]

Message ID ZFo3b8RDN8nseojl@arm.com
State New
Headers show
Series [RFC] c-family: Implement __has_feature and __has_extension [PR60512] | expand

Commit Message

Alex Coplan May 9, 2023, 12:07 p.m. UTC
Hi,

This patch implements clang's __has_feature and __has_extension in GCC.
Currently the patch aims to implement all documented features (and some
undocumented ones) following the documentation at
https://clang.llvm.org/docs/LanguageExtensions.html with the following
omissions:
 - C++ type traits.
 - Objective-C-specific features.

C++ type traits aren't currently implemented since, as the clang
documentation notes, __has_builtin is the correct "modern" way to query
for these (which GCC already implements). Of course there's an argument
that we should recognize the legacy set of C++ type traits that can be
queried through __has_feature for backwards compatibility with older
code. I'm happy to do this if reviewers think that's a good idea.

There are some comments in the patch marked with XXX, I'm looking for
review comments from C/C++ maintainers on those areas in particular.

Bootstrapped/regtested on aarch64-linux-gnu. Any comments?

Thanks,
Alex

gcc/c-family/ChangeLog:

	PR c++/60512
	* c-common.cc (struct hf_feature_info): New.
	(has_generic_feature_p): New.
	* c-common.h (c_common_has_feature): New.
	(has_generic_feature_p): New.
	(has_lang_feature_p): New.
	* c-lex.cc (init_c_lex): Plumb through has_feature callback.
	(c_common_has_builtin): Adapt into more generic helper function.
	Rename to ...
	(c_common_lex_availability_macro): ... this.
	(c_common_has_feature): New.
	* c-ppoutput.cc (init_pp_output): Plumb through has_feature callback.

gcc/c/ChangeLog:

	PR c++/60512
	* c-objc-common.cc (struct c_feature_info): New.
	(has_lang_feature_p): New.

gcc/cp/ChangeLog:

	PR c++/60512
	* cp-objcp-common.cc (struct cp_feature_selector): New.
	(cp_feature_selector::has_feature): New.
	(struct cp_feature_info): New.
	(has_lang_feature_p): New.

gcc/ChangeLog:

	PR c++/60512
	* doc/cpp.texi: Document __has_{feature,extension}.

libcpp/ChangeLog:

	PR c++/60512
	* include/cpplib.h (struct cpp_callbacks): Add has_feature callback.
	(enum cpp_builtin_type): Add types for __has_{feature,extension}.
	* init.cc (builtin_array): Add entries for __has_{feature,extension}.
	* macro.cc (_cpp_builtin_macro_text): Handle
	BT_HAS_{FEATURE,EXTENSION}.

gcc/testsuite/ChangeLog:

	PR c++/60512
	* c-c++-common/has-feature-common.c: New test.
	* g++.dg/ext/has-feature.C: New test.
	* gcc.dg/asan/has-feature-asan.c: New test.
	* gcc.dg/has-feature.c: New test.

Comments

Jason Merrill May 11, 2023, 8:25 p.m. UTC | #1
On 5/9/23 08:07, Alex Coplan wrote:
> This patch implements clang's __has_feature and __has_extension in GCC.

Thanks!

> Currently the patch aims to implement all documented features (and some
> undocumented ones) following the documentation at
> https://clang.llvm.org/docs/LanguageExtensions.html with the following
> omissions:
>   - C++ type traits.
>   - Objective-C-specific features.
> 
> C++ type traits aren't currently implemented since, as the clang
> documentation notes, __has_builtin is the correct "modern" way to query
> for these (which GCC already implements). Of course there's an argument
> that we should recognize the legacy set of C++ type traits that can be
> queried through __has_feature for backwards compatibility with older
> code. I'm happy to do this if reviewers think that's a good idea.

That seems unnecessary unless there's a specific motivation.

> There are some comments in the patch marked with XXX, I'm looking for
> review comments from C/C++ maintainers on those areas in particular.
> 
> Bootstrapped/regtested on aarch64-linux-gnu. Any comments?

All the has_*_feature_p functions need to check flag_pedantic_errors, 
for compatibility with the Clang documented behavior "If the 
-pedantic-errors option is given, __has_extension is equivalent to 
__has_feature."

> +static const cp_feature_info cp_feature_table[] =
> +{
> +  { "cxx_exceptions", &flag_exceptions },
> +  { "cxx_rtti", &flag_rtti },
> +  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
> +  { "cxx_alias_templates", cxx11 },
> +  { "cxx_alignas", cxx11 },
> +  { "cxx_alignof", cxx11 },
> +  { "cxx_attributes", cxx11 },
> +  { "cxx_constexpr", cxx11 },
> +  { "cxx_constexpr_string_builtins", cxx11 },
> +  { "cxx_decltype", cxx11 },
> +  { "cxx_decltype_incomplete_return_types", cxx11 },
> +  { "cxx_default_function_template_args", cxx11 },
> +  { "cxx_defaulted_functions", cxx11 }, /* XXX: extension in c++98?  */

I'm not sure I see the benefit of advertising a lot of these as C++98 
extensions, even if we do accept them with a pedwarn by default.  The 
ones that indicate DRs like cxx_access_control_sfinae, yes, but I'm 
inclined to be conservative if it isn't an extension that libstdc++ 
relies on, like variadic templates or inline namespaces.  My concern is 
that important implementation is limited to C++11 mode even if we don't 
immediately give an error.  For instance,

struct A
{
   int i = 42;
   A() = default;
};

breaks in C++98 mode; even though we only warn for the two C++11 
features, trying to actually combine them fails.

So if there's a question, let's say no.

> +  { "cxx_delegating_constructors", { cxx11, cxx98 } },
> +  { "cxx_deleted_functions", cxx11 },
> +  { "cxx_explicit_conversions", { cxx11, cxx98 } },
> +  { "cxx_generalized_initializers", cxx11 },
> +  { "cxx_implicit_moves", cxx11 },
> +  { "cxx_inheriting_constructors", cxx11 }, /* XXX: extension in c++98?  */
> +  { "cxx_inline_namespaces", { cxx11, cxx98 } },
> +  { "cxx_lambdas", cxx11 }, /* XXX: extension in c++98?  */
> +  { "cxx_local_type_template_args", cxx11 },
> +  { "cxx_noexcept", cxx11 },
> +  { "cxx_nonstatic_member_init", { cxx11, cxx98 } },
> +  { "cxx_nullptr", cxx11 },
> +  { "cxx_override_control", { cxx11, cxx98 } },
> +  { "cxx_reference_qualified_functions", cxx11 },
> +  { "cxx_range_for", cxx11 },
> +  { "cxx_raw_string_literals", cxx11 },
> +  { "cxx_rvalue_references", cxx11 },
> +  { "cxx_static_assert", cxx11 },
> +  { "cxx_thread_local", cxx11 },
> +  { "cxx_auto_type", cxx11 },
> +  { "cxx_strong_enums", cxx11 },
> +  { "cxx_trailing_return", cxx11 },
> +  { "cxx_unicode_literals", cxx11 },
> +  { "cxx_unrestricted_unions", cxx11 },
> +  { "cxx_user_literals", cxx11 },
> +  { "cxx_variadic_templates", { cxx11, cxx98 } },
> +  { "cxx_binary_literals", { cxx14, cxx98 } },
> +  { "cxx_contextual_conversions", { cxx14, cxx98 } },
> +  { "cxx_decltype_auto", cxx14 },
> +  { "cxx_aggregate_nsdmi", cxx14 },
> +  { "cxx_init_captures", { cxx14, cxx11 } },
> +  { "cxx_generic_lambdas", cxx14 },
> +  { "cxx_relaxed_constexpr", cxx14 },
> +  { "cxx_return_type_deduction", cxx14 },
> +  { "cxx_variable_templates", { cxx14, cxx98 } },
> +  { "modules", &flag_modules },
Jonathan Wakely May 11, 2023, 9:26 p.m. UTC | #2
On Thu, 11 May 2023 at 21:25, Jason Merrill <jason@redhat.com> wrote:

> On 5/9/23 08:07, Alex Coplan wrote:
> > This patch implements clang's __has_feature and __has_extension in GCC.
>
> Thanks!
>
> > Currently the patch aims to implement all documented features (and some
> > undocumented ones) following the documentation at
> > https://clang.llvm.org/docs/LanguageExtensions.html with the following
> > omissions:
> >   - C++ type traits.
> >   - Objective-C-specific features.
> >
> > C++ type traits aren't currently implemented since, as the clang
> > documentation notes, __has_builtin is the correct "modern" way to query
> > for these (which GCC already implements). Of course there's an argument
> > that we should recognize the legacy set of C++ type traits that can be
> > queried through __has_feature for backwards compatibility with older
> > code. I'm happy to do this if reviewers think that's a good idea.
>
> That seems unnecessary unless there's a specific motivation.
>
> > There are some comments in the patch marked with XXX, I'm looking for
> > review comments from C/C++ maintainers on those areas in particular.
> >
> > Bootstrapped/regtested on aarch64-linux-gnu. Any comments?
>
> All the has_*_feature_p functions need to check flag_pedantic_errors,
> for compatibility with the Clang documented behavior "If the
> -pedantic-errors option is given, __has_extension is equivalent to
> __has_feature."
>
> > +static const cp_feature_info cp_feature_table[] =
> > +{
> > +  { "cxx_exceptions", &flag_exceptions },
> > +  { "cxx_rtti", &flag_rtti },
> > +  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
> > +  { "cxx_alias_templates", cxx11 },
> > +  { "cxx_alignas", cxx11 },
> > +  { "cxx_alignof", cxx11 },
> > +  { "cxx_attributes", cxx11 },
> > +  { "cxx_constexpr", cxx11 },
> > +  { "cxx_constexpr_string_builtins", cxx11 },
> > +  { "cxx_decltype", cxx11 },
> > +  { "cxx_decltype_incomplete_return_types", cxx11 },
> > +  { "cxx_default_function_template_args", cxx11 },
> > +  { "cxx_defaulted_functions", cxx11 }, /* XXX: extension in c++98?  */
>
> I'm not sure I see the benefit of advertising a lot of these as C++98
> extensions, even if we do accept them with a pedwarn by default.  The
> ones that indicate DRs like cxx_access_control_sfinae, yes, but I'm
> inclined to be conservative if it isn't an extension that libstdc++
> relies on, like variadic templates or inline namespaces.


FWIW, I think the only other C++11 feature that libstdc++ assumes is
unconditionally available in C++98 mode is 'long long' (which is
technically not defined until C99 and C++11).



> My concern is
> that important implementation is limited to C++11 mode even if we don't
> immediately give an error.  For instance,
>
> struct A
> {
>    int i = 42;
>    A() = default;
> };
>
> breaks in C++98 mode; even though we only warn for the two C++11
> features, trying to actually combine them fails.
>
> So if there's a question, let's say no.
>
> > +  { "cxx_delegating_constructors", { cxx11, cxx98 } },
> > +  { "cxx_deleted_functions", cxx11 },
> > +  { "cxx_explicit_conversions", { cxx11, cxx98 } },
> > +  { "cxx_generalized_initializers", cxx11 },
> > +  { "cxx_implicit_moves", cxx11 },
> > +  { "cxx_inheriting_constructors", cxx11 }, /* XXX: extension in
> c++98?  */
> > +  { "cxx_inline_namespaces", { cxx11, cxx98 } },
> > +  { "cxx_lambdas", cxx11 }, /* XXX: extension in c++98?  */
> > +  { "cxx_local_type_template_args", cxx11 },
> > +  { "cxx_noexcept", cxx11 },
> > +  { "cxx_nonstatic_member_init", { cxx11, cxx98 } },
> > +  { "cxx_nullptr", cxx11 },
> > +  { "cxx_override_control", { cxx11, cxx98 } },
> > +  { "cxx_reference_qualified_functions", cxx11 },
> > +  { "cxx_range_for", cxx11 },
> > +  { "cxx_raw_string_literals", cxx11 },
> > +  { "cxx_rvalue_references", cxx11 },
> > +  { "cxx_static_assert", cxx11 },
> > +  { "cxx_thread_local", cxx11 },
> > +  { "cxx_auto_type", cxx11 },
> > +  { "cxx_strong_enums", cxx11 },
> > +  { "cxx_trailing_return", cxx11 },
> > +  { "cxx_unicode_literals", cxx11 },
> > +  { "cxx_unrestricted_unions", cxx11 },
> > +  { "cxx_user_literals", cxx11 },
> > +  { "cxx_variadic_templates", { cxx11, cxx98 } },
> > +  { "cxx_binary_literals", { cxx14, cxx98 } },
> > +  { "cxx_contextual_conversions", { cxx14, cxx98 } },
> > +  { "cxx_decltype_auto", cxx14 },
> > +  { "cxx_aggregate_nsdmi", cxx14 },
> > +  { "cxx_init_captures", { cxx14, cxx11 } },
> > +  { "cxx_generic_lambdas", cxx14 },
> > +  { "cxx_relaxed_constexpr", cxx14 },
> > +  { "cxx_return_type_deduction", cxx14 },
> > +  { "cxx_variable_templates", { cxx14, cxx98 } },
> > +  { "modules", &flag_modules },
>
>
>
>
Iain Sandoe May 14, 2023, 4:05 p.m. UTC | #3
Hi Alex,

thanks for working on this.

I’ve applied this patch and evaluated on a few Darwin versions (which is the
target currently most affected, I believe):

> On 9 May 2023, at 13:07, Alex Coplan <alex.coplan@arm.com> wrote:

> This patch implements clang's __has_feature and __has_extension in GCC.

Thanks, this blocks consuming Darwin SDK headers “properly” (PR 90709 as
linked to  60512) (which is why I had a WIP patch too).

So I am very keen to see this land in GCC-14, but have some  issues to deal 
with and would be looking for ideas about how to handle them by extending or
amending the patch.

The main concern I have at the moment is that it seems to me that we need
more flexible and general predicates for declaring feature/ext support:

  a) on target (see below for examples)
  b) on potentially multiple flags and language version at the same time (see below)
  c) what about features that exist for a closed range of language versions?

As mentioned by Jakub in a conversation about this on irc (months ago!) the
current identifiers potentially clash with use symbols.

IFF we add feature designations (which IMO we should, since this approach does
help simplify testcases and configurations) we should add them into the
implementation namespace:

e.g. ( for C) 
_GNU_nested_functions or __nested_functions

> Currently the patch aims to implement all documented features (and some
> undocumented ones) following the documentation at
> https://clang.llvm.org/docs/LanguageExtensions.html

TL;DR 
without guards or target-specific opt out this breaks bootstrap on Darwin.

I believe the patch is operating correctly .. but it is now exposing problems in
the system Frameworks that (in this first example) is union of three bugs
in the headers and clang versions.

Darwin’s SDK headers are a pretty complex amalgamation of macro and
conditional code spanning both regular (i.e. /usr/include) style headers and
those found in frameworks.

This took a bit of tracking down.

The bootstrap fail occurs because (with the patch) we now invoke a macro
CF_ENUM in the CoreFoundation framework with extended args.

BUG1;  This macro expands to wrong code (AFAICT) for a typedef enum. 
BUG2;  However, Apple clang accepts the wrong code and treats it as typedef.
           (GCC and upstream clang both reject, although MSVC accepts)
BUG3: Upstream clang is disabling that error when it appears in system headers.

.. so that combination means that we have a broken core framework header that
went undetected by both Apple and upstream clang.

Now it is possible that the header could be fixed in some future edition of the
SDK.  However that does not help existing and previous SDKs (I do not recall
a fix ever being made of any SDK other than the current).  So I have to figure
out how to approach this.

1. We could ‘fixincludes’ the issues… except because of PR105719, we can’t
    because we do not have  fixincludes for frameworks yet ...
   [this is not probably impossible to arrange but it’s quite some work to make
    sure we interpose the fixes correctly]

2. We could allow specific targets to opt out of declaring that they have
  support for some specific feature (see also below).

(one reason to allow target opt-in/out of specific features)

> with the following omissions:

> - Objective-C-specific features.

I can clearly append the objective-c(++) cases to the end of the respective
lists, but then we need to make them conditional on language, version and
dialect (some will not be appropriate to GNU runtime).

this is why I think we need more flexible predicates on declaring features
and extensions.

----

index 2b4c82facf7..5b8429244b2 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc

+struct hf_feature_info

+  { "enumerator_attributes",		  0, 0 },
+  { "tls", 0, 0 },

Do all GCC targets support tls?

What about things like this:

 attribute_availability_tvos, 
 attribute_availability_watchos, 
 attribute_availability_driverkit, 
?

Even if they are implemented centrally, it is unlikely that all targets would want
to claim support (although note that the availability stuff does seem now to be
used on other platforms - like android, zos and hlsl.)

(non-bug-related reasons to consider target-specific predicates)

===

Once again, thanks for working on this - and I am keen to see it land,
Iain
Alex Coplan June 20, 2023, 12:30 p.m. UTC | #4
Hi Iain,

On 14/05/2023 17:05, Iain Sandoe wrote:
> Hi Alex,
> 
> thanks for working on this.
> 
> I’ve applied this patch and evaluated on a few Darwin versions (which is the
> target currently most affected, I believe):
> 
> > On 9 May 2023, at 13:07, Alex Coplan <alex.coplan@arm.com> wrote:
> 
> > This patch implements clang's __has_feature and __has_extension in GCC.
> 
> Thanks, this blocks consuming Darwin SDK headers “properly” (PR 90709 as
> linked to  60512) (which is why I had a WIP patch too).
> 
> So I am very keen to see this land in GCC-14, but have some  issues to deal 
> with and would be looking for ideas about how to handle them by extending or
> amending the patch.
> 
> The main concern I have at the moment is that it seems to me that we need
> more flexible and general predicates for declaring feature/ext support:
> 
>   a) on target (see below for examples)
>   b) on potentially multiple flags and language version at the same time (see below)
>   c) what about features that exist for a closed range of language versions?
> 
> As mentioned by Jakub in a conversation about this on irc (months ago!) the
> current identifiers potentially clash with use symbols.
> 
> IFF we add feature designations (which IMO we should, since this approach does
> help simplify testcases and configurations) we should add them into the
> implementation namespace:
> 
> e.g. ( for C) 
> _GNU_nested_functions or __nested_functions
> 
> > Currently the patch aims to implement all documented features (and some
> > undocumented ones) following the documentation at
> > https://clang.llvm.org/docs/LanguageExtensions.html
> 
> TL;DR 
> without guards or target-specific opt out this breaks bootstrap on Darwin.

Thanks for trying out the patch and pointing this out, this blocker has
now been addressed by relaxing the C++ parser as per
g:b106f11dc6adb8df15cc5c268896d314c76ca35f.

The patch can now survive bootstrap on Darwin (it looks like we'll need
to adjust some Objective-C++ tests in light of the new pedwarn, but that
looks to be straightforward).

<snip>

> (one reason to allow target opt-in/out of specific features)
> 
> > with the following omissions:
> 
> > - Objective-C-specific features.
> 
> I can clearly append the objective-c(++) cases to the end of the respective
> lists, but then we need to make them conditional on language, version and
> dialect (some will not be appropriate to GNU runtime).
> 
> this is why I think we need more flexible predicates on declaring features
> and extensions.

Would it help mitigate these concerns if I implemented some Objective-C
features as part of this patch (say, those implemented by your WIP
patch)?

My feeling is that the vast majority of extensions / features have
similar logic, so we should exploit that redundancy to keep things terse
in the encoding for the general case. Where we need more flexible
predicates (e.g. for objc_nonfragile_abi in your WIP patch), those can
be handled on a case-by-case basis by adding a new enumerator and logic
to handle that specially.

What do you think, does that sound OK to you?

> 
> ----
> 
> index 2b4c82facf7..5b8429244b2 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> 
> +struct hf_feature_info
> 
> +  { "enumerator_attributes",		  0, 0 },
> +  { "tls", 0, 0 },
> 
> Do all GCC targets support tls?

This is a good point. In clang, the features tls, c_thread_local, and
cxx_thread_local are all gated on whether the target supports TLS.
But in clang, it is a hard error to use TLS variables on a target which
doesn't support TLS. So it seems the features are used to check whether
code can make use of TLS constructs.

In GCC, AFAICT, TLS variables never get rejected, since GCC just uses
emulated TLS in the case that the target doesn't support TLS for real.

This then begs the question of how these features should be interpreted.
For c{,xx}_thread_local I'd expect that we want them to return true
whenever the language-level constructs are useable (even if we end up
using emutls).

I think it's defensible to take the position that GCC "always supports
TLS" since (AFAIK) you can make use of thread-local variables regardless
of whether the target really supports TLS (since you just get emutls if
it doesn't). So it's not clear which interpretation we should use for
the "tls" feature.

> 
> What about things like this:
> 
>  attribute_availability_tvos, 
>  attribute_availability_watchos, 
>  attribute_availability_driverkit, 

FWIW, clang looks to define these unconditionally, so restricting these
to a given target would be deviating from its precedent.

However, I don't think it would be hard to extend the implementation in
this patch to support target-specific features if required. I think
perhaps a langhook that targets can call to add their own features would
be a reasonable approach.

> ?
> 
> Even if they are implemented centrally, it is unlikely that all targets would want
> to claim support (although note that the availability stuff does seem now to be
> used on other platforms - like android, zos and hlsl.)
> 
> (non-bug-related reasons to consider target-specific predicates)
> 
> ===
> 
> Once again, thanks for working on this - and I am keen to see it land,
> Iain
> 

Thanks,
Alex
Iain Sandoe June 20, 2023, 2:08 p.m. UTC | #5
Hi Alex

again, thanks for working on this and for fixing the SDK blocker.

> On 20 Jun 2023, at 13:30, Alex Coplan <alex.coplan@arm.com> wrote:
> 

> The patch can now survive bootstrap on Darwin (it looks like we'll need
> to adjust some Objective-C++ tests in light of the new pedwarn, but that
> looks to be straightforward).

Yes, I’ll deal with that soon (I was trying to decide whether to fix the the
header we have copied from GNUStep, or whether to mark it as a system
header).

>> (one reason to allow target opt-in/out of specific features)
>> 
>>> with the following omissions:
>> 
>>> - Objective-C-specific features.
>> 
>> I can clearly append the objective-c(++) cases to the end of the respective
>> lists, but then we need to make them conditional on language, version and
>> dialect (some will not be appropriate to GNU runtime).
>> 
>> this is why I think we need more flexible predicates on declaring features
>> and extensions.
> 
> Would it help mitigate these concerns if I implemented some Objective-C
> features as part of this patch (say, those implemented by your WIP
> patch)?
> 
> My feeling is that the vast majority of extensions / features have
> similar logic, so we should exploit that redundancy to keep things terse
> in the encoding for the general case. Where we need more flexible
> predicates (e.g. for objc_nonfragile_abi in your WIP patch), those can
> be handled on a case-by-case basis by adding a new enumerator and logic
> to handle that specially.
> 
> What do you think, does that sound OK to you?

Sketching out what you have in mind using one or two examples would be
helpful.  Again, the fact that some of the answers are target-dependent, is
what makes me think of needing a little more generality.

>> What about things like this:
>> 
>> attribute_availability_tvos, 
>> attribute_availability_watchos, 
>> attribute_availability_driverkit, 
> 
> FWIW, clang looks to define these unconditionally, so restricting these
> to a given target would be deviating from its precedent.

Hmm.. i did not check that although (for the sake of keeping target-specific
code localised) my current availabilty attribute implementation is Darwin-
specific.

Having said that, interoperability with clang is also a very useful goal - for
Darwin, the SDK headers have only been (fully) tested with clang up to
now and I am sure we will find more gotchas as we expand what we can
parse.

> However, I don't think it would be hard to extend the implementation in
> this patch to support target-specific features if required. I think
> perhaps a langhook that targets can call to add their own features would
> be a reasonable approach.

Indeed, that could work if the result is needed later than pre-processing.

In my patch, IIRC, I added another entry to the libcpp callbacks to handle
target-specific __has_xxxx queries.

cheers
Iain
Alex Coplan July 6, 2023, 1:58 p.m. UTC | #6
Hi Jason,

On 11/05/2023 16:25, Jason Merrill wrote:
> On 5/9/23 08:07, Alex Coplan wrote:
> > This patch implements clang's __has_feature and __has_extension in GCC.
> 
> Thanks!

Thanks a lot for the review, I posted a v2 patch incorporating your
feedback here:
https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623057.html

do the C++ parts of the patch look OK?

Thanks,
Alex

> 
> > Currently the patch aims to implement all documented features (and some
> > undocumented ones) following the documentation at
> > https://clang.llvm.org/docs/LanguageExtensions.html with the following
> > omissions:
> >   - C++ type traits.
> >   - Objective-C-specific features.
> > 
> > C++ type traits aren't currently implemented since, as the clang
> > documentation notes, __has_builtin is the correct "modern" way to query
> > for these (which GCC already implements). Of course there's an argument
> > that we should recognize the legacy set of C++ type traits that can be
> > queried through __has_feature for backwards compatibility with older
> > code. I'm happy to do this if reviewers think that's a good idea.
> 
> That seems unnecessary unless there's a specific motivation.
> 
> > There are some comments in the patch marked with XXX, I'm looking for
> > review comments from C/C++ maintainers on those areas in particular.
> > 
> > Bootstrapped/regtested on aarch64-linux-gnu. Any comments?
> 
> All the has_*_feature_p functions need to check flag_pedantic_errors, for
> compatibility with the Clang documented behavior "If the -pedantic-errors
> option is given, __has_extension is equivalent to __has_feature."
> 
> > +static const cp_feature_info cp_feature_table[] =
> > +{
> > +  { "cxx_exceptions", &flag_exceptions },
> > +  { "cxx_rtti", &flag_rtti },
> > +  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
> > +  { "cxx_alias_templates", cxx11 },
> > +  { "cxx_alignas", cxx11 },
> > +  { "cxx_alignof", cxx11 },
> > +  { "cxx_attributes", cxx11 },
> > +  { "cxx_constexpr", cxx11 },
> > +  { "cxx_constexpr_string_builtins", cxx11 },
> > +  { "cxx_decltype", cxx11 },
> > +  { "cxx_decltype_incomplete_return_types", cxx11 },
> > +  { "cxx_default_function_template_args", cxx11 },
> > +  { "cxx_defaulted_functions", cxx11 }, /* XXX: extension in c++98?  */
> 
> I'm not sure I see the benefit of advertising a lot of these as C++98
> extensions, even if we do accept them with a pedwarn by default.  The ones
> that indicate DRs like cxx_access_control_sfinae, yes, but I'm inclined to
> be conservative if it isn't an extension that libstdc++ relies on, like
> variadic templates or inline namespaces.  My concern is that important
> implementation is limited to C++11 mode even if we don't immediately give an
> error.  For instance,
> 
> struct A
> {
>   int i = 42;
>   A() = default;
> };
> 
> breaks in C++98 mode; even though we only warn for the two C++11 features,
> trying to actually combine them fails.
> 
> So if there's a question, let's say no.
> 
> > +  { "cxx_delegating_constructors", { cxx11, cxx98 } },
> > +  { "cxx_deleted_functions", cxx11 },
> > +  { "cxx_explicit_conversions", { cxx11, cxx98 } },
> > +  { "cxx_generalized_initializers", cxx11 },
> > +  { "cxx_implicit_moves", cxx11 },
> > +  { "cxx_inheriting_constructors", cxx11 }, /* XXX: extension in c++98?  */
> > +  { "cxx_inline_namespaces", { cxx11, cxx98 } },
> > +  { "cxx_lambdas", cxx11 }, /* XXX: extension in c++98?  */
> > +  { "cxx_local_type_template_args", cxx11 },
> > +  { "cxx_noexcept", cxx11 },
> > +  { "cxx_nonstatic_member_init", { cxx11, cxx98 } },
> > +  { "cxx_nullptr", cxx11 },
> > +  { "cxx_override_control", { cxx11, cxx98 } },
> > +  { "cxx_reference_qualified_functions", cxx11 },
> > +  { "cxx_range_for", cxx11 },
> > +  { "cxx_raw_string_literals", cxx11 },
> > +  { "cxx_rvalue_references", cxx11 },
> > +  { "cxx_static_assert", cxx11 },
> > +  { "cxx_thread_local", cxx11 },
> > +  { "cxx_auto_type", cxx11 },
> > +  { "cxx_strong_enums", cxx11 },
> > +  { "cxx_trailing_return", cxx11 },
> > +  { "cxx_unicode_literals", cxx11 },
> > +  { "cxx_unrestricted_unions", cxx11 },
> > +  { "cxx_user_literals", cxx11 },
> > +  { "cxx_variadic_templates", { cxx11, cxx98 } },
> > +  { "cxx_binary_literals", { cxx14, cxx98 } },
> > +  { "cxx_contextual_conversions", { cxx14, cxx98 } },
> > +  { "cxx_decltype_auto", cxx14 },
> > +  { "cxx_aggregate_nsdmi", cxx14 },
> > +  { "cxx_init_captures", { cxx14, cxx11 } },
> > +  { "cxx_generic_lambdas", cxx14 },
> > +  { "cxx_relaxed_constexpr", cxx14 },
> > +  { "cxx_return_type_deduction", cxx14 },
> > +  { "cxx_variable_templates", { cxx14, cxx98 } },
> > +  { "modules", &flag_modules },
> 
> 
>
Alex Coplan July 6, 2023, 2:01 p.m. UTC | #7
Hi Iain,

On 20/06/2023 15:08, Iain Sandoe wrote:
> Hi Alex
> 
> again, thanks for working on this and for fixing the SDK blocker.
> 
> > On 20 Jun 2023, at 13:30, Alex Coplan <alex.coplan@arm.com> wrote:
> > 
> 
> > The patch can now survive bootstrap on Darwin (it looks like we'll need
> > to adjust some Objective-C++ tests in light of the new pedwarn, but that
> > looks to be straightforward).
> 
> Yes, I’ll deal with that soon (I was trying to decide whether to fix the the
> header we have copied from GNUStep, or whether to mark it as a system
> header).
> 
> >> (one reason to allow target opt-in/out of specific features)
> >> 
> >>> with the following omissions:
> >> 
> >>> - Objective-C-specific features.
> >> 
> >> I can clearly append the objective-c(++) cases to the end of the respective
> >> lists, but then we need to make them conditional on language, version and
> >> dialect (some will not be appropriate to GNU runtime).
> >> 
> >> this is why I think we need more flexible predicates on declaring features
> >> and extensions.
> > 
> > Would it help mitigate these concerns if I implemented some Objective-C
> > features as part of this patch (say, those implemented by your WIP
> > patch)?
> > 
> > My feeling is that the vast majority of extensions / features have
> > similar logic, so we should exploit that redundancy to keep things terse
> > in the encoding for the general case. Where we need more flexible
> > predicates (e.g. for objc_nonfragile_abi in your WIP patch), those can
> > be handled on a case-by-case basis by adding a new enumerator and logic
> > to handle that specially.
> > 
> > What do you think, does that sound OK to you?
> 
> Sketching out what you have in mind using one or two examples would be
> helpful.  Again, the fact that some of the answers are target-dependent, is
> what makes me think of needing a little more generality.

FWIW I've implemented some Objective-C features (those from your WIP patch)
in a v2 patch here:

https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623057.html

I also tweaked the design to be closer to your patch in that we now have a hash
table which allows for registering features dynamically. Hopefully it's clear
that it should be easier to handle target-specific features in that version.

Any thoughts on the new version?

Thanks,
Alex

> 
> >> What about things like this:
> >> 
> >> attribute_availability_tvos, 
> >> attribute_availability_watchos, 
> >> attribute_availability_driverkit, 
> > 
> > FWIW, clang looks to define these unconditionally, so restricting these
> > to a given target would be deviating from its precedent.
> 
> Hmm.. i did not check that although (for the sake of keeping target-specific
> code localised) my current availabilty attribute implementation is Darwin-
> specific.
> 
> Having said that, interoperability with clang is also a very useful goal - for
> Darwin, the SDK headers have only been (fully) tested with clang up to
> now and I am sure we will find more gotchas as we expand what we can
> parse.
> 
> > However, I don't think it would be hard to extend the implementation in
> > this patch to support target-specific features if required. I think
> > perhaps a langhook that targets can call to add their own features would
> > be a reasonable approach.
> 
> Indeed, that could work if the result is needed later than pre-processing.
> 
> In my patch, IIRC, I added another entry to the libcpp callbacks to handle
> target-specific __has_xxxx queries.
> 
> cheers
> Iain
> 
>
Iain Sandoe July 6, 2023, 3:23 p.m. UTC | #8
Hi Alex,

> On 6 Jul 2023, at 15:01, Alex Coplan <alex.coplan@arm.com> wrote:
> 
> On 20/06/2023 15:08, Iain Sandoe wrote:

>> again, thanks for working on this and for fixing the SDK blocker.
>> 
>>> On 20 Jun 2023, at 13:30, Alex Coplan <alex.coplan@arm.com> wrote:
>>> 
>> 
>>> The patch can now survive bootstrap on Darwin (it looks like we'll need
>>> to adjust some Objective-C++ tests in light of the new pedwarn, but that
>>> looks to be straightforward).
>> 
>> Yes, I’ll deal with that soon (I was trying to decide whether to fix the the
>> header we have copied from GNUStep, or whether to mark it as a system
>> header).
>> 
>>>> (one reason to allow target opt-in/out of specific features)
>>>> 
>>>>> with the following omissions:
>>>> 
>>>>> - Objective-C-specific features.
>>>> 
>>>> I can clearly append the objective-c(++) cases to the end of the respective
>>>> lists, but then we need to make them conditional on language, version and
>>>> dialect (some will not be appropriate to GNU runtime).
>>>> 
>>>> this is why I think we need more flexible predicates on declaring features
>>>> and extensions.
>>> 
>>> Would it help mitigate these concerns if I implemented some Objective-C
>>> features as part of this patch (say, those implemented by your WIP
>>> patch)?
>>> 
>>> My feeling is that the vast majority of extensions / features have
>>> similar logic, so we should exploit that redundancy to keep things terse
>>> in the encoding for the general case. Where we need more flexible
>>> predicates (e.g. for objc_nonfragile_abi in your WIP patch), those can
>>> be handled on a case-by-case basis by adding a new enumerator and logic
>>> to handle that specially.
>>> 
>>> What do you think, does that sound OK to you?
>> 
>> Sketching out what you have in mind using one or two examples would be
>> helpful.  Again, the fact that some of the answers are target-dependent, is
>> what makes me think of needing a little more generality.
> 
> FWIW I've implemented some Objective-C features (those from your WIP patch)
> in a v2 patch here:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623057.html
> 
> I also tweaked the design to be closer to your patch in that we now have a hash
> table which allows for registering features dynamically. Hopefully it's clear
> that it should be easier to handle target-specific features in that version.
> 
> Any thoughts on the new version?

Yes, I’ve tried it (together with some of my pending patches) on a few systems and
it LGTM - agreed we can probably implement a target hook if/when that becomes
necessary to register target-specific cases.

The Objective-C parts are OK (when the rest is approved)

thanks again for working on this.
Iain

> 
> Thanks,
> Alex
> 
>> 
>>>> What about things like this:
>>>> 
>>>> attribute_availability_tvos, 
>>>> attribute_availability_watchos, 
>>>> attribute_availability_driverkit, 
>>> 
>>> FWIW, clang looks to define these unconditionally, so restricting these
>>> to a given target would be deviating from its precedent.
>> 
>> Hmm.. i did not check that although (for the sake of keeping target-specific
>> code localised) my current availabilty attribute implementation is Darwin-
>> specific.
>> 
>> Having said that, interoperability with clang is also a very useful goal - for
>> Darwin, the SDK headers have only been (fully) tested with clang up to
>> now and I am sure we will find more gotchas as we expand what we can
>> parse.
>> 
>>> However, I don't think it would be hard to extend the implementation in
>>> this patch to support target-specific features if required. I think
>>> perhaps a langhook that targets can call to add their own features would
>>> be a reasonable approach.
>> 
>> Indeed, that could work if the result is needed later than pre-processing.
>> 
>> In my patch, IIRC, I added another entry to the libcpp callbacks to handle
>> target-specific __has_xxxx queries.
>> 
>> cheers
>> Iain
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 2b4c82facf7..5b8429244b2 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -311,6 +311,34 @@  const struct fname_var_t fname_vars[] =
   {NULL, 0, 0},
 };
 
+enum
+{
+  HF_FLAG_EXT = 1,	/* Available only as an extension.  */
+  HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
+};
+
+struct hf_feature_info
+{
+  const char *ident;
+  unsigned flags;
+  unsigned mask;
+};
+
+static const hf_feature_info has_feature_table[] =
+{
+  { "address_sanitizer",	    HF_FLAG_SANITIZE, SANITIZE_ADDRESS },
+  { "thread_sanitizer",		    HF_FLAG_SANITIZE, SANITIZE_THREAD },
+  { "leak_sanitizer",		    HF_FLAG_SANITIZE, SANITIZE_LEAK },
+  { "hwaddress_sanitizer",	    HF_FLAG_SANITIZE, SANITIZE_HWADDRESS },
+  { "undefined_behavior_sanitizer", HF_FLAG_SANITIZE, SANITIZE_UNDEFINED },
+  { "attribute_deprecated_with_message",  0, 0 },
+  { "attribute_unavailable_with_message", 0, 0 },
+  { "enumerator_attributes",		  0, 0 },
+  { "tls", 0, 0 },
+  { "gnu_asm_goto_with_outputs",	  HF_FLAG_EXT, 0 },
+  { "gnu_asm_goto_with_outputs_full",	  HF_FLAG_EXT, 0 }
+};
+
 /* Global visibility options.  */
 struct visibility_flags visibility_options;
 
@@ -9545,4 +9573,25 @@  c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+bool
+has_generic_feature_p (const char *feat, bool strict_p)
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
+    {
+      const hf_feature_info *info = has_feature_table + i;
+      if (strcmp (info->ident, feat))
+	continue;
+
+      if ((info->flags & HF_FLAG_EXT) && strict_p)
+	return false;
+
+      if (info->flags & HF_FLAG_SANITIZE)
+	return flag_sanitize & info->mask;
+
+      return true;
+    }
+
+  return has_lang_feature_p (feat, strict_p);
+}
+
 #include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index f96350b64af..38fd30312a0 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1121,6 +1121,9 @@  extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level,
      ATTRIBUTE_GCC_DIAG(5,0);
 extern int c_common_has_attribute (cpp_reader *, bool);
 extern int c_common_has_builtin (cpp_reader *);
+extern int c_common_has_feature (cpp_reader *, bool);
+extern bool has_generic_feature_p (const char *, bool);
+extern bool has_lang_feature_p (const char *, bool);
 
 extern bool parse_optimize_options (tree, bool);
 
diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
index 6eb0fae2f53..3301d90d6ab 100644
--- a/gcc/c-family/c-lex.cc
+++ b/gcc/c-family/c-lex.cc
@@ -82,6 +82,7 @@  init_c_lex (void)
   cb->read_pch = c_common_read_pch;
   cb->has_attribute = c_common_has_attribute;
   cb->has_builtin = c_common_has_builtin;
+  cb->has_feature = c_common_has_feature;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
   cb->get_suggestion = cb_get_suggestion;
   cb->remap_filename = remap_macro_filename;
@@ -429,16 +430,16 @@  c_common_has_attribute (cpp_reader *pfile, bool std_syntax)
   return result;
 }
 
-/* Callback for has_builtin.  */
+/* Helper for __has_{builtin,feature,extension}.  */
 
-int
-c_common_has_builtin (cpp_reader *pfile)
+static const char *
+c_common_lex_availability_macro (cpp_reader *pfile, const char *builtin)
 {
   const cpp_token *token = get_token_no_padding (pfile);
   if (token->type != CPP_OPEN_PAREN)
     {
       cpp_error (pfile, CPP_DL_ERROR,
-		 "missing '(' after \"__has_builtin\"");
+		 "missing '(' after \"__has_%s\"", builtin);
       return 0;
     }
 
@@ -458,7 +459,7 @@  c_common_has_builtin (cpp_reader *pfile)
   else
     {
       cpp_error (pfile, CPP_DL_ERROR,
-		 "macro \"__has_builtin\" requires an identifier");
+		 "macro \"__has_%s\" requires an identifier", builtin);
       if (token->type == CPP_CLOSE_PAREN)
 	return 0;
     }
@@ -477,9 +478,35 @@  c_common_has_builtin (cpp_reader *pfile)
 	break;
     }
 
+  return name;
+}
+
+/* Callback for has_builtin.  */
+
+int
+c_common_has_builtin (cpp_reader *pfile)
+{
+  const char *name = c_common_lex_availability_macro (pfile, "builtin");
+  if (!name)
+    return 0;
+
   return names_builtin_p (name);
 }
 
+/* Callback for has_feature.  STRICT_P is true for has_feature and false
+   for has_extension.  */
+
+int
+c_common_has_feature (cpp_reader *pfile, bool strict_p)
+{
+  const char *builtin = strict_p ? "feature" : "extension";
+  const char *name = c_common_lex_availability_macro (pfile, builtin);
+  if (!name)
+    return 0;
+
+  return has_generic_feature_p (name, strict_p);
+}
+
 
 /* Read a token and return its type.  Fill *VALUE with its value, if
    applicable.  Fill *CPP_FLAGS with the token's flags, if it is
diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc
index 4aa2bef2c0f..a1488c6f086 100644
--- a/gcc/c-family/c-ppoutput.cc
+++ b/gcc/c-family/c-ppoutput.cc
@@ -162,6 +162,7 @@  init_pp_output (FILE *out_stream)
 
   cb->has_attribute = c_common_has_attribute;
   cb->has_builtin = c_common_has_builtin;
+  cb->has_feature = c_common_has_feature;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
   cb->remap_filename = remap_macro_filename;
 
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index e4aed61ed00..30e6c491421 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -34,6 +34,48 @@  along with GCC; see the file COPYING3.  If not see
 static bool c_tree_printer (pretty_printer *, text_info *, const char *,
 			    int, bool, bool, bool, bool *, const char **);
 
+struct c_feature_info
+{
+  const char *ident;
+  const int *enable_flag;
+};
+
+static const c_feature_info c_feature_table[] =
+{
+  { "c_alignas", &flag_isoc11 },
+  { "c_alignof", &flag_isoc11 },
+  { "c_atomic", &flag_isoc11 },
+  { "c_generic_selections", &flag_isoc11 },
+  { "c_static_assert", &flag_isoc11 },
+  { "c_thread_local", &flag_isoc11 },
+
+  /* XXX: Binary literals are available as a standard feature in
+     C2x.  They are standardised in C++14 and available as an extension
+     in all C versions.  Would it make more sense to have
+     cxx_binary_literals always report as an extension (in C) and add a
+     new c_binary_literals that reports as a feature for -std=c2x and an
+     extension below that?  */
+  { "cxx_binary_literals", &flag_isoc2x }
+};
+
+bool
+has_lang_feature_p (const char *feat, bool strict_p)
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
+    {
+      const c_feature_info *info = c_feature_table + i;
+      if (strcmp (info->ident, feat))
+	continue;
+
+      if (info->enable_flag && strict_p && !*info->enable_flag)
+	return false;
+
+      return true;
+    }
+
+  return false;
+}
+
 bool
 c_missing_noreturn_ok_p (tree decl)
 {
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 93b027b80ce..494f72b5ca1 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -23,10 +23,127 @@  along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "cp-tree.h"
 #include "cp-objcp-common.h"
+#include "c-family/c-common.h"
 #include "dwarf2.h"
 #include "stringpool.h"
 #include "contracts.h"
 
+struct cp_feature_selector
+{
+  enum
+  {
+    DIALECT,
+    FLAG
+  } kind;
+
+  union
+  {
+    const int *enable_flag;
+    struct {
+      enum cxx_dialect feat;
+      enum cxx_dialect ext;
+    } dialect;
+  };
+
+  constexpr cp_feature_selector (const int *flag)
+    : kind (FLAG), enable_flag (flag) {}
+  constexpr cp_feature_selector (enum cxx_dialect feat,
+				 enum cxx_dialect ext)
+    : kind (DIALECT), dialect{feat, ext} {}
+  constexpr cp_feature_selector (enum cxx_dialect feat)
+    : cp_feature_selector (feat, feat) {}
+
+  bool has_feature (bool strict_p) const;
+};
+
+bool cp_feature_selector::has_feature (bool strict_p) const
+{
+  switch (kind)
+    {
+    case DIALECT:
+      if (!strict_p)
+	return cxx_dialect >= dialect.ext;
+      return cxx_dialect >= dialect.feat;
+    case FLAG:
+      return *enable_flag;
+    }
+  gcc_unreachable ();
+}
+
+struct cp_feature_info
+{
+  const char *ident;
+  cp_feature_selector selector;
+};
+
+static const cp_feature_info cp_feature_table[] =
+{
+  { "cxx_exceptions", &flag_exceptions },
+  { "cxx_rtti", &flag_rtti },
+  { "cxx_access_control_sfinae", { cxx11, cxx98 } },
+  { "cxx_alias_templates", cxx11 },
+  { "cxx_alignas", cxx11 },
+  { "cxx_alignof", cxx11 },
+  { "cxx_attributes", cxx11 },
+  { "cxx_constexpr", cxx11 },
+  { "cxx_constexpr_string_builtins", cxx11 },
+  { "cxx_decltype", cxx11 },
+  { "cxx_decltype_incomplete_return_types", cxx11 },
+  { "cxx_default_function_template_args", cxx11 },
+  { "cxx_defaulted_functions", cxx11 }, /* XXX: extension in c++98?  */
+  { "cxx_delegating_constructors", { cxx11, cxx98 } },
+  { "cxx_deleted_functions", cxx11 },
+  { "cxx_explicit_conversions", { cxx11, cxx98 } },
+  { "cxx_generalized_initializers", cxx11 },
+  { "cxx_implicit_moves", cxx11 },
+  { "cxx_inheriting_constructors", cxx11 }, /* XXX: extension in c++98?  */
+  { "cxx_inline_namespaces", { cxx11, cxx98 } },
+  { "cxx_lambdas", cxx11 }, /* XXX: extension in c++98?  */
+  { "cxx_local_type_template_args", cxx11 },
+  { "cxx_noexcept", cxx11 },
+  { "cxx_nonstatic_member_init", { cxx11, cxx98 } },
+  { "cxx_nullptr", cxx11 },
+  { "cxx_override_control", { cxx11, cxx98 } },
+  { "cxx_reference_qualified_functions", cxx11 },
+  { "cxx_range_for", cxx11 },
+  { "cxx_raw_string_literals", cxx11 },
+  { "cxx_rvalue_references", cxx11 },
+  { "cxx_static_assert", cxx11 },
+  { "cxx_thread_local", cxx11 },
+  { "cxx_auto_type", cxx11 },
+  { "cxx_strong_enums", cxx11 },
+  { "cxx_trailing_return", cxx11 },
+  { "cxx_unicode_literals", cxx11 },
+  { "cxx_unrestricted_unions", cxx11 },
+  { "cxx_user_literals", cxx11 },
+  { "cxx_variadic_templates", { cxx11, cxx98 } },
+  { "cxx_binary_literals", { cxx14, cxx98 } },
+  { "cxx_contextual_conversions", { cxx14, cxx98 } },
+  { "cxx_decltype_auto", cxx14 },
+  { "cxx_aggregate_nsdmi", cxx14 },
+  { "cxx_init_captures", { cxx14, cxx11 } },
+  { "cxx_generic_lambdas", cxx14 },
+  { "cxx_relaxed_constexpr", cxx14 },
+  { "cxx_return_type_deduction", cxx14 },
+  { "cxx_variable_templates", { cxx14, cxx98 } },
+  { "modules", &flag_modules },
+};
+
+bool
+has_lang_feature_p (const char *feat, bool strict_p)
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
+    {
+      const cp_feature_info *info = cp_feature_table + i;
+      if (strcmp (info->ident, feat))
+	continue;
+
+      return info->selector.has_feature (strict_p);
+    }
+
+  return false;
+}
+
 /* Special routine to get the alias set for C++.  */
 
 alias_set_type
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index b0a2ce3ac6b..9ed96786249 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -3198,6 +3198,8 @@  directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}.
 * @code{__has_cpp_attribute}::
 * @code{__has_c_attribute}::
 * @code{__has_builtin}::
+* @code{__has_feature}::
+* @code{__has_extension}::
 * @code{__has_include}::
 @end menu
 
@@ -3560,6 +3562,30 @@  the operator is as follows:
 #endif
 @end smallexample
 
+@node @code{__has_feature}
+@subsection @code{__has_feature}
+@cindex @code{__has_feature}
+
+The special operator @code{__has_feature (@var{operand})} may be used in
+constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
+expressions to test whether the identifier given in @var{operand} is recognized
+as a feature supported by GCC given the current options and, in the case of
+standard language features, whether the feature is available in the chosen
+version of the language standard.
+
+@node @code{__has_extension}
+@subsection @code{__has_extension}
+@cindex @code{__has_extension}
+
+The special operator @code{__has_extension (@var{operand})} may be used in
+constant integer contexts and in preprocessor @samp{#if} and @samp{#elif}
+expressions to test whether the identifier given in @var{operand} is recognized
+as an extension supported by GCC given the current options.  In any given
+context, the features accepted by @code{__has_extension} are a strict superset
+of those accepted by @code{__has_feature}.  Unlike @code{__has_feature},
+@code{__has_extension} tests whether a given feature is available regardless of
+strict language standards conformance.
+
 @node @code{__has_include}
 @subsection @code{__has_include}
 @cindex @code{__has_include}
diff --git a/gcc/testsuite/c-c++-common/has-feature-common.c b/gcc/testsuite/c-c++-common/has-feature-common.c
new file mode 100644
index 00000000000..9a57b11e8e2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/has-feature-common.c
@@ -0,0 +1,57 @@ 
+/* { dg-do compile } */
+/* Test __has_{feature,extension} for generic features.  */
+
+#define FEAT(x) (__has_feature (x) && __has_extension (x))
+#define EXT(x) (__has_extension (x) && !__has_feature (x))
+
+#if __has_feature (unknown_feature) || __has_extension (unknown_feature)
+#error unknown feature is known!
+#endif
+
+#if !__has_extension (gnu_asm_goto_with_outputs)
+#error
+#endif
+
+#if !EXT (gnu_asm_goto_with_outputs)
+#error
+#endif
+
+#if !EXT (gnu_asm_goto_with_outputs_full)
+#error
+#endif
+
+#if !FEAT (enumerator_attributes)
+#error
+#endif
+
+#if !FEAT (attribute_deprecated_with_message)
+#error
+#endif
+
+#if !FEAT (attribute_unavailable_with_message)
+#error
+#endif
+
+#if !FEAT (enumerator_attributes)
+#error
+#endif
+
+#if !FEAT (tls)
+#error
+#endif
+
+#if defined (__SANITIZE_ADDRESS__) != __has_feature (address_sanitizer)
+#error
+#endif
+
+#if defined (__SANITIZE_ADDRESS__) != __has_extension (address_sanitizer)
+#error
+#endif
+
+#if defined (__SANITIZE_THREAD__) != __has_feature (thread_sanitizer)
+#error
+#endif
+
+#if defined (__SANITIZE_THREAD__) != __has_extension (thread_sanitizer)
+#error
+#endif
diff --git a/gcc/testsuite/g++.dg/ext/has-feature.C b/gcc/testsuite/g++.dg/ext/has-feature.C
new file mode 100644
index 00000000000..58d0057c30d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/has-feature.C
@@ -0,0 +1,225 @@ 
+// { dg-do compile }
+
+#define FEAT(x) (__has_feature(x) && __has_extension(x))
+#define CXX11 (__cplusplus >= 201103L)
+#define CXX14 (__cplusplus >= 201402L)
+
+#if !FEAT(cxx_exceptions) || !FEAT(cxx_rtti)
+#error
+#endif
+
+#if __has_feature (cxx_access_control_sfinae) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_access_control_sfinae)
+#error
+#endif
+
+#if FEAT(cxx_alias_templates) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_alignas) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_alignof) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_attributes) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_constexpr) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_decltype) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_decltype_incomplete_return_types) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_default_function_template_args) != CXX11
+#error
+#endif
+
+#if FEAT(cxx_defaulted_functions) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_delegating_constructors)
+#error
+#endif
+
+#if __has_feature (cxx_delegating_constructors) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_deleted_functions) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_explicit_conversions)
+#error
+#endif
+
+#if __has_feature (cxx_explicit_conversions) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_generalized_initializers) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_implicit_moves) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_inheriting_constructors) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_inline_namespaces)
+#error
+#endif
+
+#if __has_feature (cxx_inline_namespaces) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_lambdas) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_local_type_template_args) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_noexcept) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_nonstatic_member_init)
+#error
+#endif
+
+#if __has_feature (cxx_nonstatic_member_init) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_nullptr) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_override_control)
+#error
+#endif
+
+#if __has_feature (cxx_override_control) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_reference_qualified_functions) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_range_for) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_raw_string_literals) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_rvalue_references) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_static_assert) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_thread_local) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_auto_type) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_strong_enums) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_trailing_return) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_unicode_literals) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_unrestricted_unions) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_user_literals) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_variadic_templates)
+#error
+#endif
+
+#if __has_feature (cxx_variadic_templates) != CXX11
+#error
+#endif
+
+#if !__has_extension (cxx_binary_literals)
+#error
+#endif
+
+#if __has_feature (cxx_binary_literals) != CXX14
+#error
+#endif
+
+#if FEAT (cxx_decltype_auto) != CXX14
+#error
+#endif
+
+#if FEAT (cxx_aggregate_nsdmi) != CXX14
+#error
+#endif
+
+#if __has_extension (cxx_init_captures) != CXX11
+#error
+#endif
+
+#if __has_feature (cxx_init_captures) != CXX14
+#error
+#endif
+
+#if FEAT (cxx_generic_lambdas) != CXX14
+#error
+#endif
+
+#if FEAT (cxx_relaxed_constexpr) != CXX14
+#error
+#endif
+
+#if FEAT (cxx_return_type_deduction) != CXX14
+#error
+#endif
+
+#if !__has_extension (cxx_variable_templates)
+#error
+#endif
+
+#if __has_feature (cxx_variable_templates) != CXX14
+#error
+#endif
diff --git a/gcc/testsuite/gcc.dg/asan/has-feature-asan.c b/gcc/testsuite/gcc.dg/asan/has-feature-asan.c
new file mode 100644
index 00000000000..810b69b8fc8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/has-feature-asan.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=address" } */
+#define FEAT(x) (__has_feature (x) && __has_extension (x))
+#if !FEAT (address_sanitizer)
+#error
+#endif
diff --git a/gcc/testsuite/gcc.dg/has-feature.c b/gcc/testsuite/gcc.dg/has-feature.c
new file mode 100644
index 00000000000..19d27699a92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/has-feature.c
@@ -0,0 +1,63 @@ 
+/* { dg-do compile } */
+/* Test __has_{feature,extension} for C language features.  */
+
+#if !__has_extension (c_alignas) || !__has_extension (c_alignof)
+#error
+#endif
+
+#if !__has_extension (c_atomic) || !__has_extension (c_generic_selections)
+#error
+#endif
+
+#if !__has_extension (c_static_assert) || !__has_extension (c_thread_local)
+#error
+#endif
+
+#if !__has_extension (cxx_binary_literals)
+#error
+#endif
+
+#if  __STDC_VERSION__ >= 201112L
+/* Have C11 features.  */
+#if !__has_feature (c_alignas) || !__has_feature (c_alignof)
+#error
+#endif
+
+#if !__has_feature (c_atomic) || !__has_feature (c_generic_selections)
+#error
+#endif
+
+#if !__has_feature (c_static_assert) || !__has_feature (c_thread_local)
+#error
+#endif
+
+#else
+/* Don't have C11 features.  */
+#if __has_feature (c_alignas) || __has_feature (c_alignof)
+#error
+#endif
+
+#if __has_feature (c_atomic) || __has_feature (c_generic_selections)
+#error
+#endif
+
+#if __has_feature (c_static_assert) || __has_feature (c_thread_local)
+#error
+#endif
+
+#endif
+
+#if __STDC_VERSION__ >= 202000L
+/* Have C2x features.  */
+#if !__has_feature (cxx_binary_literals)
+#error
+#endif
+
+#else
+/* Don't have C2x features.  */
+#if __has_feature (cxx_binary_literals)
+#error
+#endif
+#endif
+
+enum { just_here_to_make_this_tu_nonempty };
diff --git a/gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c b/gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c
new file mode 100644
index 00000000000..e5da1cc5628
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/has-feature-ubsan.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+#define FEAT(x) (__has_feature (x) && __has_extension (x))
+#if !FEAT (undefined_behavior_sanitizer)
+#error
+#endif
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index a6f0abd894c..8f39a6a3a66 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -755,6 +755,9 @@  struct cpp_callbacks
   /* Callback to determine whether a built-in function is recognized.  */
   int (*has_builtin) (cpp_reader *);
 
+  /* Callback to determine whether a feature is available.  */
+  int (*has_feature) (cpp_reader *, bool);
+
   /* Callback that can change a user lazy into normal macro.  */
   void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned);
 
@@ -959,7 +962,9 @@  enum cpp_builtin_type
   BT_HAS_STD_ATTRIBUTE,		/* `__has_c_attribute(x)' */
   BT_HAS_BUILTIN,		/* `__has_builtin(x)' */
   BT_HAS_INCLUDE,		/* `__has_include(x)' */
-  BT_HAS_INCLUDE_NEXT		/* `__has_include_next(x)' */
+  BT_HAS_INCLUDE_NEXT,		/* `__has_include_next(x)' */
+  BT_HAS_FEATURE,		/* `__has_feature(x)' */
+  BT_HAS_EXTENSION		/* `__has_extension(x)' */
 };
 
 #define CPP_HASHNODE(HNODE)	((cpp_hashnode *) (HNODE))
diff --git a/libcpp/init.cc b/libcpp/init.cc
index c508f06112a..465dafefe9d 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -433,6 +433,8 @@  static const struct builtin_macro builtin_array[] =
   B("__has_builtin",	 BT_HAS_BUILTIN,   true),
   B("__has_include",	 BT_HAS_INCLUDE,   true),
   B("__has_include_next",BT_HAS_INCLUDE_NEXT,   true),
+  B("__has_feature",	 BT_HAS_FEATURE, true),
+  B("__has_extension",	 BT_HAS_EXTENSION, true),
   /* Keep builtins not used for -traditional-cpp at the end, and
      update init_builtins() if any more are added.  */
   B("_Pragma",		 BT_PRAGMA,        true),
diff --git a/libcpp/macro.cc b/libcpp/macro.cc
index d4238d4f621..d2e8f9bd411 100644
--- a/libcpp/macro.cc
+++ b/libcpp/macro.cc
@@ -677,6 +677,12 @@  _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node,
       number = builtin_has_include (pfile, node,
 				    node->value.builtin == BT_HAS_INCLUDE_NEXT);
       break;
+
+    case BT_HAS_FEATURE:
+    case BT_HAS_EXTENSION:
+      number = pfile->cb.has_feature (pfile,
+				      node->value.builtin == BT_HAS_FEATURE);
+      break;
     }
 
   if (result == NULL)