diff mbox series

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

Message ID ZRQ70pZKA/5Kup4q@arm.com
State New
Headers show
Series [v4] c-family: Implement __has_feature and __has_extension [PR60512] | expand

Commit Message

Alex Coplan Sept. 27, 2023, 2:27 p.m. UTC
Hi,

This is a v4 patch to address Jason's feedback here:
https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html

w.r.t. v3 it just removes a comment now that some uncertainty around
cxx_binary_literals has been resolved, and updates the documentation as
suggested to point to the Clang docs.

----------------------------------------------------------------------

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 exception
of the legacy features for C++ type traits.  These are omitted, since as
the clang documentation notes, __has_builtin is the correct "modern" way
to query for these (which GCC already implements).

Bootstrapped/regtested on aarch64-linux-gnu, bootstrapped on
x86_64-apple-darwin, darwin regtest in progress.  OK for trunk if
testing passes?

Thanks,
Alex

gcc/c-family/ChangeLog:

	PR c++/60512
	* c-common.cc (struct hf_feature_info): New.
	(c_common_register_feature): New.
	(init_has_feature): New.
	(has_feature_p): New.
	* c-common.h (c_common_has_feature): New.
	(c_family_register_lang_features): New.
	(c_common_register_feature): New.
	(has_feature_p): New.
	(c_register_features): New.
	(cp_register_features): New.
	* c-lex.cc (init_c_lex): Plumb through has_feature callback.
	(c_common_has_builtin): Generalize and move common part ...
	(c_common_lex_availability_macro): ... here.
	(c_common_has_feature): New.
	* c-ppoutput.cc (init_pp_output): Plumb through has_feature.

gcc/c/ChangeLog:

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

gcc/cp/ChangeLog:

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

gcc/ChangeLog:

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

gcc/objc/ChangeLog:

	PR c++/60512
	* objc-act.cc (struct objc_feature_info): New.
	(objc_nonfragile_abi_p): New.
	(objc_common_register_features): New.
	* objc-act.h (objc_common_register_features): New.
	* objc-lang.cc (c_family_register_lang_features): New.

gcc/objcp/ChangeLog:

	PR c++/60512
	* objcp-lang.cc (c_family_register_lang_features): New.

libcpp/ChangeLog:

	PR c++/60512
	* include/cpplib.h (struct cpp_callbacks): Add has_feature.
	(enum cpp_builtin_type): Add BT_HAS_{FEATURE,EXTENSION}.
	* init.cc: Add __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.
	* gcc.dg/ubsan/has-feature-ubsan.c: New test.
	* obj-c++.dg/has-feature.mm: New test.
	* objc.dg/has-feature.m: New test.

Comments

Alex Coplan Oct. 11, 2023, 1:31 p.m. UTC | #1
On 27/09/2023 15:27, Alex Coplan wrote:
> Hi,
> 
> This is a v4 patch to address Jason's feedback here:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
> 
> w.r.t. v3 it just removes a comment now that some uncertainty around
> cxx_binary_literals has been resolved, and updates the documentation as
> suggested to point to the Clang docs.
> 
> ----------------------------------------------------------------------
> 
> 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 exception
> of the legacy features for C++ type traits.  These are omitted, since as
> the clang documentation notes, __has_builtin is the correct "modern" way
> to query for these (which GCC already implements).

Gentle ping on this:
https://gcc.gnu.org/pipermail/gcc-patches/2023-September/631525.html

Thanks,
Alex
Alex Coplan Oct. 25, 2023, 10:28 a.m. UTC | #2
On 11/10/2023 14:31, Alex Coplan wrote:
> On 27/09/2023 15:27, Alex Coplan wrote:
> > Hi,
> > 
> > This is a v4 patch to address Jason's feedback here:
> > https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
> > 
> > w.r.t. v3 it just removes a comment now that some uncertainty around
> > cxx_binary_literals has been resolved, and updates the documentation as
> > suggested to point to the Clang docs.
> > 
> > ----------------------------------------------------------------------
> > 
> > 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 exception
> > of the legacy features for C++ type traits.  These are omitted, since as
> > the clang documentation notes, __has_builtin is the correct "modern" way
> > to query for these (which GCC already implements).
> 
> Gentle ping on this:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/631525.html

Ping^2

> 
> Thanks,
> Alex
Jason Merrill Oct. 26, 2023, 9:06 p.m. UTC | #3
On 10/25/23 06:28, Alex Coplan wrote:
> On 11/10/2023 14:31, Alex Coplan wrote:
>> On 27/09/2023 15:27, Alex Coplan wrote:
>>> Hi,
>>>
>>> This is a v4 patch to address Jason's feedback here:
>>> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
>>>
>>> w.r.t. v3 it just removes a comment now that some uncertainty around
>>> cxx_binary_literals has been resolved, and updates the documentation as
>>> suggested to point to the Clang docs.
>>>
>>> ----------------------------------------------------------------------

Incidentally, putting a 8< or >8 in the line of dashes lets git am 
--scissors prune the text above the line.

>>> 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 exception
>>> of the legacy features for C++ type traits.  These are omitted, since as
>>> the clang documentation notes, __has_builtin is the correct "modern" way
>>> to query for these (which GCC already implements).
>>
>> Gentle ping on this:
>> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/631525.html
> 
> Ping^2

> +static const hf_feature_info has_feature_table[] =

You might use constexpr for these tables?

OK either way, thanks!

Jason
Alex Coplan Nov. 3, 2023, 10:39 a.m. UTC | #4
Hi Jason,

On 26/10/2023 17:06, Jason Merrill wrote:
> On 10/25/23 06:28, Alex Coplan wrote:
> > On 11/10/2023 14:31, Alex Coplan wrote:
> > > On 27/09/2023 15:27, Alex Coplan wrote:
> > > > Hi,
> > > > 
> > > > This is a v4 patch to address Jason's feedback here:
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
> > > > 
> > > > w.r.t. v3 it just removes a comment now that some uncertainty around
> > > > cxx_binary_literals has been resolved, and updates the documentation as
> > > > suggested to point to the Clang docs.
> > > > 
> > > > ----------------------------------------------------------------------
> 
> Incidentally, putting a 8< or >8 in the line of dashes lets git am
> --scissors prune the text above the line.
> 
> > > > 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 exception
> > > > of the legacy features for C++ type traits.  These are omitted, since as
> > > > the clang documentation notes, __has_builtin is the correct "modern" way
> > > > to query for these (which GCC already implements).
> > > 
> > > Gentle ping on this:
> > > https://gcc.gnu.org/pipermail/gcc-patches/2023-September/631525.html
> > 
> > Ping^2
> 
> > +static const hf_feature_info has_feature_table[] =
> 
> You might use constexpr for these tables?

I'll give that a go, thanks.

> 
> OK either way, thanks!

Thanks a lot for the reviews.  Just to clarify, does this OK count for the C
front-end parts as well as the C++ and generic parts, or do we need a C
front-end maintainer to take a look at those bits?

Thanks,
Alex

> 
> Jason
>
Marek Polacek Nov. 3, 2023, 4:19 p.m. UTC | #5
On Wed, Sep 27, 2023 at 03:27:30PM +0100, Alex Coplan wrote:
> Hi,
> 
> This is a v4 patch to address Jason's feedback here:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
> 
> w.r.t. v3 it just removes a comment now that some uncertainty around
> cxx_binary_literals has been resolved, and updates the documentation as
> suggested to point to the Clang docs.
> 
> ----------------------------------------------------------------------
> 
> 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 exception
> of the legacy features for C++ type traits.  These are omitted, since as
> the clang documentation notes, __has_builtin is the correct "modern" way
> to query for these (which GCC already implements).
> 
> Bootstrapped/regtested on aarch64-linux-gnu, bootstrapped on
> x86_64-apple-darwin, darwin regtest in progress.  OK for trunk if
> testing passes?

Thanks for the patch.  I only have a few minor comments.

> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index aae57260097..1210953d33a 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -311,6 +311,43 @@ const struct fname_var_t fname_vars[] =
>    {NULL, 0, 0},
>  };
>  
> +/* Flags to restrict availability of generic features that
> +   are known to __has_{feature,extension}.  */
> +
> +enum
> +{
> +  HF_FLAG_EXT = 1,	/* Available only as an extension.  */
> +  HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
> +};

Why not have a new HF_FLAG_ = 0 here and use it below...

> +/* Info for generic features which can be queried through
> +   __has_{feature,extension}.  */
> +
> +struct hf_feature_info
> +{
> +  const char *ident;
> +  unsigned flags;
> +  unsigned mask;

Not enum sanitize_code for mask?

> +};
> +
> +/* Table of generic features which can be queried through
> +   __has_{feature,extension}.  */
> +
> +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 },

...here?  Might be more obvious what it means then.

> +  { "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;
>  
> @@ -9808,4 +9845,63 @@ c_strict_flex_array_level_of (tree array_field)
>    return strict_flex_array_level;
>  }
>  
> +/* Map from identifiers to booleans.  Value is true for features, and
> +   false for extensions.  Used to implement __has_{feature,extension}.  */
> +
> +using feature_map_t = hash_map <tree, bool>;
> +static feature_map_t *feature_map = nullptr;

You don't need " = nullptr" here.

> +/* Register a feature for __has_{feature,extension}.  FEATURE_P is true
> +   if the feature identified by NAME is a feature (as opposed to an
> +   extension).  */
> +
> +void
> +c_common_register_feature (const char *name, bool feature_p)
> +{
> +  bool dup = feature_map->put (get_identifier (name), feature_p);
> +  gcc_checking_assert (!dup);
> +}
> +
> +/* Lazily initialize hash table for __has_{feature,extension},
> +   dispatching to the appropriate frontend to register language-specific

"front end"

> +   features.  */
> +
> +static void
> +init_has_feature ()
> +{
> +  gcc_assert (!feature_map);
> +  feature_map = new feature_map_t;
> +  gcc_assert (feature_map);

I don't see asserts like that around "new hash_map" elsewhere in the code base.
If you want them, maybe use gcc_checking_assert instead?

> +  for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
> +    {
> +      const hf_feature_info *info = has_feature_table + i;
> +
> +      if ((info->flags & HF_FLAG_SANITIZE) && !(flag_sanitize & info->mask))
> +	continue;
> +
> +      const bool feature_p = !(info->flags & HF_FLAG_EXT);
> +      c_common_register_feature (info->ident, feature_p);
> +    }
> +
> +  /* Register language-specific features.  */
> +  c_family_register_lang_features ();
> +}
> +
> +/* If STRICT_P is true, evaluate __has_feature (IDENT).
> +   Otherwise, evaluate __has_extension (IDENT).  */
> +
> +bool
> +has_feature_p (const char *ident, bool strict_p)

strict_p sounds a little bit odd here, maybe use bool feature_p/ext_p?
Or not, it's fine either way.

> +{
> +  if (!feature_map)
> +    init_has_feature ();
> +
> +  bool *feat_p = feature_map->get (get_identifier (ident));
> +  if (!feat_p)
> +    return false;
> +
> +  return !strict_p || *feat_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 1fdba7ef3ea..28ea8d63f5a 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -1126,6 +1126,20 @@ 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);
> +
> +/* Implemented by each frontend in *-lang.cc.  */

"front end"

> +extern void c_family_register_lang_features ();
> +
> +/* Implemented in c-common.cc.  */

Not important, but below you use the c/ or cp/ path so this could have c-family/.

> +extern void c_common_register_feature (const char *, bool);
> +extern bool has_feature_p (const char *, bool);
> +
> +/* Implemented in c/c-objc-common.cc.  */
> +extern void c_register_features ();
> +
> +/* Implemented in cp/cp-objcp-common.cc.  */
> +extern void cp_register_features ();
>  
>  extern bool parse_optimize_options (tree, bool);
>  
> diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
> index 5e457b78cdd..6a605f91858 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;
> @@ -453,16 +454,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);

Since %s shouldn't be translated, I guess this is fine.

>        return 0;
>      }
>  
> @@ -482,7 +483,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;
>      }
> @@ -501,9 +502,38 @@ 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;
> +
> +  /* If -pedantic-errors is given, __has_extension is equivalent to
> +     __has_feature.  */
> +  strict_p |= flag_pedantic_errors;
> +  return has_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-lang.cc b/gcc/c/c-lang.cc
> index b4e0c8cfb8a..11e7aaac2e3 100644
> --- a/gcc/c/c-lang.cc
> +++ b/gcc/c/c-lang.cc
> @@ -61,6 +61,15 @@ c_get_sarif_source_language (const char *)
>    return "c";
>  }
>  
> +/* Implement c-family hook to register language-specific features for
> +   __has_{feature,extension}.  */
> +
> +void
> +c_family_register_lang_features ()
> +{
> +  c_register_features ();
> +}
> +
>  #if CHECKING_P
>  
>  namespace selftest {
> diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
> index e4aed61ed00..fdff0cb999e 100644
> --- a/gcc/c/c-objc-common.cc
> +++ b/gcc/c/c-objc-common.cc
> @@ -34,6 +34,39 @@ 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 **);
>  
> +/* Info for C language features which can be queried through
> +   __has_{feature,extension}.  */
> +
> +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 },
> +  { "cxx_binary_literals", &flag_isoc2x }
> +};
> +
> +/* Register features specific to the C language.  */
> +
> +void
> +c_register_features ()
> +{
> +  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
> +    {
> +      const c_feature_info *info = c_feature_table + i;
> +      const bool feat_p = !info->enable_flag || *info->enable_flag;
> +      c_common_register_feature (info->ident, feat_p);
> +    }
> +}
> +
>  bool
>  c_missing_noreturn_ok_p (tree decl)
>  {
> diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
> index 2f541460c4a..84200a9a04a 100644
> --- a/gcc/cp/cp-lang.cc
> +++ b/gcc/cp/cp-lang.cc
> @@ -121,6 +121,15 @@ objcp_tsubst_copy_and_build (tree /*t*/,
>    return NULL_TREE;
>  }
>  
> +/* Implement c-family hook to add language-specific features
> +   for __has_{feature,extension}.  */
> +
> +void
> +c_family_register_lang_features ()
> +{
> +  cp_register_features ();
> +}
> +
>  static const char *
>  cxx_dwarf_name (tree t, int verbosity)
>  {
> diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
> index 93b027b80ce..ad8af5b401e 100644
> --- a/gcc/cp/cp-objcp-common.cc
> +++ b/gcc/cp/cp-objcp-common.cc
> @@ -23,10 +23,154 @@ 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"
>  
> +/* Class to determine whether a given C++ langauge feature is available.

"language"

> +   Used to implement __has_{feature,extension}.  */
> +
> +struct cp_feature_selector
> +{
> +  enum
> +  {
> +    DIALECT,
> +    FLAG
> +  } kind;
> +
> +  enum class result
> +  {
> +    NONE,
> +    EXT,
> +    FEAT
> +  };
> +
> +  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) {}
> +
> +  inline result has_feature () const;
> +};
> +
> +/* Check whether this language feature is available as a feature,
> +   extension, or not at all.  */
> +
> +cp_feature_selector::result
> +cp_feature_selector::has_feature () const
> +{
> +  switch (kind)
> +    {
> +    case DIALECT:
> +      if (cxx_dialect >= dialect.feat)
> +	return result::FEAT;
> +      else if (cxx_dialect >= dialect.ext)
> +	return result::EXT;
> +      else
> +	return result::NONE;
> +    case FLAG:
> +      return *enable_flag ? result::FEAT : result::NONE;
> +    }
> +
> +  gcc_unreachable ();
> +}
> +
> +/* Information about a C++ language feature which can be queried
> +   through __has_{feature,extension}.  IDENT is the name of the feature,
> +   and SELECTOR encodes how to compute whether the feature is available.  */
> +
> +struct cp_feature_info
> +{
> +  const char *ident;
> +  cp_feature_selector selector;
> +};
> +
> +/* Table of features for __has_{feature,extension}.  */
> +
> +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 },
> +  { "cxx_delegating_constructors", cxx11 },
> +  { "cxx_deleted_functions", cxx11 },
> +  { "cxx_explicit_conversions", cxx11 },
> +  { "cxx_generalized_initializers", cxx11 },
> +  { "cxx_implicit_moves", cxx11 },
> +  { "cxx_inheriting_constructors", cxx11 },
> +  { "cxx_inline_namespaces", { cxx11, cxx98 } },
> +  { "cxx_lambdas", cxx11 },
> +  { "cxx_local_type_template_args", cxx11 },
> +  { "cxx_noexcept", cxx11 },
> +  { "cxx_nonstatic_member_init", cxx11 },
> +  { "cxx_nullptr", cxx11 },
> +  { "cxx_override_control", cxx11 },
> +  { "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 },
> +  { "cxx_generic_lambdas", cxx14 },
> +  { "cxx_relaxed_constexpr", cxx14 },
> +  { "cxx_return_type_deduction", cxx14 },
> +  { "cxx_variable_templates", cxx14 },
> +  { "modules", &flag_modules },
> +};
> +
> +/* Register C++ language features for __has_{feature,extension}.  */
> +
> +void
> +cp_register_features ()
> +{
> +  using result = cp_feature_selector::result;
> +
> +  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
> +    {
> +      const cp_feature_info *info = cp_feature_table + i;
> +      const auto res = info->selector.has_feature ();
> +      if (res == result::NONE)
> +	continue;
> +
> +      c_common_register_feature (info->ident, res == result::FEAT);
> +    }
> +}
> +
>  /* 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 3f492b33470..9ab22a46899 100644
> --- a/gcc/doc/cpp.texi
> +++ b/gcc/doc/cpp.texi
> @@ -3199,6 +3199,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
>  
> @@ -3561,6 +3563,45 @@ 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.
> +
> +Note that @code{__has_feature} and @code{__has_extension} are not recommended
> +for use in new code, and are only provided for compatibility with Clang.  For
> +details of which identifiers are accepted by these function-like macros, see
> +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
> +the Clang documentation}}.
> +
> +@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.
> +
> +If the @code{-pedantic-errors} flag is given, @code{__has_extension} is

Should be @option{-pedantic-errors}.

> +equivalent to @code{__has_feature}.
> +
> +Note that @code{__has_feature} and @code{__has_extension} are not recommended
> +for use in new code, and are only provided for compatibility with Clang.  For
> +details of which identifiers are accepted by these function-like macros, see
> +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
> +the Clang documentation}}.
> +
>  @node @code{__has_include}
>  @subsection @code{__has_include}
>  @cindex @code{__has_include}
> diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
> index 186a29cd270..27fe317b587 100644
> --- a/gcc/objc/objc-act.cc
> +++ b/gcc/objc/objc-act.cc
> @@ -10359,5 +10359,51 @@ objc_common_tree_size (enum tree_code code)
>      }
>  }
>  
> +/* Information for Objective-C-specific features known to __has_feature.  */
> +
> +struct objc_feature_info
> +{
> +  typedef bool (*predicate_t) ();
> +
> +  const char *ident;
> +  predicate_t predicate;
> +
> +  constexpr objc_feature_info (const char *name)
> +    : ident (name), predicate (nullptr) {}
> +  constexpr objc_feature_info (const char *name, predicate_t p)
> +    : ident (name), predicate (p) {}
> +
> +  bool has_feature () const
> +    {
> +      return predicate ? predicate () : true;
> +    }
> +};
> +
> +static bool objc_nonfragile_abi_p ()
> +{
> +  return flag_next_runtime && flag_objc_abi >= 2;
> +}
> +
> +static const objc_feature_info objc_features[] =
> +{
> +  { "objc_default_synthesize_properties" },
> +  { "objc_instancetype" },
> +  { "objc_nonfragile_abi", objc_nonfragile_abi_p }
> +};
> +
> +/* Register Objective-C-specific features for __has_feature.  */
> +
> +void
> +objc_common_register_features ()
> +{
> +  for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++)
> +    {
> +      const objc_feature_info *info = objc_features + i;
> +      if (!info->has_feature ())
> +	continue;
> +
> +      c_common_register_feature (info->ident, true);
> +    }
> +}
>  
>  #include "gt-objc-objc-act.h"
> diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h
> index e21ab52d8ca..bcf0249515a 100644
> --- a/gcc/objc/objc-act.h
> +++ b/gcc/objc/objc-act.h
> @@ -29,6 +29,9 @@ int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
>  void objc_common_init_ts (void);
>  const char *objc_get_sarif_source_language (const char *);
>  
> +/* Register features common to Objective-C and Objective-C++.  */
> +void objc_common_register_features ();
> +
>  /* NB: The remaining public functions are prototyped in c-common.h, for the
>     benefit of stub-objc.cc and objc-act.cc.  */
>  
> diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc
> index 89b3be48b9e..451cbe09413 100644
> --- a/gcc/objc/objc-lang.cc
> +++ b/gcc/objc/objc-lang.cc
> @@ -58,6 +58,15 @@ objc_get_sarif_source_language (const char *)
>    return "objectivec";
>  }
>  
> +/* Implement c-family hook to add language-specific features
> +   for __has_{feature,extension}.  */
> +
> +void c_family_register_lang_features ()

I guess the void should be on a separate line.

> +{
> +  objc_common_register_features ();
> +  c_register_features ();
> +}
> +
>  /* Lang hook routines common to C and ObjC appear in c-objc-common.cc;
>     there should be very few (if any) routines below.  */
>  
> diff --git a/gcc/objcp/objcp-lang.cc b/gcc/objcp/objcp-lang.cc
> index 9887209b9c8..1c22456115f 100644
> --- a/gcc/objcp/objcp-lang.cc
> +++ b/gcc/objcp/objcp-lang.cc
> @@ -80,6 +80,15 @@ objcp_tsubst_copy_and_build (tree t, tree args, tsubst_flags_t complain,
>  #undef RECURSE
>  }
>  
> +/* Implement c-family hook to add language-specific features
> +   for __has_{feature,extension}.  */
> +
> +void c_family_register_lang_features ()

I guess the void should be on a separate line.

> diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
> index c0af82c0b1c..32282a37bee 100644
> --- a/libcpp/include/cpplib.h
> +++ b/libcpp/include/cpplib.h
> @@ -761,6 +761,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);
>  
> @@ -965,7 +968,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 9a20f8d8176..c1424d35949 100644
> --- a/libcpp/init.cc
> +++ b/libcpp/init.cc
> @@ -435,6 +435,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 dada8fea835..f8b86d0fbfe 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)

These bits look good too.

I don't see a test using -pedantic-errors, in which case __has_extension == __has_feature.

The Clang doc says "The feature name or extension name can also be specified with a
preceding and following __ (double underscore) to avoid interference from a
macro with the same name. For instance, __cxx_rvalue_references__ can be used
instead of cxx_rvalue_references."

but with this patch, __has_feature(__tls__) or __has_feature(__tls) appear
to behave differently than __has_feature(tls).  Was that a deliberate choice?
If not, we have canonicalize_attr_name which should fix that.

Thanks!

Marek
Alex Coplan Nov. 17, 2023, 3:12 p.m. UTC | #6
On 03/11/2023 12:19, Marek Polacek wrote:
> On Wed, Sep 27, 2023 at 03:27:30PM +0100, Alex Coplan wrote:
> > Hi,
> > 
> > This is a v4 patch to address Jason's feedback here:
> > https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630911.html
> > 
> > w.r.t. v3 it just removes a comment now that some uncertainty around
> > cxx_binary_literals has been resolved, and updates the documentation as
> > suggested to point to the Clang docs.
> > 
> > ----------------------------------------------------------------------
> > 
> > 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 exception
> > of the legacy features for C++ type traits.  These are omitted, since as
> > the clang documentation notes, __has_builtin is the correct "modern" way
> > to query for these (which GCC already implements).
> > 
> > Bootstrapped/regtested on aarch64-linux-gnu, bootstrapped on
> > x86_64-apple-darwin, darwin regtest in progress.  OK for trunk if
> > testing passes?
> 
> Thanks for the patch.  I only have a few minor comments.

Thanks a lot for the detailed review.  Please see the incremental change
from v4 to v5 attached (which addresses your comments).  The full v5
patch is posted here:
https://gcc.gnu.org/pipermail/gcc-patches/2023-November/637028.html

> 
> > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > index aae57260097..1210953d33a 100644
> > --- a/gcc/c-family/c-common.cc
> > +++ b/gcc/c-family/c-common.cc
> > @@ -311,6 +311,43 @@ const struct fname_var_t fname_vars[] =
> >    {NULL, 0, 0},
> >  };
> >  
> > +/* Flags to restrict availability of generic features that
> > +   are known to __has_{feature,extension}.  */
> > +
> > +enum
> > +{
> > +  HF_FLAG_EXT = 1,	/* Available only as an extension.  */
> > +  HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
> > +};
> 
> Why not have a new HF_FLAG_ = 0 here and use it below...

Sure, I've used HF_FLAG_NONE for this in the updated patch.

> 
> > +/* Info for generic features which can be queried through
> > +   __has_{feature,extension}.  */
> > +
> > +struct hf_feature_info
> > +{
> > +  const char *ident;
> > +  unsigned flags;
> > +  unsigned mask;
> 
> Not enum sanitize_code for mask?

I initially intended the mask field to have a flexible interpretation
depending on the value of flags, i.e. it's only interpreted as
enum sanitize_code if flags has HF_FLAG_SANITIZE set.  Of course, at
the moment the mask field happens to only be used for sanitizer flags.

So personally I'd lean towards keeping the type as is with the view to
allowing re-purposing in the future, but happy to change it if you feel
strongly.

> 
> > +};
> > +
> > +/* Table of generic features which can be queried through
> > +   __has_{feature,extension}.  */
> > +
> > +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 },
> 
> ...here?  Might be more obvious what it means then.
> 
> > +  { "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;
> >  
> > @@ -9808,4 +9845,63 @@ c_strict_flex_array_level_of (tree array_field)
> >    return strict_flex_array_level;
> >  }
> >  
> > +/* Map from identifiers to booleans.  Value is true for features, and
> > +   false for extensions.  Used to implement __has_{feature,extension}.  */
> > +
> > +using feature_map_t = hash_map <tree, bool>;
> > +static feature_map_t *feature_map = nullptr;
> 
> You don't need " = nullptr" here.

Dropped, thanks.

> 
> > +/* Register a feature for __has_{feature,extension}.  FEATURE_P is true
> > +   if the feature identified by NAME is a feature (as opposed to an
> > +   extension).  */
> > +
> > +void
> > +c_common_register_feature (const char *name, bool feature_p)
> > +{
> > +  bool dup = feature_map->put (get_identifier (name), feature_p);
> > +  gcc_checking_assert (!dup);
> > +}
> > +
> > +/* Lazily initialize hash table for __has_{feature,extension},
> > +   dispatching to the appropriate frontend to register language-specific
> 
> "front end"

Done.

> 
> > +   features.  */
> > +
> > +static void
> > +init_has_feature ()
> > +{
> > +  gcc_assert (!feature_map);
> > +  feature_map = new feature_map_t;
> > +  gcc_assert (feature_map);
> 
> I don't see asserts like that around "new hash_map" elsewhere in the code base.
> If you want them, maybe use gcc_checking_assert instead?

Yeah, this was perhaps a little overzealous use of gcc_assert.  IIUC,
new should abort on failure (since GCC is built with -fno-exceptions),
so we shouldn't need the second assert.  The first one seems reasonable
to make sure we don't call init_has_feature twice (and leak the initial
table), but only seems worth being a gcc_checking_assert at most,
indeed.  So I dropped the second one and downgraded the first to a
gcc_checking_assert.

> 
> > +  for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
> > +    {
> > +      const hf_feature_info *info = has_feature_table + i;
> > +
> > +      if ((info->flags & HF_FLAG_SANITIZE) && !(flag_sanitize & info->mask))
> > +	continue;
> > +
> > +      const bool feature_p = !(info->flags & HF_FLAG_EXT);
> > +      c_common_register_feature (info->ident, feature_p);
> > +    }
> > +
> > +  /* Register language-specific features.  */
> > +  c_family_register_lang_features ();
> > +}
> > +
> > +/* If STRICT_P is true, evaluate __has_feature (IDENT).
> > +   Otherwise, evaluate __has_extension (IDENT).  */
> > +
> > +bool
> > +has_feature_p (const char *ident, bool strict_p)
> 
> strict_p sounds a little bit odd here, maybe use bool feature_p/ext_p?
> Or not, it's fine either way.

I've kept it as is for now, I find the name clear given the semantic
difference between the builtins and, if nothing else, it avoids
confusion with other variable names later in the function (unlike e.g.
feature_p).

> 
> > +{
> > +  if (!feature_map)
> > +    init_has_feature ();
> > +
> > +  bool *feat_p = feature_map->get (get_identifier (ident));
> > +  if (!feat_p)
> > +    return false;
> > +
> > +  return !strict_p || *feat_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 1fdba7ef3ea..28ea8d63f5a 100644
> > --- a/gcc/c-family/c-common.h
> > +++ b/gcc/c-family/c-common.h
> > @@ -1126,6 +1126,20 @@ 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);
> > +
> > +/* Implemented by each frontend in *-lang.cc.  */
> 
> "front end"

Done.

> 
> > +extern void c_family_register_lang_features ();
> > +
> > +/* Implemented in c-common.cc.  */
> 
> Not important, but below you use the c/ or cp/ path so this could have c-family/.

Done, thanks.

> 
> > +extern void c_common_register_feature (const char *, bool);
> > +extern bool has_feature_p (const char *, bool);
> > +
> > +/* Implemented in c/c-objc-common.cc.  */
> > +extern void c_register_features ();
> > +
> > +/* Implemented in cp/cp-objcp-common.cc.  */
> > +extern void cp_register_features ();
> >  
> >  extern bool parse_optimize_options (tree, bool);
> >  
> > diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
> > index 5e457b78cdd..6a605f91858 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;
> > @@ -453,16 +454,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);
> 
> Since %s shouldn't be translated, I guess this is fine.
> 
> >        return 0;
> >      }
> >  
> > @@ -482,7 +483,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;
> >      }
> > @@ -501,9 +502,38 @@ 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;
> > +
> > +  /* If -pedantic-errors is given, __has_extension is equivalent to
> > +     __has_feature.  */
> > +  strict_p |= flag_pedantic_errors;
> > +  return has_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-lang.cc b/gcc/c/c-lang.cc
> > index b4e0c8cfb8a..11e7aaac2e3 100644
> > --- a/gcc/c/c-lang.cc
> > +++ b/gcc/c/c-lang.cc
> > @@ -61,6 +61,15 @@ c_get_sarif_source_language (const char *)
> >    return "c";
> >  }
> >  
> > +/* Implement c-family hook to register language-specific features for
> > +   __has_{feature,extension}.  */
> > +
> > +void
> > +c_family_register_lang_features ()
> > +{
> > +  c_register_features ();
> > +}
> > +
> >  #if CHECKING_P
> >  
> >  namespace selftest {
> > diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
> > index e4aed61ed00..fdff0cb999e 100644
> > --- a/gcc/c/c-objc-common.cc
> > +++ b/gcc/c/c-objc-common.cc
> > @@ -34,6 +34,39 @@ 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 **);
> >  
> > +/* Info for C language features which can be queried through
> > +   __has_{feature,extension}.  */
> > +
> > +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 },
> > +  { "cxx_binary_literals", &flag_isoc2x }
> > +};
> > +
> > +/* Register features specific to the C language.  */
> > +
> > +void
> > +c_register_features ()
> > +{
> > +  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
> > +    {
> > +      const c_feature_info *info = c_feature_table + i;
> > +      const bool feat_p = !info->enable_flag || *info->enable_flag;
> > +      c_common_register_feature (info->ident, feat_p);
> > +    }
> > +}
> > +
> >  bool
> >  c_missing_noreturn_ok_p (tree decl)
> >  {
> > diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
> > index 2f541460c4a..84200a9a04a 100644
> > --- a/gcc/cp/cp-lang.cc
> > +++ b/gcc/cp/cp-lang.cc
> > @@ -121,6 +121,15 @@ objcp_tsubst_copy_and_build (tree /*t*/,
> >    return NULL_TREE;
> >  }
> >  
> > +/* Implement c-family hook to add language-specific features
> > +   for __has_{feature,extension}.  */
> > +
> > +void
> > +c_family_register_lang_features ()
> > +{
> > +  cp_register_features ();
> > +}
> > +
> >  static const char *
> >  cxx_dwarf_name (tree t, int verbosity)
> >  {
> > diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
> > index 93b027b80ce..ad8af5b401e 100644
> > --- a/gcc/cp/cp-objcp-common.cc
> > +++ b/gcc/cp/cp-objcp-common.cc
> > @@ -23,10 +23,154 @@ 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"
> >  
> > +/* Class to determine whether a given C++ langauge feature is available.
> 
> "language"

Fixed, thanks.

> 
> > +   Used to implement __has_{feature,extension}.  */
> > +
> > +struct cp_feature_selector
> > +{
> > +  enum
> > +  {
> > +    DIALECT,
> > +    FLAG
> > +  } kind;
> > +
> > +  enum class result
> > +  {
> > +    NONE,
> > +    EXT,
> > +    FEAT
> > +  };
> > +
> > +  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) {}
> > +
> > +  inline result has_feature () const;
> > +};
> > +
> > +/* Check whether this language feature is available as a feature,
> > +   extension, or not at all.  */
> > +
> > +cp_feature_selector::result
> > +cp_feature_selector::has_feature () const
> > +{
> > +  switch (kind)
> > +    {
> > +    case DIALECT:
> > +      if (cxx_dialect >= dialect.feat)
> > +	return result::FEAT;
> > +      else if (cxx_dialect >= dialect.ext)
> > +	return result::EXT;
> > +      else
> > +	return result::NONE;
> > +    case FLAG:
> > +      return *enable_flag ? result::FEAT : result::NONE;
> > +    }
> > +
> > +  gcc_unreachable ();
> > +}
> > +
> > +/* Information about a C++ language feature which can be queried
> > +   through __has_{feature,extension}.  IDENT is the name of the feature,
> > +   and SELECTOR encodes how to compute whether the feature is available.  */
> > +
> > +struct cp_feature_info
> > +{
> > +  const char *ident;
> > +  cp_feature_selector selector;
> > +};
> > +
> > +/* Table of features for __has_{feature,extension}.  */
> > +
> > +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 },
> > +  { "cxx_delegating_constructors", cxx11 },
> > +  { "cxx_deleted_functions", cxx11 },
> > +  { "cxx_explicit_conversions", cxx11 },
> > +  { "cxx_generalized_initializers", cxx11 },
> > +  { "cxx_implicit_moves", cxx11 },
> > +  { "cxx_inheriting_constructors", cxx11 },
> > +  { "cxx_inline_namespaces", { cxx11, cxx98 } },
> > +  { "cxx_lambdas", cxx11 },
> > +  { "cxx_local_type_template_args", cxx11 },
> > +  { "cxx_noexcept", cxx11 },
> > +  { "cxx_nonstatic_member_init", cxx11 },
> > +  { "cxx_nullptr", cxx11 },
> > +  { "cxx_override_control", cxx11 },
> > +  { "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 },
> > +  { "cxx_generic_lambdas", cxx14 },
> > +  { "cxx_relaxed_constexpr", cxx14 },
> > +  { "cxx_return_type_deduction", cxx14 },
> > +  { "cxx_variable_templates", cxx14 },
> > +  { "modules", &flag_modules },
> > +};
> > +
> > +/* Register C++ language features for __has_{feature,extension}.  */
> > +
> > +void
> > +cp_register_features ()
> > +{
> > +  using result = cp_feature_selector::result;
> > +
> > +  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
> > +    {
> > +      const cp_feature_info *info = cp_feature_table + i;
> > +      const auto res = info->selector.has_feature ();
> > +      if (res == result::NONE)
> > +	continue;
> > +
> > +      c_common_register_feature (info->ident, res == result::FEAT);
> > +    }
> > +}
> > +
> >  /* 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 3f492b33470..9ab22a46899 100644
> > --- a/gcc/doc/cpp.texi
> > +++ b/gcc/doc/cpp.texi
> > @@ -3199,6 +3199,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
> >  
> > @@ -3561,6 +3563,45 @@ 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.
> > +
> > +Note that @code{__has_feature} and @code{__has_extension} are not recommended
> > +for use in new code, and are only provided for compatibility with Clang.  For
> > +details of which identifiers are accepted by these function-like macros, see
> > +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
> > +the Clang documentation}}.
> > +
> > +@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.
> > +
> > +If the @code{-pedantic-errors} flag is given, @code{__has_extension} is
> 
> Should be @option{-pedantic-errors}.

Changed, thanks.

> 
> > +equivalent to @code{__has_feature}.
> > +
> > +Note that @code{__has_feature} and @code{__has_extension} are not recommended
> > +for use in new code, and are only provided for compatibility with Clang.  For
> > +details of which identifiers are accepted by these function-like macros, see
> > +@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
> > +the Clang documentation}}.
> > +
> >  @node @code{__has_include}
> >  @subsection @code{__has_include}
> >  @cindex @code{__has_include}
> > diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
> > index 186a29cd270..27fe317b587 100644
> > --- a/gcc/objc/objc-act.cc
> > +++ b/gcc/objc/objc-act.cc
> > @@ -10359,5 +10359,51 @@ objc_common_tree_size (enum tree_code code)
> >      }
> >  }
> >  
> > +/* Information for Objective-C-specific features known to __has_feature.  */
> > +
> > +struct objc_feature_info
> > +{
> > +  typedef bool (*predicate_t) ();
> > +
> > +  const char *ident;
> > +  predicate_t predicate;
> > +
> > +  constexpr objc_feature_info (const char *name)
> > +    : ident (name), predicate (nullptr) {}
> > +  constexpr objc_feature_info (const char *name, predicate_t p)
> > +    : ident (name), predicate (p) {}
> > +
> > +  bool has_feature () const
> > +    {
> > +      return predicate ? predicate () : true;
> > +    }
> > +};
> > +
> > +static bool objc_nonfragile_abi_p ()
> > +{
> > +  return flag_next_runtime && flag_objc_abi >= 2;
> > +}
> > +
> > +static const objc_feature_info objc_features[] =
> > +{
> > +  { "objc_default_synthesize_properties" },
> > +  { "objc_instancetype" },
> > +  { "objc_nonfragile_abi", objc_nonfragile_abi_p }
> > +};
> > +
> > +/* Register Objective-C-specific features for __has_feature.  */
> > +
> > +void
> > +objc_common_register_features ()
> > +{
> > +  for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++)
> > +    {
> > +      const objc_feature_info *info = objc_features + i;
> > +      if (!info->has_feature ())
> > +	continue;
> > +
> > +      c_common_register_feature (info->ident, true);
> > +    }
> > +}
> >  
> >  #include "gt-objc-objc-act.h"
> > diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h
> > index e21ab52d8ca..bcf0249515a 100644
> > --- a/gcc/objc/objc-act.h
> > +++ b/gcc/objc/objc-act.h
> > @@ -29,6 +29,9 @@ int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
> >  void objc_common_init_ts (void);
> >  const char *objc_get_sarif_source_language (const char *);
> >  
> > +/* Register features common to Objective-C and Objective-C++.  */
> > +void objc_common_register_features ();
> > +
> >  /* NB: The remaining public functions are prototyped in c-common.h, for the
> >     benefit of stub-objc.cc and objc-act.cc.  */
> >  
> > diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc
> > index 89b3be48b9e..451cbe09413 100644
> > --- a/gcc/objc/objc-lang.cc
> > +++ b/gcc/objc/objc-lang.cc
> > @@ -58,6 +58,15 @@ objc_get_sarif_source_language (const char *)
> >    return "objectivec";
> >  }
> >  
> > +/* Implement c-family hook to add language-specific features
> > +   for __has_{feature,extension}.  */
> > +
> > +void c_family_register_lang_features ()
> 
> I guess the void should be on a separate line.

Fixed, thanks.

> 
> > +{
> > +  objc_common_register_features ();
> > +  c_register_features ();
> > +}
> > +
> >  /* Lang hook routines common to C and ObjC appear in c-objc-common.cc;
> >     there should be very few (if any) routines below.  */
> >  
> > diff --git a/gcc/objcp/objcp-lang.cc b/gcc/objcp/objcp-lang.cc
> > index 9887209b9c8..1c22456115f 100644
> > --- a/gcc/objcp/objcp-lang.cc
> > +++ b/gcc/objcp/objcp-lang.cc
> > @@ -80,6 +80,15 @@ objcp_tsubst_copy_and_build (tree t, tree args, tsubst_flags_t complain,
> >  #undef RECURSE
> >  }
> >  
> > +/* Implement c-family hook to add language-specific features
> > +   for __has_{feature,extension}.  */
> > +
> > +void c_family_register_lang_features ()
> 
> I guess the void should be on a separate line.
> 
> > diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
> > index c0af82c0b1c..32282a37bee 100644
> > --- a/libcpp/include/cpplib.h
> > +++ b/libcpp/include/cpplib.h
> > @@ -761,6 +761,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);
> >  
> > @@ -965,7 +968,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 9a20f8d8176..c1424d35949 100644
> > --- a/libcpp/init.cc
> > +++ b/libcpp/init.cc
> > @@ -435,6 +435,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 dada8fea835..f8b86d0fbfe 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)
> 
> These bits look good too.
> 
> I don't see a test using -pedantic-errors, in which case __has_extension == __has_feature.

Good point, I've added one with the v5 patch.

> 
> The Clang doc says "The feature name or extension name can also be specified with a
> preceding and following __ (double underscore) to avoid interference from a
> macro with the same name. For instance, __cxx_rvalue_references__ can be used
> instead of cxx_rvalue_references."
> 
> but with this patch, __has_feature(__tls__) or __has_feature(__tls) appear
> to behave differently than __has_feature(tls).  Was that a deliberate choice?

Thanks for the catch.  I'd missed this detail from the clang docs.

> If not, we have canonicalize_attr_name which should fix that.

The v5 patch uses this to implement the above behaviour (and adjusts
tests to check we handle it OK).

How does the new version look?

Thanks,
Alex

> 
> Thanks!
> 
> Marek
>
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index a19c62fc231..f270fa2f5b5 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -316,6 +316,7 @@ const struct fname_var_t fname_vars[] =
 
 enum
 {
+  HF_FLAG_NONE = 0,
   HF_FLAG_EXT = 1,	/* Available only as an extension.  */
   HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
 };
@@ -333,17 +334,17 @@ struct hf_feature_info
 /* Table of generic features which can be queried through
    __has_{feature,extension}.  */
 
-static const hf_feature_info has_feature_table[] =
+static constexpr 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 },
+  { "attribute_deprecated_with_message",  HF_FLAG_NONE, 0 },
+  { "attribute_unavailable_with_message", HF_FLAG_NONE, 0 },
+  { "enumerator_attributes",		  HF_FLAG_NONE, 0 },
+  { "tls",				  HF_FLAG_NONE, 0 },
   { "gnu_asm_goto_with_outputs",	  HF_FLAG_EXT, 0 },
   { "gnu_asm_goto_with_outputs_full",	  HF_FLAG_EXT, 0 }
 };
@@ -9918,7 +9919,7 @@ c_strict_flex_array_level_of (tree array_field)
    false for extensions.  Used to implement __has_{feature,extension}.  */
 
 using feature_map_t = hash_map <tree, bool>;
-static feature_map_t *feature_map = nullptr;
+static feature_map_t *feature_map;
 
 /* Register a feature for __has_{feature,extension}.  FEATURE_P is true
    if the feature identified by NAME is a feature (as opposed to an
@@ -9932,15 +9933,14 @@ c_common_register_feature (const char *name, bool feature_p)
 }
 
 /* Lazily initialize hash table for __has_{feature,extension},
-   dispatching to the appropriate frontend to register language-specific
+   dispatching to the appropriate front end to register language-specific
    features.  */
 
 static void
 init_has_feature ()
 {
-  gcc_assert (!feature_map);
+  gcc_checking_assert (!feature_map);
   feature_map = new feature_map_t;
-  gcc_assert (feature_map);
 
   for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
     {
@@ -9966,7 +9966,8 @@ has_feature_p (const char *ident, bool strict_p)
   if (!feature_map)
     init_has_feature ();
 
-  bool *feat_p = feature_map->get (get_identifier (ident));
+  tree name = canonicalize_attr_name (get_identifier (ident));
+  bool *feat_p = feature_map->get (name);
   if (!feat_p)
     return false;
 
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 3d976dbb10f..a5359b7caa5 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1128,10 +1128,10 @@ 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);
 
-/* Implemented by each frontend in *-lang.cc.  */
+/* Implemented by each front end in *-lang.cc.  */
 extern void c_family_register_lang_features ();
 
-/* Implemented in c-common.cc.  */
+/* Implemented in c-family/c-common.cc.  */
 extern void c_common_register_feature (const char *, bool);
 extern bool has_feature_p (const char *, bool);
 
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index 68e417b901f..53eda7fa707 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -51,7 +51,7 @@ static const c_feature_info c_feature_table[] =
   { "c_generic_selections", &flag_isoc11 },
   { "c_static_assert", &flag_isoc11 },
   { "c_thread_local", &flag_isoc11 },
-  { "cxx_binary_literals", &flag_isoc2x }
+  { "cxx_binary_literals", &flag_isoc23 }
 };
 
 /* Register features specific to the C language.  */
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 8caebf32f4d..70f9e4ab9e4 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "contracts.h"
 
-/* Class to determine whether a given C++ langauge feature is available.
+/* Class to determine whether a given C++ language feature is available.
    Used to implement __has_{feature,extension}.  */
 
 struct cp_feature_selector
@@ -100,7 +100,7 @@ struct cp_feature_info
 
 /* Table of features for __has_{feature,extension}.  */
 
-static const cp_feature_info cp_feature_table[] =
+static constexpr cp_feature_info cp_feature_table[] =
 {
   { "cxx_exceptions", &flag_exceptions },
   { "cxx_rtti", &flag_rtti },
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 518a77d61d8..7bfe72b3e4e 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -3593,7 +3593,7 @@ 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.
 
-If the @code{-pedantic-errors} flag is given, @code{__has_extension} is
+If the @option{-pedantic-errors} flag is given, @code{__has_extension} is
 equivalent to @code{__has_feature}.
 
 Note that @code{__has_feature} and @code{__has_extension} are not recommended
diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
index 4b322446eee..02ed6d92eb9 100644
--- a/gcc/objc/objc-act.cc
+++ b/gcc/objc/objc-act.cc
@@ -10365,7 +10365,7 @@ static bool objc_nonfragile_abi_p ()
   return flag_next_runtime && flag_objc_abi >= 2;
 }
 
-static const objc_feature_info objc_features[] =
+static constexpr objc_feature_info objc_features[] =
 {
   { "objc_default_synthesize_properties" },
   { "objc_instancetype" },
diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc
index b2c56e78e6e..107fb0e8138 100644
--- a/gcc/objc/objc-lang.cc
+++ b/gcc/objc/objc-lang.cc
@@ -59,7 +59,8 @@ objc_get_sarif_source_language (const char *)
 /* Implement c-family hook to add language-specific features
    for __has_{feature,extension}.  */
 
-void c_family_register_lang_features ()
+void
+c_family_register_lang_features ()
 {
   objc_common_register_features ();
   c_register_features ();
diff --git a/gcc/objcp/objcp-lang.cc b/gcc/objcp/objcp-lang.cc
index 1877e7fd84b..8a2c9f74250 100644
--- a/gcc/objcp/objcp-lang.cc
+++ b/gcc/objcp/objcp-lang.cc
@@ -88,7 +88,8 @@ objcp_tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 /* Implement c-family hook to add language-specific features
    for __has_{feature,extension}.  */
 
-void c_family_register_lang_features ()
+void
+c_family_register_lang_features ()
 {
   objc_common_register_features ();
   cp_register_features ();
diff --git a/gcc/testsuite/c-c++-common/has-feature-common.c b/gcc/testsuite/c-c++-common/has-feature-common.c
index 9a57b11e8e2..1604d7790fc 100644
--- a/gcc/testsuite/c-c++-common/has-feature-common.c
+++ b/gcc/testsuite/c-c++-common/has-feature-common.c
@@ -8,11 +8,11 @@
 #error unknown feature is known!
 #endif
 
-#if !__has_extension (gnu_asm_goto_with_outputs)
+#if !EXT (gnu_asm_goto_with_outputs)
 #error
 #endif
 
-#if !EXT (gnu_asm_goto_with_outputs)
+#if !EXT (__gnu_asm_goto_with_outputs__)
 #error
 #endif
 
@@ -20,19 +20,31 @@
 #error
 #endif
 
+#if !EXT (__gnu_asm_goto_with_outputs_full__)
+#error
+#endif
+
 #if !FEAT (enumerator_attributes)
 #error
 #endif
 
+#if !FEAT (__enumerator_attributes__)
+#error
+#endif
+
 #if !FEAT (attribute_deprecated_with_message)
 #error
 #endif
 
+#if !FEAT (__attribute_deprecated_with_message__)
+#error
+#endif
+
 #if !FEAT (attribute_unavailable_with_message)
 #error
 #endif
 
-#if !FEAT (enumerator_attributes)
+#if !FEAT (__attribute_unavailable_with_message__)
 #error
 #endif
 
@@ -40,6 +52,10 @@
 #error
 #endif
 
+#if !FEAT(__tls__)
+#error
+#endif
+
 #if defined (__SANITIZE_ADDRESS__) != __has_feature (address_sanitizer)
 #error
 #endif
diff --git a/gcc/testsuite/c-c++-common/has-feature-pedantic.c b/gcc/testsuite/c-c++-common/has-feature-pedantic.c
new file mode 100644
index 00000000000..4ac16a4ed8a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/has-feature-pedantic.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-pedantic-errors" } */
+
+/* When -pedantic-errors is passed, __has_extension should behave like
+   __has_feature.  */
+
+#if __has_feature (gnu_asm_goto_with_outputs)
+#error extension recognized as feature
+#endif
+
+#if __has_extension (gnu_asm_goto_with_outputs)
+#error pure extensions should not be recognized with -pedantic-errors
+#endif
+
+#if !__has_feature (tls) || !__has_extension (tls)
+#error features should still be recognized with -pedantic-errors
+#endif
+
+/* Make this TU non-empty to appease -pedantic-errors.  */
+int foo;
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index aae57260097..1210953d33a 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -311,6 +311,43 @@  const struct fname_var_t fname_vars[] =
   {NULL, 0, 0},
 };
 
+/* Flags to restrict availability of generic features that
+   are known to __has_{feature,extension}.  */
+
+enum
+{
+  HF_FLAG_EXT = 1,	/* Available only as an extension.  */
+  HF_FLAG_SANITIZE = 2, /* Availability depends on sanitizer flags.  */
+};
+
+/* Info for generic features which can be queried through
+   __has_{feature,extension}.  */
+
+struct hf_feature_info
+{
+  const char *ident;
+  unsigned flags;
+  unsigned mask;
+};
+
+/* Table of generic features which can be queried through
+   __has_{feature,extension}.  */
+
+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;
 
@@ -9808,4 +9845,63 @@  c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* Map from identifiers to booleans.  Value is true for features, and
+   false for extensions.  Used to implement __has_{feature,extension}.  */
+
+using feature_map_t = hash_map <tree, bool>;
+static feature_map_t *feature_map = nullptr;
+
+/* Register a feature for __has_{feature,extension}.  FEATURE_P is true
+   if the feature identified by NAME is a feature (as opposed to an
+   extension).  */
+
+void
+c_common_register_feature (const char *name, bool feature_p)
+{
+  bool dup = feature_map->put (get_identifier (name), feature_p);
+  gcc_checking_assert (!dup);
+}
+
+/* Lazily initialize hash table for __has_{feature,extension},
+   dispatching to the appropriate frontend to register language-specific
+   features.  */
+
+static void
+init_has_feature ()
+{
+  gcc_assert (!feature_map);
+  feature_map = new feature_map_t;
+  gcc_assert (feature_map);
+
+  for (unsigned i = 0; i < ARRAY_SIZE (has_feature_table); i++)
+    {
+      const hf_feature_info *info = has_feature_table + i;
+
+      if ((info->flags & HF_FLAG_SANITIZE) && !(flag_sanitize & info->mask))
+	continue;
+
+      const bool feature_p = !(info->flags & HF_FLAG_EXT);
+      c_common_register_feature (info->ident, feature_p);
+    }
+
+  /* Register language-specific features.  */
+  c_family_register_lang_features ();
+}
+
+/* If STRICT_P is true, evaluate __has_feature (IDENT).
+   Otherwise, evaluate __has_extension (IDENT).  */
+
+bool
+has_feature_p (const char *ident, bool strict_p)
+{
+  if (!feature_map)
+    init_has_feature ();
+
+  bool *feat_p = feature_map->get (get_identifier (ident));
+  if (!feat_p)
+    return false;
+
+  return !strict_p || *feat_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 1fdba7ef3ea..28ea8d63f5a 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1126,6 +1126,20 @@  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);
+
+/* Implemented by each frontend in *-lang.cc.  */
+extern void c_family_register_lang_features ();
+
+/* Implemented in c-common.cc.  */
+extern void c_common_register_feature (const char *, bool);
+extern bool has_feature_p (const char *, bool);
+
+/* Implemented in c/c-objc-common.cc.  */
+extern void c_register_features ();
+
+/* Implemented in cp/cp-objcp-common.cc.  */
+extern void cp_register_features ();
 
 extern bool parse_optimize_options (tree, bool);
 
diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
index 5e457b78cdd..6a605f91858 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;
@@ -453,16 +454,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;
     }
 
@@ -482,7 +483,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;
     }
@@ -501,9 +502,38 @@  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;
+
+  /* If -pedantic-errors is given, __has_extension is equivalent to
+     __has_feature.  */
+  strict_p |= flag_pedantic_errors;
+  return has_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-lang.cc b/gcc/c/c-lang.cc
index b4e0c8cfb8a..11e7aaac2e3 100644
--- a/gcc/c/c-lang.cc
+++ b/gcc/c/c-lang.cc
@@ -61,6 +61,15 @@  c_get_sarif_source_language (const char *)
   return "c";
 }
 
+/* Implement c-family hook to register language-specific features for
+   __has_{feature,extension}.  */
+
+void
+c_family_register_lang_features ()
+{
+  c_register_features ();
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index e4aed61ed00..fdff0cb999e 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -34,6 +34,39 @@  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 **);
 
+/* Info for C language features which can be queried through
+   __has_{feature,extension}.  */
+
+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 },
+  { "cxx_binary_literals", &flag_isoc2x }
+};
+
+/* Register features specific to the C language.  */
+
+void
+c_register_features ()
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (c_feature_table); i++)
+    {
+      const c_feature_info *info = c_feature_table + i;
+      const bool feat_p = !info->enable_flag || *info->enable_flag;
+      c_common_register_feature (info->ident, feat_p);
+    }
+}
+
 bool
 c_missing_noreturn_ok_p (tree decl)
 {
diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
index 2f541460c4a..84200a9a04a 100644
--- a/gcc/cp/cp-lang.cc
+++ b/gcc/cp/cp-lang.cc
@@ -121,6 +121,15 @@  objcp_tsubst_copy_and_build (tree /*t*/,
   return NULL_TREE;
 }
 
+/* Implement c-family hook to add language-specific features
+   for __has_{feature,extension}.  */
+
+void
+c_family_register_lang_features ()
+{
+  cp_register_features ();
+}
+
 static const char *
 cxx_dwarf_name (tree t, int verbosity)
 {
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 93b027b80ce..ad8af5b401e 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -23,10 +23,154 @@  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"
 
+/* Class to determine whether a given C++ langauge feature is available.
+   Used to implement __has_{feature,extension}.  */
+
+struct cp_feature_selector
+{
+  enum
+  {
+    DIALECT,
+    FLAG
+  } kind;
+
+  enum class result
+  {
+    NONE,
+    EXT,
+    FEAT
+  };
+
+  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) {}
+
+  inline result has_feature () const;
+};
+
+/* Check whether this language feature is available as a feature,
+   extension, or not at all.  */
+
+cp_feature_selector::result
+cp_feature_selector::has_feature () const
+{
+  switch (kind)
+    {
+    case DIALECT:
+      if (cxx_dialect >= dialect.feat)
+	return result::FEAT;
+      else if (cxx_dialect >= dialect.ext)
+	return result::EXT;
+      else
+	return result::NONE;
+    case FLAG:
+      return *enable_flag ? result::FEAT : result::NONE;
+    }
+
+  gcc_unreachable ();
+}
+
+/* Information about a C++ language feature which can be queried
+   through __has_{feature,extension}.  IDENT is the name of the feature,
+   and SELECTOR encodes how to compute whether the feature is available.  */
+
+struct cp_feature_info
+{
+  const char *ident;
+  cp_feature_selector selector;
+};
+
+/* Table of features for __has_{feature,extension}.  */
+
+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 },
+  { "cxx_delegating_constructors", cxx11 },
+  { "cxx_deleted_functions", cxx11 },
+  { "cxx_explicit_conversions", cxx11 },
+  { "cxx_generalized_initializers", cxx11 },
+  { "cxx_implicit_moves", cxx11 },
+  { "cxx_inheriting_constructors", cxx11 },
+  { "cxx_inline_namespaces", { cxx11, cxx98 } },
+  { "cxx_lambdas", cxx11 },
+  { "cxx_local_type_template_args", cxx11 },
+  { "cxx_noexcept", cxx11 },
+  { "cxx_nonstatic_member_init", cxx11 },
+  { "cxx_nullptr", cxx11 },
+  { "cxx_override_control", cxx11 },
+  { "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 },
+  { "cxx_generic_lambdas", cxx14 },
+  { "cxx_relaxed_constexpr", cxx14 },
+  { "cxx_return_type_deduction", cxx14 },
+  { "cxx_variable_templates", cxx14 },
+  { "modules", &flag_modules },
+};
+
+/* Register C++ language features for __has_{feature,extension}.  */
+
+void
+cp_register_features ()
+{
+  using result = cp_feature_selector::result;
+
+  for (unsigned i = 0; i < ARRAY_SIZE (cp_feature_table); i++)
+    {
+      const cp_feature_info *info = cp_feature_table + i;
+      const auto res = info->selector.has_feature ();
+      if (res == result::NONE)
+	continue;
+
+      c_common_register_feature (info->ident, res == result::FEAT);
+    }
+}
+
 /* 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 3f492b33470..9ab22a46899 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -3199,6 +3199,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
 
@@ -3561,6 +3563,45 @@  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.
+
+Note that @code{__has_feature} and @code{__has_extension} are not recommended
+for use in new code, and are only provided for compatibility with Clang.  For
+details of which identifiers are accepted by these function-like macros, see
+@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
+the Clang documentation}}.
+
+@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.
+
+If the @code{-pedantic-errors} flag is given, @code{__has_extension} is
+equivalent to @code{__has_feature}.
+
+Note that @code{__has_feature} and @code{__has_extension} are not recommended
+for use in new code, and are only provided for compatibility with Clang.  For
+details of which identifiers are accepted by these function-like macros, see
+@w{@uref{https://clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension,
+the Clang documentation}}.
+
 @node @code{__has_include}
 @subsection @code{__has_include}
 @cindex @code{__has_include}
diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
index 186a29cd270..27fe317b587 100644
--- a/gcc/objc/objc-act.cc
+++ b/gcc/objc/objc-act.cc
@@ -10359,5 +10359,51 @@  objc_common_tree_size (enum tree_code code)
     }
 }
 
+/* Information for Objective-C-specific features known to __has_feature.  */
+
+struct objc_feature_info
+{
+  typedef bool (*predicate_t) ();
+
+  const char *ident;
+  predicate_t predicate;
+
+  constexpr objc_feature_info (const char *name)
+    : ident (name), predicate (nullptr) {}
+  constexpr objc_feature_info (const char *name, predicate_t p)
+    : ident (name), predicate (p) {}
+
+  bool has_feature () const
+    {
+      return predicate ? predicate () : true;
+    }
+};
+
+static bool objc_nonfragile_abi_p ()
+{
+  return flag_next_runtime && flag_objc_abi >= 2;
+}
+
+static const objc_feature_info objc_features[] =
+{
+  { "objc_default_synthesize_properties" },
+  { "objc_instancetype" },
+  { "objc_nonfragile_abi", objc_nonfragile_abi_p }
+};
+
+/* Register Objective-C-specific features for __has_feature.  */
+
+void
+objc_common_register_features ()
+{
+  for (unsigned i = 0; i < ARRAY_SIZE (objc_features); i++)
+    {
+      const objc_feature_info *info = objc_features + i;
+      if (!info->has_feature ())
+	continue;
+
+      c_common_register_feature (info->ident, true);
+    }
+}
 
 #include "gt-objc-objc-act.h"
diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h
index e21ab52d8ca..bcf0249515a 100644
--- a/gcc/objc/objc-act.h
+++ b/gcc/objc/objc-act.h
@@ -29,6 +29,9 @@  int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
 void objc_common_init_ts (void);
 const char *objc_get_sarif_source_language (const char *);
 
+/* Register features common to Objective-C and Objective-C++.  */
+void objc_common_register_features ();
+
 /* NB: The remaining public functions are prototyped in c-common.h, for the
    benefit of stub-objc.cc and objc-act.cc.  */
 
diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc
index 89b3be48b9e..451cbe09413 100644
--- a/gcc/objc/objc-lang.cc
+++ b/gcc/objc/objc-lang.cc
@@ -58,6 +58,15 @@  objc_get_sarif_source_language (const char *)
   return "objectivec";
 }
 
+/* Implement c-family hook to add language-specific features
+   for __has_{feature,extension}.  */
+
+void c_family_register_lang_features ()
+{
+  objc_common_register_features ();
+  c_register_features ();
+}
+
 /* Lang hook routines common to C and ObjC appear in c-objc-common.cc;
    there should be very few (if any) routines below.  */
 
diff --git a/gcc/objcp/objcp-lang.cc b/gcc/objcp/objcp-lang.cc
index 9887209b9c8..1c22456115f 100644
--- a/gcc/objcp/objcp-lang.cc
+++ b/gcc/objcp/objcp-lang.cc
@@ -80,6 +80,15 @@  objcp_tsubst_copy_and_build (tree t, tree args, tsubst_flags_t complain,
 #undef RECURSE
 }
 
+/* Implement c-family hook to add language-specific features
+   for __has_{feature,extension}.  */
+
+void c_family_register_lang_features ()
+{
+  objc_common_register_features ();
+  cp_register_features ();
+}
+
 static void
 objcxx_init_ts (void)
 {
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..52191b78fd6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/has-feature.C
@@ -0,0 +1,206 @@ 
+// { dg-do compile }
+// { dg-options "" }
+
+#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_feature (cxx_delegating_constructors) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_deleted_functions) != CXX11
+#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_feature (cxx_nonstatic_member_init) != CXX11
+#error
+#endif
+
+#if FEAT (cxx_nullptr) != CXX11
+#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_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..2fd0b4c7f1d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/has-feature.c
@@ -0,0 +1,62 @@ 
+/* { dg-do compile } */
+/* { dg-options "" } */
+/* 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
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/gcc/testsuite/obj-c++.dg/has-feature.mm b/gcc/testsuite/obj-c++.dg/has-feature.mm
new file mode 100644
index 00000000000..77c76173bfb
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/has-feature.mm
@@ -0,0 +1,21 @@ 
+// { dg-do compile }
+
+#define CXX11 (__cplusplus >= 201103L)
+
+#if !__has_feature (objc_instancetype)
+#error
+#endif
+
+#if !__has_feature (objc_default_synthesize_properties)
+#error
+#endif
+
+// C features should not be available.
+#if __has_extension (c_alignas) || __has_feature (c_alignof)
+#error
+#endif
+
+// C++ features should be available (given the right standard).
+#if __has_feature (cxx_constexpr) != CXX11
+#error
+#endif
diff --git a/gcc/testsuite/objc.dg/has-feature.m b/gcc/testsuite/objc.dg/has-feature.m
new file mode 100644
index 00000000000..168b0ce16e7
--- /dev/null
+++ b/gcc/testsuite/objc.dg/has-feature.m
@@ -0,0 +1,26 @@ 
+/* { dg-do compile } */
+
+#define HAVE_C11 (__STDC_VERSION__ >= 201112L)
+
+#if !__has_feature (objc_instancetype)
+#error
+#endif
+
+#if !__has_feature (objc_default_synthesize_properties)
+#error
+#endif
+
+/* C features should be available as extensions.  */
+#if !__has_extension (c_alignas)
+#error
+#endif
+
+/* And as features given the appropriate C standard.  */
+#if __has_feature (c_alignas) != HAVE_C11
+#error
+#endif
+
+/* Shouldn't have C++ features even as extensions.  */
+#if __has_feature (cxx_constexpr) || __has_extension (cxx_constexpr)
+#error
+#endif
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index c0af82c0b1c..32282a37bee 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -761,6 +761,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);
 
@@ -965,7 +968,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 9a20f8d8176..c1424d35949 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -435,6 +435,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 dada8fea835..f8b86d0fbfe 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)